From dddb11bf404ab95ba9b81eb3fc79a1feda967b55 Mon Sep 17 00:00:00 2001 From: Marc Date: Thu, 8 May 2014 16:59:35 +0000 Subject: [PATCH] Initial commit --- .gitignore | 6 + INSTALL.md | 83 + LICENSE | 14 + MANIFEST.in | 8 + README.md | 94 + ROADMAP.md | 43 + TODO.md | 37 + build/pip-delete-this-directory.txt | 5 + docs/API.rst | 222 + docs/Makefile | 153 + docs/conf.py | 242 + docs/index.rst | 21 + docs/make.bat | 190 + orchestra/__init__.py | 23 + orchestra/admin/__init__.py | 2 + orchestra/admin/dashboard.py | 20 + orchestra/admin/decorators.py | 55 + orchestra/admin/html.py | 10 + orchestra/admin/menu.py | 96 + orchestra/admin/options.py | 76 + orchestra/admin/utils.py | 133 + orchestra/api/__init__.py | 2 + orchestra/api/actions.py | 18 + orchestra/api/fields.py | 51 + orchestra/api/helpers.py | 52 + orchestra/api/options.py | 139 + orchestra/api/serializers.py | 33 + orchestra/apps/__init__.py | 0 orchestra/apps/accounts/__init__.py | 0 orchestra/apps/accounts/admin.py | 212 + orchestra/apps/accounts/api.py | 25 + orchestra/apps/accounts/filters.py | 20 + orchestra/apps/accounts/forms.py | 55 + .../apps/accounts/management/__init__.py | 0 .../accounts/management/commands/__init__.py | 0 .../commands/createinitialaccount.py | 36 + .../management/commands/createsuperuser.py | 14 + orchestra/apps/accounts/models.py | 25 + orchestra/apps/accounts/serializers.py | 17 + orchestra/apps/accounts/settings.py | 20 + .../admin/accounts/account/change_form.html | 20 + orchestra/apps/contacts/__init__.py | 0 orchestra/apps/contacts/admin.py | 67 + orchestra/apps/contacts/api.py | 21 + orchestra/apps/contacts/filters.py | 20 + orchestra/apps/contacts/models.py | 41 + orchestra/apps/contacts/serializers.py | 23 + orchestra/apps/contacts/settings.py | 24 + orchestra/apps/databases/__init__.py | 0 orchestra/apps/databases/admin.py | 135 + orchestra/apps/databases/api.py | 26 + orchestra/apps/databases/backends.py | 60 + orchestra/apps/databases/forms.py | 135 + orchestra/apps/databases/models.py | 91 + orchestra/apps/databases/serializers.py | 40 + orchestra/apps/databases/settings.py | 14 + orchestra/apps/domains/__init__.py | 0 orchestra/apps/domains/admin.py | 125 + orchestra/apps/domains/api.py | 35 + orchestra/apps/domains/backends.py | 104 + orchestra/apps/domains/filters.py | 35 + orchestra/apps/domains/forms.py | 56 + orchestra/apps/domains/helpers.py | 24 + orchestra/apps/domains/models.py | 174 + orchestra/apps/domains/serializers.py | 40 + orchestra/apps/domains/settings.py | 51 + .../admin/domains/domain/change_form.html | 15 + .../admin/domains/domain/view_zone.html | 22 + orchestra/apps/domains/tests/__init__.py | 0 .../tests/functional_tests/__init__.py | 0 .../domains/tests/functional_tests/tests.py | 299 + orchestra/apps/domains/tests/test_domains.py | 18 + orchestra/apps/domains/utils.py | 26 + orchestra/apps/domains/validators.py | 108 + orchestra/apps/issues/__init__.py | 1 + orchestra/apps/issues/actions.py | 109 + orchestra/apps/issues/admin.py | 337 + orchestra/apps/issues/filters.py | 43 + orchestra/apps/issues/forms.py | 106 + orchestra/apps/issues/helpers.py | 36 + orchestra/apps/issues/models.py | 185 + orchestra/apps/issues/settings.py | 7 + .../issues/static/issues/css/ticket-admin.css | 67 + .../issues/static/issues/images/btn_edit.gif | Bin 0 -> 204 bytes .../static/issues/images/unread_ticket.gif | Bin 0 -> 260 bytes .../issues/static/issues/js/admin-ticket.js | 16 + .../issues/static/issues/js/ticket-admin.js | 30 + .../issues/static/issues/markdown_syntax.html | 55 + .../templates/issues/ticket_notification.mail | 36 + .../issues/ticket_notification_html.mail | 60 + orchestra/apps/issues/tests.py | 16 + orchestra/apps/lists/__init__.py | 0 orchestra/apps/lists/admin.py | 60 + orchestra/apps/lists/api.py | 16 + orchestra/apps/lists/backends.py | 11 + orchestra/apps/lists/forms.py | 51 + orchestra/apps/lists/models.py | 35 + orchestra/apps/lists/serializers.py | 11 + orchestra/apps/lists/settings.py | 8 + orchestra/apps/orchestration/README.md | 88 + orchestra/apps/orchestration/__init__.py | 1 + orchestra/apps/orchestration/admin.py | 125 + orchestra/apps/orchestration/backends.py | 104 + orchestra/apps/orchestration/helpers.py | 46 + orchestra/apps/orchestration/manager.py | 73 + orchestra/apps/orchestration/methods.py | 102 + orchestra/apps/orchestration/middlewares.py | 96 + orchestra/apps/orchestration/models.py | 179 + orchestra/apps/orchestration/settings.py | 21 + .../apps/orchestration/tests/__init__.py | 0 .../apps/orchestration/tests/test_route.py | 44 + orchestra/apps/orders/README.md | 8 + orchestra/apps/orders/__init__.py | 0 orchestra/apps/orders/collector.py | 29 + orchestra/apps/orders/models.py | 36 + orchestra/apps/orders/settings.py | 7 + orchestra/apps/orders/tests/__init__.py | 0 orchestra/apps/orders/tests/models.py | 21 + orchestra/apps/orders/tests/test_collector.py | 28 + orchestra/apps/users/__init__.py | 0 orchestra/apps/users/admin.py | 110 + orchestra/apps/users/api.py | 23 + orchestra/apps/users/backends.py | 41 + orchestra/apps/users/forms.py | 50 + orchestra/apps/users/models.py | 88 + orchestra/apps/users/roles/__init__.py | 0 orchestra/apps/users/roles/admin.py | 147 + orchestra/apps/users/roles/filters.py | 23 + orchestra/apps/users/roles/forms.py | 17 + orchestra/apps/users/roles/jabber/__init__.py | 0 orchestra/apps/users/roles/jabber/admin.py | 17 + orchestra/apps/users/roles/jabber/models.py | 10 + orchestra/apps/users/roles/mail/__init__.py | 0 orchestra/apps/users/roles/mail/admin.py | 129 + orchestra/apps/users/roles/mail/api.py | 27 + orchestra/apps/users/roles/mail/backends.py | 143 + orchestra/apps/users/roles/mail/forms.py | 53 + orchestra/apps/users/roles/mail/models.py | 110 + .../apps/users/roles/mail/serializers.py | 43 + orchestra/apps/users/roles/mail/settings.py | 29 + orchestra/apps/users/roles/mail/validators.py | 63 + .../apps/users/roles/owncloud/__init__.py | 0 orchestra/apps/users/roles/posix/__init__.py | 0 orchestra/apps/users/roles/posix/admin.py | 17 + orchestra/apps/users/roles/posix/models.py | 16 + .../apps/users/roles/posix/serializers.py | 14 + orchestra/apps/users/roles/posix/settings.py | 11 + orchestra/apps/users/serializers.py | 35 + orchestra/apps/users/settings.py | 4 + .../admin/users/user/change_form.html | 15 + .../admin/users/user/delete_role.html | 15 + .../templates/admin/users/user/role.html | 70 + orchestra/apps/vps/__init__.py | 0 orchestra/apps/vps/admin.py | 50 + orchestra/apps/vps/forms.py | 39 + orchestra/apps/vps/models.py | 37 + orchestra/apps/vps/settings.py | 17 + orchestra/apps/webapps/__init__.py | 0 orchestra/apps/webapps/admin.py | 48 + orchestra/apps/webapps/api.py | 28 + orchestra/apps/webapps/backends/__init__.py | 26 + orchestra/apps/webapps/backends/awstats.py | 12 + orchestra/apps/webapps/backends/dokuwikimu.py | 28 + orchestra/apps/webapps/backends/drupalmu.py | 35 + orchestra/apps/webapps/backends/phpfcgid.py | 48 + orchestra/apps/webapps/backends/phpfpm.py | 58 + orchestra/apps/webapps/backends/static.py | 17 + .../apps/webapps/backends/wordpressmu.py | 101 + orchestra/apps/webapps/models.py | 98 + orchestra/apps/webapps/serializers.py | 14 + orchestra/apps/webapps/settings.py | 200 + orchestra/apps/websites/__init__.py | 0 orchestra/apps/websites/admin.py | 93 + orchestra/apps/websites/api.py | 25 + orchestra/apps/websites/backends/__init__.py | 5 + orchestra/apps/websites/backends/apache.py | 175 + orchestra/apps/websites/backends/webalizer.py | 84 + orchestra/apps/websites/models.py | 92 + orchestra/apps/websites/serializers.py | 22 + orchestra/apps/websites/settings.py | 50 + orchestra/bin/celerybeat | 212 + orchestra/bin/celeryd | 234 + orchestra/bin/celeryevcam | 226 + orchestra/bin/django_bash_completion.sh | 72 + orchestra/bin/orchestra-admin | 422 ++ orchestra/bin/sieve-test | Bin 0 -> 1295912 bytes orchestra/conf/__init__.py | 0 orchestra/conf/base_settings.py | 222 + orchestra/conf/devel_settings.py | 20 + orchestra/conf/production_settings.py | 2 + orchestra/conf/project_template/manage.py | 11 + .../project_template/project_name/__init__.py | 0 .../project_template/project_name/settings.py | 63 + .../project_template/project_name/urls.py | 6 + .../project_template/project_name/wsgi.py | 14 + orchestra/core/__init__.py | 19 + orchestra/core/context_processors.py | 10 + orchestra/core/validators.py | 69 + orchestra/forms/__init__.py | 0 orchestra/forms/fields.py | 15 + orchestra/forms/widgets.py | 19 + orchestra/management/__init__.py | 0 orchestra/management/commands/__init__.py | 0 .../management/commands/orchestraversion.py | 10 + .../commands/postupgradeorchestra.py | 92 + .../management/commands/restartservices.py | 11 + orchestra/management/commands/setupcelery.py | 103 + orchestra/management/commands/setupnginx.py | 129 + orchestra/management/commands/setuppostfix.py | 321 + .../management/commands/setuppostgres.py | 69 + .../management/commands/startservices.py | 59 + orchestra/management/commands/stopservices.py | 11 + .../management/commands/upgradeorchestra.py | 98 + orchestra/models/__init__.py | 0 orchestra/models/fields.py | 57 + orchestra/models/utils.py | 49 + orchestra/permissions/__init__.py | 1 + orchestra/permissions/api.py | 28 + orchestra/permissions/auth.py | 48 + orchestra/permissions/options.py | 107 + orchestra/settings.py | 28 + orchestra/static/admin/css/login.css | 99 + orchestra/static/admin_tools/css/theming.css | 21 + .../orchestra/css/adminextraprettystyle.css | 95 + .../static/orchestra/css/hide-inline-id.css | 7 + .../orchestra/icons/Applications-internet.png | Bin 0 -> 4646 bytes .../orchestra/icons/Applications-internet.svg | 520 ++ .../orchestra/icons/Applications-other.png | Bin 0 -> 2863 bytes .../orchestra/icons/Applications-other.svg | 380 ++ .../orchestra/icons/Emblem-important.png | Bin 0 -> 906 bytes .../orchestra/icons/Emblem-important.svg | 108 + .../static/orchestra/icons/Face-monkey.png | Bin 0 -> 3908 bytes .../static/orchestra/icons/Face-monkey.svg | 414 ++ orchestra/static/orchestra/icons/Koala.png | Bin 0 -> 4728 bytes orchestra/static/orchestra/icons/Koala.svg | 5595 +++++++++++++++++ .../static/orchestra/icons/Mr-potato.png | Bin 0 -> 4811 bytes .../static/orchestra/icons/Mr-potato.svg | 4273 +++++++++++++ .../static/orchestra/icons/Text-x-boo.svg | 997 +++ .../static/orchestra/icons/Text-x-script.svg | 419 ++ .../static/orchestra/icons/Ticket_star.png | Bin 0 -> 4230 bytes orchestra/static/orchestra/icons/Tux.png | Bin 0 -> 3957 bytes orchestra/static/orchestra/icons/Tux.svg | 648 ++ orchestra/static/orchestra/icons/TuxBox.png | Bin 0 -> 3490 bytes orchestra/static/orchestra/icons/TuxBox.svg | 4661 ++++++++++++++ .../orchestra/icons/X-office-address-book.png | Bin 0 -> 3193 bytes .../orchestra/icons/X-office-address-book.svg | 301 + orchestra/static/orchestra/icons/apps.png | Bin 0 -> 2122 bytes orchestra/static/orchestra/icons/apps.svg | 518 ++ orchestra/static/orchestra/icons/auth.svg | 2449 ++++++++ orchestra/static/orchestra/icons/bill.svg | 1348 ++++ orchestra/static/orchestra/icons/contact.png | Bin 0 -> 2729 bytes orchestra/static/orchestra/icons/contact.svg | 2810 +++++++++ orchestra/static/orchestra/icons/daemon.png | Bin 0 -> 3252 bytes orchestra/static/orchestra/icons/daemon.svg | 562 ++ orchestra/static/orchestra/icons/database.png | Bin 0 -> 3039 bytes orchestra/static/orchestra/icons/database.svg | 401 ++ orchestra/static/orchestra/icons/dns.png | Bin 0 -> 4206 bytes orchestra/static/orchestra/icons/dns.svg | 3045 +++++++++ orchestra/static/orchestra/icons/domain.png | Bin 0 -> 4559 bytes orchestra/static/orchestra/icons/domain.svg | 3045 +++++++++ .../static/orchestra/icons/email-alter.png | Bin 0 -> 2892 bytes .../static/orchestra/icons/email-alter.svg | 3072 +++++++++ orchestra/static/orchestra/icons/email.png | Bin 0 -> 2646 bytes orchestra/static/orchestra/icons/email.svg | 1643 +++++ .../static/orchestra/icons/extrafield.png | Bin 0 -> 2512 bytes .../static/orchestra/icons/extrafield.svg | 1170 ++++ .../static/orchestra/icons/gnome-terminal.png | Bin 0 -> 1690 bytes .../static/orchestra/icons/gnome-terminal.svg | 354 ++ orchestra/static/orchestra/icons/hal.png | Bin 0 -> 4052 bytes orchestra/static/orchestra/icons/hal.svg | 1002 +++ orchestra/static/orchestra/icons/job.svg | 3386 ++++++++++ orchestra/static/orchestra/icons/monitor.png | Bin 0 -> 2591 bytes orchestra/static/orchestra/icons/monitor.svg | 3134 +++++++++ orchestra/static/orchestra/icons/mysql.png | Bin 0 -> 3221 bytes orchestra/static/orchestra/icons/mysql.svg | 1686 +++++ orchestra/static/orchestra/icons/order.png | Bin 0 -> 3506 bytes orchestra/static/orchestra/icons/order.svg | 1790 ++++++ .../static/orchestra/icons/periodictask.svg | 3801 +++++++++++ .../static/orchestra/icons/postgresql.png | Bin 0 -> 3868 bytes .../static/orchestra/icons/postgresql.svg | 894 +++ .../static/orchestra/icons/scriptlog.png | Bin 0 -> 2709 bytes .../static/orchestra/icons/scriptlog.svg | 756 +++ orchestra/static/orchestra/icons/ssh.svg | 890 +++ .../static/orchestra/icons/taskstate.png | Bin 0 -> 3868 bytes .../static/orchestra/icons/transaction.png | Bin 0 -> 4100 bytes .../static/orchestra/icons/transaction.svg | 1418 +++++ orchestra/static/orchestra/icons/users.png | Bin 0 -> 3081 bytes orchestra/static/orchestra/icons/users.svg | 3802 +++++++++++ orchestra/static/orchestra/icons/vps.png | Bin 0 -> 2710 bytes orchestra/static/orchestra/icons/vps.svg | 3625 +++++++++++ orchestra/static/orchestra/icons/web.png | Bin 0 -> 3984 bytes orchestra/static/orchestra/icons/web.svg | 295 + orchestra/static/orchestra/icons/zone.png | Bin 0 -> 3119 bytes orchestra/static/orchestra/icons/zone.svg | 2875 +++++++++ orchestra/static/orchestra/images/favicon.png | Bin 0 -> 1150 bytes .../orchestra/images/orchestra-logo.png | Bin 0 -> 1964 bytes .../orchestra/images/orchestra-logo.svg | 206 + .../static/orchestra/images/page-gradient.png | Bin 0 -> 280 bytes orchestra/templates/admin/base.html | 104 + orchestra/templates/admin/base_site.html | 26 + orchestra/templates/admin/index.html | 18 + orchestra/templates/admin/login.html | 62 + orchestra/templates/rest_framework/api.html | 27 + orchestra/templatetags/__init__.py | 0 orchestra/templatetags/markdown.py | 13 + orchestra/templatetags/utils.py | 47 + orchestra/urls.py | 31 + orchestra/utils/__init__.py | 1 + orchestra/utils/apps.py | 42 + orchestra/utils/functional.py | 9 + orchestra/utils/options.py | 39 + orchestra/utils/paths.py | 24 + orchestra/utils/plugins.py | 13 + orchestra/utils/python.py | 60 + orchestra/utils/system.py | 116 + orchestra/utils/tests.py | 100 + orchestra/utils/time.py | 34 + scripts/container/create.sh | 45 + scripts/container/deploy.sh | 118 + scripts/migration/README.md | 6 + scripts/migration/accounts.sh | 17 + scripts/migration/apache2.py | 62 + scripts/migration/domains.sh | 50 + scripts/migration/mailbox.sh | 36 + scripts/migration/mysql.sh | 11 + scripts/migration/virtusertable.sh | 34 + scripts/services/README.md | 6 + scripts/services/apache_full_stack.md | 96 + scripts/services/bind9.sh | 9 + scripts/services/mailman.sh | 3 + scripts/services/mysql.sh | 1 + scripts/services/php4_on_debian.md | 94 + scripts/services/postfix.md | 12 + scripts/services/vsftpd.md | 25 + scripts/services/webalizer.md | 21 + scripts/tests/setup.sh | 9 + setup.py | 49 + 337 files changed, 81969 insertions(+) create mode 100644 .gitignore create mode 100644 INSTALL.md create mode 100644 LICENSE create mode 100644 MANIFEST.in create mode 100644 README.md create mode 100644 ROADMAP.md create mode 100644 TODO.md create mode 100644 build/pip-delete-this-directory.txt create mode 100644 docs/API.rst create mode 100644 docs/Makefile create mode 100644 docs/conf.py create mode 100644 docs/index.rst create mode 100644 docs/make.bat create mode 100644 orchestra/__init__.py create mode 100644 orchestra/admin/__init__.py create mode 100644 orchestra/admin/dashboard.py create mode 100644 orchestra/admin/decorators.py create mode 100644 orchestra/admin/html.py create mode 100644 orchestra/admin/menu.py create mode 100644 orchestra/admin/options.py create mode 100644 orchestra/admin/utils.py create mode 100644 orchestra/api/__init__.py create mode 100644 orchestra/api/actions.py create mode 100644 orchestra/api/fields.py create mode 100644 orchestra/api/helpers.py create mode 100644 orchestra/api/options.py create mode 100644 orchestra/api/serializers.py create mode 100644 orchestra/apps/__init__.py create mode 100644 orchestra/apps/accounts/__init__.py create mode 100644 orchestra/apps/accounts/admin.py create mode 100644 orchestra/apps/accounts/api.py create mode 100644 orchestra/apps/accounts/filters.py create mode 100644 orchestra/apps/accounts/forms.py create mode 100644 orchestra/apps/accounts/management/__init__.py create mode 100644 orchestra/apps/accounts/management/commands/__init__.py create mode 100644 orchestra/apps/accounts/management/commands/createinitialaccount.py create mode 100644 orchestra/apps/accounts/management/commands/createsuperuser.py create mode 100644 orchestra/apps/accounts/models.py create mode 100644 orchestra/apps/accounts/serializers.py create mode 100644 orchestra/apps/accounts/settings.py create mode 100644 orchestra/apps/accounts/templates/admin/accounts/account/change_form.html create mode 100644 orchestra/apps/contacts/__init__.py create mode 100644 orchestra/apps/contacts/admin.py create mode 100644 orchestra/apps/contacts/api.py create mode 100644 orchestra/apps/contacts/filters.py create mode 100644 orchestra/apps/contacts/models.py create mode 100644 orchestra/apps/contacts/serializers.py create mode 100644 orchestra/apps/contacts/settings.py create mode 100644 orchestra/apps/databases/__init__.py create mode 100644 orchestra/apps/databases/admin.py create mode 100644 orchestra/apps/databases/api.py create mode 100644 orchestra/apps/databases/backends.py create mode 100644 orchestra/apps/databases/forms.py create mode 100644 orchestra/apps/databases/models.py create mode 100644 orchestra/apps/databases/serializers.py create mode 100644 orchestra/apps/databases/settings.py create mode 100644 orchestra/apps/domains/__init__.py create mode 100644 orchestra/apps/domains/admin.py create mode 100644 orchestra/apps/domains/api.py create mode 100644 orchestra/apps/domains/backends.py create mode 100644 orchestra/apps/domains/filters.py create mode 100644 orchestra/apps/domains/forms.py create mode 100644 orchestra/apps/domains/helpers.py create mode 100644 orchestra/apps/domains/models.py create mode 100644 orchestra/apps/domains/serializers.py create mode 100644 orchestra/apps/domains/settings.py create mode 100644 orchestra/apps/domains/templates/admin/domains/domain/change_form.html create mode 100644 orchestra/apps/domains/templates/admin/domains/domain/view_zone.html create mode 100644 orchestra/apps/domains/tests/__init__.py create mode 100644 orchestra/apps/domains/tests/functional_tests/__init__.py create mode 100644 orchestra/apps/domains/tests/functional_tests/tests.py create mode 100644 orchestra/apps/domains/tests/test_domains.py create mode 100644 orchestra/apps/domains/utils.py create mode 100644 orchestra/apps/domains/validators.py create mode 100644 orchestra/apps/issues/__init__.py create mode 100644 orchestra/apps/issues/actions.py create mode 100644 orchestra/apps/issues/admin.py create mode 100644 orchestra/apps/issues/filters.py create mode 100644 orchestra/apps/issues/forms.py create mode 100644 orchestra/apps/issues/helpers.py create mode 100644 orchestra/apps/issues/models.py create mode 100644 orchestra/apps/issues/settings.py create mode 100644 orchestra/apps/issues/static/issues/css/ticket-admin.css create mode 100644 orchestra/apps/issues/static/issues/images/btn_edit.gif create mode 100644 orchestra/apps/issues/static/issues/images/unread_ticket.gif create mode 100644 orchestra/apps/issues/static/issues/js/admin-ticket.js create mode 100644 orchestra/apps/issues/static/issues/js/ticket-admin.js create mode 100644 orchestra/apps/issues/static/issues/markdown_syntax.html create mode 100644 orchestra/apps/issues/templates/issues/ticket_notification.mail create mode 100644 orchestra/apps/issues/templates/issues/ticket_notification_html.mail create mode 100644 orchestra/apps/issues/tests.py create mode 100644 orchestra/apps/lists/__init__.py create mode 100644 orchestra/apps/lists/admin.py create mode 100644 orchestra/apps/lists/api.py create mode 100644 orchestra/apps/lists/backends.py create mode 100644 orchestra/apps/lists/forms.py create mode 100644 orchestra/apps/lists/models.py create mode 100644 orchestra/apps/lists/serializers.py create mode 100644 orchestra/apps/lists/settings.py create mode 100644 orchestra/apps/orchestration/README.md create mode 100644 orchestra/apps/orchestration/__init__.py create mode 100644 orchestra/apps/orchestration/admin.py create mode 100644 orchestra/apps/orchestration/backends.py create mode 100644 orchestra/apps/orchestration/helpers.py create mode 100644 orchestra/apps/orchestration/manager.py create mode 100644 orchestra/apps/orchestration/methods.py create mode 100644 orchestra/apps/orchestration/middlewares.py create mode 100644 orchestra/apps/orchestration/models.py create mode 100644 orchestra/apps/orchestration/settings.py create mode 100644 orchestra/apps/orchestration/tests/__init__.py create mode 100644 orchestra/apps/orchestration/tests/test_route.py create mode 100644 orchestra/apps/orders/README.md create mode 100644 orchestra/apps/orders/__init__.py create mode 100644 orchestra/apps/orders/collector.py create mode 100644 orchestra/apps/orders/models.py create mode 100644 orchestra/apps/orders/settings.py create mode 100644 orchestra/apps/orders/tests/__init__.py create mode 100644 orchestra/apps/orders/tests/models.py create mode 100644 orchestra/apps/orders/tests/test_collector.py create mode 100644 orchestra/apps/users/__init__.py create mode 100644 orchestra/apps/users/admin.py create mode 100644 orchestra/apps/users/api.py create mode 100644 orchestra/apps/users/backends.py create mode 100644 orchestra/apps/users/forms.py create mode 100644 orchestra/apps/users/models.py create mode 100644 orchestra/apps/users/roles/__init__.py create mode 100644 orchestra/apps/users/roles/admin.py create mode 100644 orchestra/apps/users/roles/filters.py create mode 100644 orchestra/apps/users/roles/forms.py create mode 100644 orchestra/apps/users/roles/jabber/__init__.py create mode 100644 orchestra/apps/users/roles/jabber/admin.py create mode 100644 orchestra/apps/users/roles/jabber/models.py create mode 100644 orchestra/apps/users/roles/mail/__init__.py create mode 100644 orchestra/apps/users/roles/mail/admin.py create mode 100644 orchestra/apps/users/roles/mail/api.py create mode 100644 orchestra/apps/users/roles/mail/backends.py create mode 100644 orchestra/apps/users/roles/mail/forms.py create mode 100644 orchestra/apps/users/roles/mail/models.py create mode 100644 orchestra/apps/users/roles/mail/serializers.py create mode 100644 orchestra/apps/users/roles/mail/settings.py create mode 100644 orchestra/apps/users/roles/mail/validators.py create mode 100644 orchestra/apps/users/roles/owncloud/__init__.py create mode 100644 orchestra/apps/users/roles/posix/__init__.py create mode 100644 orchestra/apps/users/roles/posix/admin.py create mode 100644 orchestra/apps/users/roles/posix/models.py create mode 100644 orchestra/apps/users/roles/posix/serializers.py create mode 100644 orchestra/apps/users/roles/posix/settings.py create mode 100644 orchestra/apps/users/serializers.py create mode 100644 orchestra/apps/users/settings.py create mode 100644 orchestra/apps/users/templates/admin/users/user/change_form.html create mode 100644 orchestra/apps/users/templates/admin/users/user/delete_role.html create mode 100644 orchestra/apps/users/templates/admin/users/user/role.html create mode 100644 orchestra/apps/vps/__init__.py create mode 100644 orchestra/apps/vps/admin.py create mode 100644 orchestra/apps/vps/forms.py create mode 100644 orchestra/apps/vps/models.py create mode 100644 orchestra/apps/vps/settings.py create mode 100644 orchestra/apps/webapps/__init__.py create mode 100644 orchestra/apps/webapps/admin.py create mode 100644 orchestra/apps/webapps/api.py create mode 100644 orchestra/apps/webapps/backends/__init__.py create mode 100644 orchestra/apps/webapps/backends/awstats.py create mode 100644 orchestra/apps/webapps/backends/dokuwikimu.py create mode 100644 orchestra/apps/webapps/backends/drupalmu.py create mode 100644 orchestra/apps/webapps/backends/phpfcgid.py create mode 100644 orchestra/apps/webapps/backends/phpfpm.py create mode 100644 orchestra/apps/webapps/backends/static.py create mode 100644 orchestra/apps/webapps/backends/wordpressmu.py create mode 100644 orchestra/apps/webapps/models.py create mode 100644 orchestra/apps/webapps/serializers.py create mode 100644 orchestra/apps/webapps/settings.py create mode 100644 orchestra/apps/websites/__init__.py create mode 100644 orchestra/apps/websites/admin.py create mode 100644 orchestra/apps/websites/api.py create mode 100644 orchestra/apps/websites/backends/__init__.py create mode 100644 orchestra/apps/websites/backends/apache.py create mode 100644 orchestra/apps/websites/backends/webalizer.py create mode 100644 orchestra/apps/websites/models.py create mode 100644 orchestra/apps/websites/serializers.py create mode 100644 orchestra/apps/websites/settings.py create mode 100755 orchestra/bin/celerybeat create mode 100755 orchestra/bin/celeryd create mode 100755 orchestra/bin/celeryevcam create mode 100755 orchestra/bin/django_bash_completion.sh create mode 100755 orchestra/bin/orchestra-admin create mode 100755 orchestra/bin/sieve-test create mode 100644 orchestra/conf/__init__.py create mode 100644 orchestra/conf/base_settings.py create mode 100644 orchestra/conf/devel_settings.py create mode 100644 orchestra/conf/production_settings.py create mode 100755 orchestra/conf/project_template/manage.py create mode 100644 orchestra/conf/project_template/project_name/__init__.py create mode 100644 orchestra/conf/project_template/project_name/settings.py create mode 100644 orchestra/conf/project_template/project_name/urls.py create mode 100644 orchestra/conf/project_template/project_name/wsgi.py create mode 100644 orchestra/core/__init__.py create mode 100644 orchestra/core/context_processors.py create mode 100644 orchestra/core/validators.py create mode 100644 orchestra/forms/__init__.py create mode 100644 orchestra/forms/fields.py create mode 100644 orchestra/forms/widgets.py create mode 100644 orchestra/management/__init__.py create mode 100644 orchestra/management/commands/__init__.py create mode 100644 orchestra/management/commands/orchestraversion.py create mode 100644 orchestra/management/commands/postupgradeorchestra.py create mode 100644 orchestra/management/commands/restartservices.py create mode 100644 orchestra/management/commands/setupcelery.py create mode 100644 orchestra/management/commands/setupnginx.py create mode 100644 orchestra/management/commands/setuppostfix.py create mode 100644 orchestra/management/commands/setuppostgres.py create mode 100644 orchestra/management/commands/startservices.py create mode 100644 orchestra/management/commands/stopservices.py create mode 100644 orchestra/management/commands/upgradeorchestra.py create mode 100644 orchestra/models/__init__.py create mode 100644 orchestra/models/fields.py create mode 100644 orchestra/models/utils.py create mode 100644 orchestra/permissions/__init__.py create mode 100644 orchestra/permissions/api.py create mode 100644 orchestra/permissions/auth.py create mode 100644 orchestra/permissions/options.py create mode 100644 orchestra/settings.py create mode 100644 orchestra/static/admin/css/login.css create mode 100644 orchestra/static/admin_tools/css/theming.css create mode 100644 orchestra/static/orchestra/css/adminextraprettystyle.css create mode 100644 orchestra/static/orchestra/css/hide-inline-id.css create mode 100644 orchestra/static/orchestra/icons/Applications-internet.png create mode 100644 orchestra/static/orchestra/icons/Applications-internet.svg create mode 100644 orchestra/static/orchestra/icons/Applications-other.png create mode 100644 orchestra/static/orchestra/icons/Applications-other.svg create mode 100644 orchestra/static/orchestra/icons/Emblem-important.png create mode 100644 orchestra/static/orchestra/icons/Emblem-important.svg create mode 100644 orchestra/static/orchestra/icons/Face-monkey.png create mode 100644 orchestra/static/orchestra/icons/Face-monkey.svg create mode 100644 orchestra/static/orchestra/icons/Koala.png create mode 100644 orchestra/static/orchestra/icons/Koala.svg create mode 100644 orchestra/static/orchestra/icons/Mr-potato.png create mode 100644 orchestra/static/orchestra/icons/Mr-potato.svg create mode 100644 orchestra/static/orchestra/icons/Text-x-boo.svg create mode 100644 orchestra/static/orchestra/icons/Text-x-script.svg create mode 100644 orchestra/static/orchestra/icons/Ticket_star.png create mode 100644 orchestra/static/orchestra/icons/Tux.png create mode 100644 orchestra/static/orchestra/icons/Tux.svg create mode 100644 orchestra/static/orchestra/icons/TuxBox.png create mode 100644 orchestra/static/orchestra/icons/TuxBox.svg create mode 100644 orchestra/static/orchestra/icons/X-office-address-book.png create mode 100644 orchestra/static/orchestra/icons/X-office-address-book.svg create mode 100644 orchestra/static/orchestra/icons/apps.png create mode 100644 orchestra/static/orchestra/icons/apps.svg create mode 100644 orchestra/static/orchestra/icons/auth.svg create mode 100644 orchestra/static/orchestra/icons/bill.svg create mode 100644 orchestra/static/orchestra/icons/contact.png create mode 100644 orchestra/static/orchestra/icons/contact.svg create mode 100644 orchestra/static/orchestra/icons/daemon.png create mode 100644 orchestra/static/orchestra/icons/daemon.svg create mode 100644 orchestra/static/orchestra/icons/database.png create mode 100644 orchestra/static/orchestra/icons/database.svg create mode 100644 orchestra/static/orchestra/icons/dns.png create mode 100644 orchestra/static/orchestra/icons/dns.svg create mode 100644 orchestra/static/orchestra/icons/domain.png create mode 100644 orchestra/static/orchestra/icons/domain.svg create mode 100644 orchestra/static/orchestra/icons/email-alter.png create mode 100644 orchestra/static/orchestra/icons/email-alter.svg create mode 100644 orchestra/static/orchestra/icons/email.png create mode 100644 orchestra/static/orchestra/icons/email.svg create mode 100644 orchestra/static/orchestra/icons/extrafield.png create mode 100644 orchestra/static/orchestra/icons/extrafield.svg create mode 100644 orchestra/static/orchestra/icons/gnome-terminal.png create mode 100644 orchestra/static/orchestra/icons/gnome-terminal.svg create mode 100644 orchestra/static/orchestra/icons/hal.png create mode 100644 orchestra/static/orchestra/icons/hal.svg create mode 100644 orchestra/static/orchestra/icons/job.svg create mode 100644 orchestra/static/orchestra/icons/monitor.png create mode 100644 orchestra/static/orchestra/icons/monitor.svg create mode 100644 orchestra/static/orchestra/icons/mysql.png create mode 100644 orchestra/static/orchestra/icons/mysql.svg create mode 100644 orchestra/static/orchestra/icons/order.png create mode 100644 orchestra/static/orchestra/icons/order.svg create mode 100644 orchestra/static/orchestra/icons/periodictask.svg create mode 100644 orchestra/static/orchestra/icons/postgresql.png create mode 100644 orchestra/static/orchestra/icons/postgresql.svg create mode 100644 orchestra/static/orchestra/icons/scriptlog.png create mode 100644 orchestra/static/orchestra/icons/scriptlog.svg create mode 100644 orchestra/static/orchestra/icons/ssh.svg create mode 100755 orchestra/static/orchestra/icons/taskstate.png create mode 100644 orchestra/static/orchestra/icons/transaction.png create mode 100644 orchestra/static/orchestra/icons/transaction.svg create mode 100644 orchestra/static/orchestra/icons/users.png create mode 100644 orchestra/static/orchestra/icons/users.svg create mode 100644 orchestra/static/orchestra/icons/vps.png create mode 100644 orchestra/static/orchestra/icons/vps.svg create mode 100644 orchestra/static/orchestra/icons/web.png create mode 100644 orchestra/static/orchestra/icons/web.svg create mode 100644 orchestra/static/orchestra/icons/zone.png create mode 100644 orchestra/static/orchestra/icons/zone.svg create mode 100644 orchestra/static/orchestra/images/favicon.png create mode 100644 orchestra/static/orchestra/images/orchestra-logo.png create mode 100644 orchestra/static/orchestra/images/orchestra-logo.svg create mode 100644 orchestra/static/orchestra/images/page-gradient.png create mode 100644 orchestra/templates/admin/base.html create mode 100644 orchestra/templates/admin/base_site.html create mode 100644 orchestra/templates/admin/index.html create mode 100644 orchestra/templates/admin/login.html create mode 100644 orchestra/templates/rest_framework/api.html create mode 100644 orchestra/templatetags/__init__.py create mode 100644 orchestra/templatetags/markdown.py create mode 100644 orchestra/templatetags/utils.py create mode 100644 orchestra/urls.py create mode 100644 orchestra/utils/__init__.py create mode 100644 orchestra/utils/apps.py create mode 100644 orchestra/utils/functional.py create mode 100644 orchestra/utils/options.py create mode 100644 orchestra/utils/paths.py create mode 100644 orchestra/utils/plugins.py create mode 100644 orchestra/utils/python.py create mode 100644 orchestra/utils/system.py create mode 100644 orchestra/utils/tests.py create mode 100644 orchestra/utils/time.py create mode 100755 scripts/container/create.sh create mode 100755 scripts/container/deploy.sh create mode 100644 scripts/migration/README.md create mode 100644 scripts/migration/accounts.sh create mode 100644 scripts/migration/apache2.py create mode 100644 scripts/migration/domains.sh create mode 100644 scripts/migration/mailbox.sh create mode 100644 scripts/migration/mysql.sh create mode 100644 scripts/migration/virtusertable.sh create mode 100644 scripts/services/README.md create mode 100644 scripts/services/apache_full_stack.md create mode 100644 scripts/services/bind9.sh create mode 100644 scripts/services/mailman.sh create mode 100644 scripts/services/mysql.sh create mode 100644 scripts/services/php4_on_debian.md create mode 100644 scripts/services/postfix.md create mode 100644 scripts/services/vsftpd.md create mode 100644 scripts/services/webalizer.md create mode 100644 scripts/tests/setup.sh create mode 100644 setup.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..3a8423d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*.log +*.pot +*.pyc +*~ +.svn +local_settings.py diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 00000000..8afa9103 --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,83 @@ +Installation +============ + +Django-orchestra ships with a set of management commands for automating some of the installation steps. + +These commands are meant to be run within a **clean** Debian-like distribution, you should be specially careful while following this guide on a customized system. + +Django-orchestra can be installed on any Linux system, however it is **strongly recommended** to chose the reference platform for your deployment (Debian 7.0 wheezy and Python 2.7). + + +1. Create a system user for running Orchestra + ```bash + adduser orchestra + # not required but it will be very handy + sudo adduser orchestra sudo + su - orchestra + ``` + +2. Install django-orchestra's source code + ```bash + sudo apt-get install python-pip + sudo pip install django-orchestra # ==dev if you want the in-devel version + ``` + +3. Install requirements + ```bash + sudo orchestra-admin install_requirements + ``` + +4. Create a new project + ```bash + cd ~orchestra + orchestra-admin startproject # e.g. panel + cd + ``` + +5. Create and configure a Postgres database + ```bash + sudo python manage.py setuppostgres + python manage.py syncdb + python manage.py migrate + ``` + +6. Create a panel administrator + ```bash + python manage.py createsuperuser + ``` + +7. Configure celeryd + ```bash + sudo python manage.py setupcelery --username orchestra + ``` + +8. Configure the web server: + ```bash + python manage.py collectstatic --noinput + sudo apt-get install nginx-full uwsgi uwsgi-plugin-python + sudo python manage.py setupnginx + ``` + +9. Start all services: + ```bash + sudo python manage.py startservices + ``` + + +Upgrade +======= +To upgrade your Orchestra installation to the last release you can use `upgradeorchestra` management command. Before rolling the upgrade it is strongly recommended to check the [release notes](http://django-orchestra.readthedocs.org/en/latest/). +```bash +sudo python manage.py upgradeorchestra +``` + +Current in *development* version (master branch) can be installed by +```bash +sudo python manage.py upgradeorchestra dev +``` + +Additionally the following command can be used in order to determine the currently installed version: +```bash +python manage.py orchestraversion +``` + diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..c684ec22 --- /dev/null +++ b/LICENSE @@ -0,0 +1,14 @@ +Copyright (C) 2013 Marc Aymerich + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 00000000..b6c1788d --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,8 @@ +recursive-include orchestra * + +recursive-exclude * __pycache__ +recursive-exclude * *.py[co] +recursive-exclude * *~ +recursive-exclude * *.save +recursive-exclude * *.svg + diff --git a/README.md b/README.md new file mode 100644 index 00000000..b268ef80 --- /dev/null +++ b/README.md @@ -0,0 +1,94 @@ +![](orchestra/static/orchestra/icons/Emblem-important.png) **This project is in early development stage** + +Django Orchestra +================ + +Orchestra is a Django-based framework for building web hosting control panels. + +* [Documentation](http://django-orchestra.readthedocs.org/) +* [Install and upgrade](INSTALL.md) +* [Roadmap](ROADMAP.md) + + +Motivation +---------- +There are a lot of widely used open source hosting control panels, however, none of them seems apropiate when you already have an existing service infrastructure or simply you want your services to run on a particular architecture. + +The goal of this project is to provide the tools for easily build a fully featured control panel that is not tied to any particular service architecture. + + +Overview +-------- + +Django-orchestra is mostly a bunch of [plugable applications](orchestra/apps) providing common functionalities, like service management, resource monitoring or billing. + +The admin interface relies on [Django Admin](https://docs.djangoproject.com/en/dev/ref/contrib/admin/), but enhaced with [Django Admin Tools](https://bitbucket.org/izi/django-admin-tools) and [Django Fluent Dashboard](https://github.com/edoburu/django-fluent-dashboard). [Django REST Framework](http://www.django-rest-framework.org/) is used for the REST API, with it you can build your client-side custom user interface. + +Every app is [reusable](https://docs.djangoproject.com/en/dev/intro/reusable-apps/), this means that you can add any Orchestra application into your Django project `INSTALLED_APPS` strigh away. +However, Orchestra also provides glue, tools and patterns that you may find very convinient to use. Checkout the [documentation](http://django-orchestra.readthedocs.org/) if you want to know more. + + + +Development and Testing Setup +----------------------------- +If you are planing to do some development or perhaps just checking out this project, you may want to consider doing it under the following setup + +1. Create a basic [LXC](http://linuxcontainers.org/) container, start it and get inside. + ```bash + wget -O /tmp/create.sh \ + https://raw2.github.com/glic3rinu/django-orchestra/master/scripts/container/create.sh + chmod +x /tmp/create.sh + sudo /tmp/create.sh + sudo lxc-start -n orchestra + ``` + +2. Deploy Django-orchestra development environment inside the container + ```bash + wget -O /tmp/deploy.sh \ + https://raw2.github.com/glic3rinu/django-orchestra/master/scripts/container/deploy.sh + chmod +x /tmp/deploy.sh + cd /tmp/ # Moving away from /root before running deploy.sh + /tmp/deploy.sh + ``` + Django-orchestra source code should be now under `~orchestra/django-orchestra` and an Orchestra instance called _panel_ under `~orchestra/panel` + + +3. Nginx should be serving on port 80, but Django's development server can be used as well: + ```bash + su - orchestra + cd panel + python manage.py runserver 0.0.0.0:8888 + ``` + +4. A convenient practice can be mounting `~orchestra` on your host machine so you can code with your favourite IDE, sshfs can be used for that + ```bash + # On your host + mkdir ~/orchestra + sshfs orchestra@: ~/orchestra + ``` + +5. To upgrade to current master just + ```bash + cd ~orchestra/django-orchestra/ + git pull origin master + sudo ~orchestra/django-orchestra/scripts/container/deploy.sh + ``` + + +License +------- +Copyright (C) 2013 Marc Aymerich + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +Status API Training Shop Blog About diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 00000000..4f44b598 --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,43 @@ +# Roadmap + + +### 1.0a1 Milestone (first alpha release on May '14) + +1. [x] Automated deployment of the development environment +2. [x] Automated installation and upgrading +2. [ ] Testing framework for running unittests and functional tests +2. [ ] Continuous integration environment +2. [x] Admin interface based on django.contrib.admin foundations +3. [x] REST API based on django-rest-framework foundations +2. [x] [Orchestra-orm](https://github.com/glic3rinu/orchestra-orm) a Python library for easily interacting with the REST API +3. [x] Service orchestration framework +4. [ ] Data model, input validation, admin and REST interfaces, permissions, unit and functional tests, service management, migration scripts and some documentation of: + 1. [ ] Web applications and FTP accounts + 2. [ ] Databases + 1. [ ] Mail accounts, aliases, forwards + 1. [ ] DNS + 1. [ ] Mailing lists +1. [ ] Contact management and service contraction +1. [ ] Object level permissions system +1. [ ] Unittests of all the logic +2. [ ] Functional tests of all Admin and REST interations +1. [ ] Initial documentation + + +### 1.0b1 Milestone (first beta release on Jul '14) + +1. [ ] Resource monitoring +1. [ ] Orders +2. [ ] Pricing +3. [ ] Billing +1. [ ] Payment gateways +2. [ ] Scheduling of service cancellations +1. [ ] Full documentation + + +### 1.0 Milestone (first stable release on Dec '14) + +1. [ ] Stabilize data model, internal APIs and REST API +1. [ ] Integration with third-party service providers, e.g. Gandi +1. [ ] Support for additional services like VPS +2. [ ] Issue tracking system diff --git a/TODO.md b/TODO.md new file mode 100644 index 00000000..40ba2b24 --- /dev/null +++ b/TODO.md @@ -0,0 +1,37 @@ +TODO +==== + +* scape strings before executing scripts in order to prevent exploits: django templates automatically scapes things. Most important is to ensuer that all escape ' to " +* Optimize SSH: pool, `UseDNS no` +* Don't store passwords and other service parameters that can be changed by the services i.e. mailman, vps etc. Find an execution mechanism that trigger `change_password()` + +* abort transaction on orchestration when `state == TIMEOUT` ? +* filter and other user.is_main refactoring +* use format_html_join for orchestration email alerts + +* generic form for change and display passwords and crack change password form +* enforce an emergency email contact and account to contact contacts about problems when mailserver is down + +* add `BackendLog` retry action +* move invoice contact to invoices app? +* wrapper around reverse('admin:....') `link()` and `link_factory()` +* PHPbBckendMiixin with get_php_ini +* Apache: `IncludeOptional /etc/apache2/extra-vhos[t]/account-site-custom.con[f]` +* rename account.user to primary_user +* webmail identities and addresses +* cached -> cached_property +* user.roles.mailbox its awful when combined with addresses: + * address.mailboxes filter by account is crap in admin and api + * address.mailboxes api needs a mailbox object endpoint (not nested user) + * Its not intuitive, users expect to create mailboxes, not users! + * Mailbox is something tangible, not a role! +* System user vs virtual user: + * system user automatically hast @domain.com address :( + +* use Code: https://github.com/django/django/blob/master/django/forms/forms.py#L415 for domain.refresh_serial() +* Permissions .filter_queryset() + + +* git deploy in addition to FTP? +* env vars instead of multiple settings files: https://devcenter.heroku.com/articles/config-vars ? +* optional chroot shell? diff --git a/build/pip-delete-this-directory.txt b/build/pip-delete-this-directory.txt new file mode 100644 index 00000000..c8883ea9 --- /dev/null +++ b/build/pip-delete-this-directory.txt @@ -0,0 +1,5 @@ +This file is placed here by pip to indicate the source was put +here by pip. + +Once this package is successfully installed this source code will be +deleted (unless you remove this file). diff --git a/docs/API.rst b/docs/API.rst new file mode 100644 index 00000000..228cf72e --- /dev/null +++ b/docs/API.rst @@ -0,0 +1,222 @@ +================================= + Orchestra REST API Specification +================================= + +:Version: 0.1 + +Resources +--------- + +.. contents:: + :local: + +Panel [application/vnd.orchestra.Panel+json] +============================================ + +A Panel represents a user's view of all accessible resources. +A "Panel" resource model contains the following fields: + +========================== ============ ========== =========================== +**Field name** **Type** **Occurs** **Description** +========================== ============ ========== =========================== +uri URI 1 A GET against this URI refreshes the client representation of the resources accessible to this user. +services Object[] 0..1 {'DNS': {'names': "names_URI", 'zones': "zones_URI}, {'Mail': {'Virtual_user': "virtual_user_URI" .... +accountancy Object[] 0..1 +administration Object[] 0..1 +========================== ============ ========== =========================== + + +Contact [application/vnd.orchestra.Contact+json] +================================================ + +A Contact represents + +========================== ============ ========== =========================== +**Field name** **Type** **Occurs** **Description** +========================== ============ ========== =========================== +uri URI 1 +name String 1 +surname String 0..1 +second_surname String 0..1 +national_id String 1 +type String 1 +language String 1 +address String 1 +city String 1 +zipcode Number 1 +province String 1 +country String 1 +fax String 0..1 +emails String[] 1 +phones String[] 1 +billing_contact Contact 0..1 +technical_contact Contact 0..1 +administrative_contact Contact 0..1 +========================== ============ ========== =========================== + +TODO: phone and emails for this contacts this contacts should be equal to Contact on Django models + + +User [application/vnd.orchestra.User+json] +========================================== + +A User represents + +========================== ============ ========== =========================== +**Field name** **Type** **Occurs** **Description** +========================== ============ ========== =========================== +username String +uri URI 1 +contact Contact +password String +first_name String +last_name String +email_address String +active Boolean +staff_status Boolean +superuser_status Boolean +groups Group +user_permissions Permission[] +last_login String +date_joined String +system_user SystemUser +virtual_user VirtualUser +========================== ============ ========== =========================== + + +SystemUser [application/vnd.orchestra.SystemUser+json] +====================================================== + +========================== =========== ========== =========================== +**Field name** **Type** **Occurs** **Description** +========================== =========== ========== =========================== +user User +uri URI 1 +user_shell String +user_uid Number +primary_group Group +homedir String +only_ftp Boolean +========================== =========== ========== =========================== + + +VirtualUser [application/vnd.orchestra.VirtualUser+json] +======================================================== + +========================== ============ ========== =========================== +**Field name** **Type** **Occurs** **Description** +========================== ============ ========== =========================== +user User +uri URI 1 +emailname String +domain Name +home String +========================== ============ ========== =========================== + +Zone [application/vnd.orchestra.Zone+json] +========================================== + +========================== ============ ========== =========================== +**Field name** **Type** **Occurs** **Description** +========================== ============ ========== =========================== +origin String +uri URI 1 +contact Contact +primary_ns String +hostmaster_email String +serial Number +slave_refresh Number +slave_retry Number +slave_expiration Number +min_caching_time Number +records Object[] Domain record i.e. {'name': ('type', 'value') } +========================== ============ ========== =========================== + +Name [application/vnd.orchestra.Name+json] +========================================== +========================== ============ ========== =========================== +**Field name** **Type** **Occurs** **Description** +========================== ============ ========== =========================== +name String +extension String +uri URI 1 +contact Contact +register_provider String +name_server Object[] Name server key/value i.e. {'ns1.pangea.org': '1.1.1.1'} +virtual_domain Boolean +virtual_domain_type String +zone Zone +========================== ============ ========== =========================== + +VirtualHost [application/vnd.orchestra.VirtualHost+json] +======================================================== + +A VirtualHost represents an Apache-like virtualhost configuration, which is useful for generating all the configuration files on the web server. +A VirtualHost resource model contains the following fields: + +========================== ============ ========== =========================== +**Field name** **Type** **Occurs** **Description** +========================== ============ ========== =========================== +server_name String +uri URI +contact Contact +ip String +port Number +domains Name[] +document_root String +custom_directives String[] +fcgid_user String +fcgid_group string String +fcgid_directives Object Fcgid custom directives represented on a key/value pairs i.e. {'FcgidildeTimeout': 1202} +php_version String +php_directives Object PHP custom directives represented on key/value pairs i.e. {'display errors': 'True'} +resource_swap_current Number PHP custom directives represented on key/value pairs i.e. {'display errors': 'True'} +resource_swap_limit Number PHP custom directives represented on key/value pairs i.e. {'display errors': 'True'} +resource_cpu_current Number +resource_cpu_limit Number +========================== ============ ========== =========================== + +Daemon [application/vnd.orchestra.Daemon+json] +============================================== + +========================== ============ ========== =========================== +**Field name** **Type** **Occurs** **Description** +========================== ============ ========== =========================== +name String +uri URI 1 +content_type String +active Boolean +save_template String +save_method String +delete_template String +delete_method String +daemon_instances Object[] {'host': 'expression'} +========================== ============ ========== =========================== + +Monitor [application/vnd.orchestra.Monitor+json] +================================================ + +========================== ============ ========== =========================== +**Field name** **Type** **Occurs** **Description** +========================== ============ ========== =========================== +uri URI 1 +daemon Daemon +resource String +monitoring_template String +monitoring method String +exceed_template String +exceed_method String +recover_template String +recover_method String +allow_limit Boolean +allow_unlimit Boolean +default_initial Number +block_size Number +algorithm String +period String +interval String 0..1 +crontab String 0..1 +========================== ============ ========== =========================== + + +#Layout inspired from http://kenai.com/projects/suncloudapis/pages/CloudAPISpecificationResourceModels diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..2626a9cf --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,153 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-orchestra.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-orchestra.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/django-orchestra" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-orchestra" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 00000000..7b99e9cd --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,242 @@ +# -*- coding: utf-8 -*- +# +# django-orchestra documentation build configuration file, created by +# sphinx-quickstart on Wed Aug 8 11:07:40 2012. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'django-orchestra' +copyright = u'2012, Marc Aymerich' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.1' +# The full version, including alpha/beta/rc tags. +release = '0.1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'django-orchestradoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'django-orchestra.tex', u'django-orchestra Documentation', + u'Marc Aymerich', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'django-orchestra', u'django-orchestra Documentation', + [u'Marc Aymerich'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'django-orchestra', u'django-orchestra Documentation', + u'Marc Aymerich', 'django-orchestra', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 00000000..4a15192a --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,21 @@ +.. django-orchestra documentation master file, created by + sphinx-quickstart on Wed Aug 8 11:07:40 2012. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to django-orchestra's documentation! +============================================ + +Contents: + +.. toctree:: + :maxdepth: 2 + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 00000000..83d5e83c --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,190 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\django-orchestra.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\django-orchestra.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +:end diff --git a/orchestra/__init__.py b/orchestra/__init__.py new file mode 100644 index 00000000..892726ce --- /dev/null +++ b/orchestra/__init__.py @@ -0,0 +1,23 @@ +VERSION = (0, 0, 1, 'alpha', 1) + + +def get_version(): + "Returns a PEP 386-compliant version number from VERSION." + assert len(VERSION) == 5 + assert VERSION[3] in ('alpha', 'beta', 'rc', 'final') + + # Now build the two parts of the version number: + # main = X.Y[.Z] + # sub = .devN - for pre-alpha releases + # | {a|b|c}N - for alpha, beta and rc releases + + parts = 2 if VERSION[2] == 0 else 3 + main = '.'.join(str(x) for x in VERSION[:parts]) + + sub = '' + + if VERSION[3] != 'final': + mapping = {'alpha': 'a', 'beta': 'b', 'rc': 'c'} + sub = mapping[VERSION[3]] + str(VERSION[4]) + + return str(main + sub) diff --git a/orchestra/admin/__init__.py b/orchestra/admin/__init__.py new file mode 100644 index 00000000..89dd130c --- /dev/null +++ b/orchestra/admin/__init__.py @@ -0,0 +1,2 @@ +from options import * +from dashboard import * diff --git a/orchestra/admin/dashboard.py b/orchestra/admin/dashboard.py new file mode 100644 index 00000000..fa6afc23 --- /dev/null +++ b/orchestra/admin/dashboard.py @@ -0,0 +1,20 @@ +from django.conf import settings + +from orchestra.core import services + + +def generate_services_group(): + models = [] + for model, options in services.get().iteritems(): + if options.get('menu', True): + models.append("%s.%s" % (model.__module__, model._meta.object_name)) + + settings.FLUENT_DASHBOARD_APP_GROUPS += ( + ('Services', { + 'models': models, + 'collapsible': True + }), + ) + + +generate_services_group() diff --git a/orchestra/admin/decorators.py b/orchestra/admin/decorators.py new file mode 100644 index 00000000..61ec215a --- /dev/null +++ b/orchestra/admin/decorators.py @@ -0,0 +1,55 @@ +from functools import wraps + +from django.contrib import messages +from django.contrib.admin import helpers +from django.template.response import TemplateResponse +from django.utils.decorators import available_attrs +from django.utils.encoding import force_text + + +def action_with_confirmation(action_name, extra_context={}, + template='admin/controller/generic_confirmation.html'): + """ + Generic pattern for actions that needs confirmation step + If custom template is provided the form must contain: + + """ + def decorator(func, extra_context=extra_context, template=template): + @wraps(func, assigned=available_attrs(func)) + def inner(modeladmin, request, queryset): + # The user has already confirmed the action. + if request.POST.get('post') == "generic_confirmation": + stay = func(modeladmin, request, queryset) + if not stay: + return + + opts = modeladmin.model._meta + app_label = opts.app_label + action_value = func.__name__ + + if len(queryset) == 1: + objects_name = force_text(opts.verbose_name) + else: + objects_name = force_text(opts.verbose_name_plural) + + context = { + "title": "Are you sure?", + "content_message": "Are you sure you want to %s the selected %s?" % + (action_name, objects_name), + "action_name": action_name.capitalize(), + "action_value": action_value, + "deletable_objects": queryset, + 'queryset': queryset, + "opts": opts, + "app_label": app_label, + 'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME, + } + + context.update(extra_context) + + # Display the confirmation page + return TemplateResponse(request, template, + context, current_app=modeladmin.admin_site.name) + return inner + return decorator + diff --git a/orchestra/admin/html.py b/orchestra/admin/html.py new file mode 100644 index 00000000..c36322c6 --- /dev/null +++ b/orchestra/admin/html.py @@ -0,0 +1,10 @@ +from django.utils.safestring import mark_safe + + +MONOSPACE_FONTS = ('Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,' + 'Bitstream Vera Sans Mono,Courier New,monospace') + + +def monospace_format(text): + style="font-family:%s;padding-left:110px;" % MONOSPACE_FONTS + return mark_safe('
%s
' % (style, text)) diff --git a/orchestra/admin/menu.py b/orchestra/admin/menu.py new file mode 100644 index 00000000..df218af8 --- /dev/null +++ b/orchestra/admin/menu.py @@ -0,0 +1,96 @@ +from admin_tools.menu import items, Menu +from django.core.urlresolvers import reverse +from django.utils.translation import ugettext_lazy as _ + +from orchestra.core import services +from orchestra.utils.apps import isinstalled + + +def api_link(context): + """ Dynamically generates API related URL """ + if 'opts' in context: + opts = context['opts'] + elif 'cl' in context: + opts = context['cl'].opts + else: + return reverse('api-root') + if 'object_id' in context: + object_id = context['object_id'] + try: + return reverse('%s-detail' % opts.module_name, args=[object_id]) + except: + return reverse('api-root') + try: + return reverse('%s-list' % opts.module_name) + except: + return reverse('api-root') + + +def get_services(): + result = [] + for model, options in services.get().iteritems(): + if options.get('menu', True): + opts = model._meta + url = reverse('admin:%s_%s_changelist' % (opts.app_label, opts.model_name)) + result.append(items.MenuItem(options.get('verbose_name_plural'), url)) + return sorted(result, key=lambda i: i.title) + + +def get_accounts(): + accounts = [ + items.MenuItem(_("Accounts"), reverse('admin:accounts_account_changelist')) + ] + if isinstalled('orchestra.apps.contacts'): + url = reverse('admin:contacts_contact_changelist') + accounts.append(items.MenuItem(_("Contacts"), url)) + if isinstalled('orchestra.apps.users'): + url = reverse('admin:users_user_changelist') + users = [items.MenuItem(_("Users"), url)] + if isinstalled('rest_framework.authtoken'): + tokens = reverse('admin:authtoken_token_changelist') + users.append(items.MenuItem(_("Tokens"), tokens)) + accounts.append(items.MenuItem(_("Users"), url, children=users)) + return accounts + + +def get_administration(): + administration = [] + return administration + + +def get_administration_models(): + administration_models = [] + if isinstalled('orchestra.apps.orchestration'): + administration_models.append('orchestra.apps.orchestration.*') + if isinstalled('djcelery'): + administration_models.append('djcelery.*') + if isinstalled('orchestra.apps.issues'): + administration_models.append('orchestra.apps.issues.*') + return administration_models + + +class OrchestraMenu(Menu): + def init_with_context(self, context): + self.children += [ + items.MenuItem( + _('Dashboard'), + reverse('admin:index') + ), + items.Bookmarks(), + items.MenuItem( + _("Services"), + reverse('admin:index'), + children=get_services() + ), + items.MenuItem( + _("Accounts"), + reverse('admin:accounts_account_changelist'), + children=get_accounts() + ), + items.AppList( + _("Administration"), + models=get_administration_models(), + children=get_administration() + ), + items.MenuItem("API", api_link(context)) + ] diff --git a/orchestra/admin/options.py b/orchestra/admin/options.py new file mode 100644 index 00000000..bcd70904 --- /dev/null +++ b/orchestra/admin/options.py @@ -0,0 +1,76 @@ +from django import forms +from django.contrib import admin +from django.forms.models import BaseInlineFormSet + +from .utils import set_default_filter + + +class ExtendedModelAdmin(admin.ModelAdmin): + add_fields = () + add_fieldsets = () + add_form = None + change_readonly_fields = () + + def get_readonly_fields(self, request, obj=None): + fields = super(ExtendedModelAdmin, self).get_readonly_fields(request, obj=obj) + if obj: + return fields + self.change_readonly_fields + return fields + + def get_fieldsets(self, request, obj=None): + if not obj: + if self.add_fieldsets: + return self.add_fieldsets + elif self.add_fields: + return [(None, {'fields': self.add_fields})] + return super(ExtendedModelAdmin, self).get_fieldsets(request, obj=obj) + + def get_inline_instances(self, request, obj=None): + """ add_inlines and inline.parent_object """ + self.inlines = getattr(self, 'add_inlines', self.inlines) + if obj: + self.inlines = type(self).inlines + inlines = super(ExtendedModelAdmin, self).get_inline_instances(request, obj=obj) + for inline in inlines: + inline.parent_object = obj + return inlines + + def get_form(self, request, obj=None, **kwargs): + """ Use special form during user creation """ + defaults = {} + if obj is None and self.add_form: + defaults['form'] = self.add_form + defaults.update(kwargs) + return super(ExtendedModelAdmin, self).get_form(request, obj, **defaults) + + +class ChangeListDefaultFilter(object): + """ + Enables support for default filtering on admin change list pages + Your model admin class should define an default_changelist_filters attribute + default_changelist_filters = (('my_nodes', 'True'),) + """ + default_changelist_filters = () + + def changelist_view(self, request, extra_context=None): + """ Default filter as 'my_nodes=True' """ + defaults = [] + for queryarg, value in self.default_changelist_filters: + set_default_filter(queryarg, request, value) + defaults.append(queryarg) + # hack response cl context in order to hook default filter awaearness into search_form.html template + response = super(ChangeListDefaultFilter, self).changelist_view(request, extra_context=extra_context) + if hasattr(response, 'context_data') and 'cl' in response.context_data: + response.context_data['cl'].default_changelist_filters = defaults + return response + + +class AtLeastOneRequiredInlineFormSet(BaseInlineFormSet): + def clean(self): + """Check that at least one service has been entered.""" + super(AtLeastOneRequiredInlineFormSet, self).clean() + if any(self.errors): + return + if not any(cleaned_data and not cleaned_data.get('DELETE', False) + for cleaned_data in self.cleaned_data): + raise forms.ValidationError('At least one item required.') diff --git a/orchestra/admin/utils.py b/orchestra/admin/utils.py new file mode 100644 index 00000000..8a888973 --- /dev/null +++ b/orchestra/admin/utils.py @@ -0,0 +1,133 @@ +from functools import update_wrapper + +from django.conf import settings +from django.contrib import admin +from django.core.urlresolvers import reverse +from django.db import models +from django.utils import importlib +from django.utils.html import escape +from django.utils.safestring import mark_safe +from django.utils.translation import ugettext_lazy as _ + +from orchestra.models.utils import get_field_value +from orchestra.utils.time import timesince, timeuntil + + +def get_modeladmin(model, import_module=True): + """ returns the modeladmin registred for model """ + for k,v in admin.site._registry.iteritems(): + if k is model: + return v + if import_module: + # Sometimes the admin module is not yet imported + app_label = model._meta.app_label + for app in settings.INSTALLED_APPS: + if app.endswith(app_label): + app_label = app + importlib.import_module('%s.%s' % (app_label, 'admin')) + return get_modeladmin(model, import_module=False) + + +def insertattr(model, name, value, weight=0): + """ Inserts attribute to a modeladmin """ + is_model = models.Model in model.__mro__ + modeladmin = get_modeladmin(model) if is_model else model + # Avoid inlines defined on parent class be shared between subclasses + # Seems that if we use tuples they are lost in some conditions like changing + # the tuple in modeladmin.__init__ + if not getattr(modeladmin, name): + setattr(type(modeladmin), name, []) + + inserted_attrs = getattr(modeladmin, '__inserted_attrs__', {}) + if not name in inserted_attrs: + weights = {} + if hasattr(modeladmin, 'weights') and name in modeladmin.weights: + weights = modeladmin.weights.get(name) + inserted_attrs[name] = [ (attr, weights.get(attr, 0)) for attr in getattr(modeladmin, name) ] + + inserted_attrs[name].append((value, weight)) + inserted_attrs[name].sort(key=lambda a: a[1]) + setattr(modeladmin, name, [ attr[0] for attr in inserted_attrs[name] ]) + setattr(modeladmin, '__inserted_attrs__', inserted_attrs) + + +def wrap_admin_view(modeladmin, view): + """ Add admin authentication to view """ + def wrapper(*args, **kwargs): + return modeladmin.admin_site.admin_view(view)(*args, **kwargs) + return update_wrapper(wrapper, view) + + +def set_default_filter(queryarg, request, value): + """ set default filters for changelist_view """ + if queryarg not in request.GET: + q = request.GET.copy() + if callable(value): + value = value(request) + q[queryarg] = value + request.GET = q + request.META['QUERY_STRING'] = request.GET.urlencode() + + +def link(*args, **kwargs): + """ utility function for creating admin links """ + field = args[0] if args else '' + order = kwargs.pop('order', field) + popup = kwargs.pop('popup', False) + + def display_link(self, instance): + obj = getattr(instance, field, instance) + if not getattr(obj, 'pk', False): + return '---' + opts = obj._meta + view_name = 'admin:%s_%s_change' % (opts.app_label, opts.model_name) + url = reverse(view_name, args=(obj.pk,)) + extra = '' + if popup: + extra = 'onclick="return showAddAnotherPopup(this);"' + return '%s' % (url, extra, obj) + display_link.allow_tags = True + display_link.short_description = _(field) + display_link.admin_order_field = order + return display_link + + +def colored(field_name, colours, description='', verbose=False, bold=True): + """ returns a method that will render obj with colored html """ + def colored_field(obj, field=field_name, colors=colours, verbose=verbose): + value = escape(get_field_value(obj, field)) + color = colors.get(value, "black") + if verbose: + # Get the human-readable value of a choice field + value = getattr(obj, 'get_%s_display' % field)() + colored_value = '%s' % (color, value) + if bold: + colored_value = '%s' % colored_value + return mark_safe(colored_value) + if not description: + description = field_name.split('__').pop().replace('_', ' ').capitalize() + colored_field.short_description = description + colored_field.allow_tags = True + colored_field.admin_order_field = field_name + return colored_field + + +def display_timesince(date, double=False): + """ + Format date for messages create_on: show a relative time + with contextual helper to show fulltime format. + """ + if not date: + return 'Never' + date_rel = timesince(date) + if not double: + date_rel = date_rel.split(',')[0] + date_rel += ' ago' + date_abs = date.strftime("%Y-%m-%d %H:%M:%S %Z") + return mark_safe("%s" % (date_abs, date_rel)) + + +def display_timeuntil(date): + date_rel = timeuntil(date) + ' left' + date_abs = date.strftime("%Y-%m-%d %H:%M:%S %Z") + return mark_safe("%s" % (date_abs, date_rel)) diff --git a/orchestra/api/__init__.py b/orchestra/api/__init__.py new file mode 100644 index 00000000..c5895bfd --- /dev/null +++ b/orchestra/api/__init__.py @@ -0,0 +1,2 @@ +from options import * +from actions import * diff --git a/orchestra/api/actions.py b/orchestra/api/actions.py new file mode 100644 index 00000000..0a02be68 --- /dev/null +++ b/orchestra/api/actions.py @@ -0,0 +1,18 @@ +from rest_framework import status +from rest_framework.decorators import action +from rest_framework.response import Response + +from .serializers import SetPasswordSerializer + + +class SetPasswordApiMixin(object): + @action(serializer_class=SetPasswordSerializer) + def set_password(self, request, pk): + obj = self.get_object() + serializer = SetPasswordSerializer(data=request.DATA) + if serializer.is_valid(): + obj.set_password(serializer.data['password']) + obj.save() + return Response({'status': 'password changed'}) + else: + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) diff --git a/orchestra/api/fields.py b/orchestra/api/fields.py new file mode 100644 index 00000000..16d42f80 --- /dev/null +++ b/orchestra/api/fields.py @@ -0,0 +1,51 @@ +import json + +from rest_framework import serializers, exceptions + + +class OptionField(serializers.WritableField): + """ + Dict-like representation of a OptionField + A bit hacky, objects get deleted on from_native method and Serializer will + need a custom override of restore_object method. + """ + def to_native(self, value): + """ dict-like representation of a Property Model""" + return dict((prop.name, prop.value) for prop in value.all()) + + def from_native(self, value): + """ Convert a dict-like representation back to a WebOptionField """ + parent = self.parent + related_manager = getattr(parent.object, self.source or 'options', False) + properties = serializers.RelationsList() + if value: + model = getattr(parent.opts.model, self.source or 'options').related.model + if isinstance(value, basestring): + try: + value = json.loads(value) + except: + raise exceptions.ParseError("Malformed property: %s" % str(value)) + if not related_manager: + # POST (new parent object) + return [ model(name=n, value=v) for n,v in value.iteritems() ] + # PUT + to_save = [] + for (name, value) in value.iteritems(): + try: + # Update existing property + prop = related_manager.get(name=name) + except model.DoesNotExist: + # Create a new one + prop = model(name=name, value=value) + else: + prop.value = value + to_save.append(prop.pk) + properties.append(prop) + + # Discart old values + if related_manager: + properties._deleted = [] # Redefine class attribute + for obj in related_manager.all(): + if not value or obj.pk not in to_save: + properties._deleted.append(obj) + return properties diff --git a/orchestra/api/helpers.py b/orchestra/api/helpers.py new file mode 100644 index 00000000..80b7c6a9 --- /dev/null +++ b/orchestra/api/helpers.py @@ -0,0 +1,52 @@ +from django.core.urlresolvers import NoReverseMatch +from rest_framework.reverse import reverse +from rest_framework.routers import replace_methodname + + +def replace_collectionmethodname(format_string, methodname): + ret = replace_methodname(format_string, methodname) + ret = ret.replace('{collectionmethodname}', methodname) + return ret + + +def link_wrap(view, view_names): + def wrapper(self, request, view=view, *args, **kwargs): + """ wrapper function that inserts HTTP links on view """ + links = [] + for name in view_names: + try: + url = reverse(name, request=self.request) + except NoReverseMatch: + url = reverse(name, args, kwargs, request=request) + links.append('<%s>; rel="%s"' % (url, name)) + response = view(self, request, *args, **kwargs) + response['Link'] = ', '.join(links) + return response + for attr in dir(view): + try: + setattr(wrapper, attr, getattr(view, attr)) + except: + pass + return wrapper + + +def insert_links(viewset, base_name): + collection_links = ['api-root', '%s-list' % base_name] + object_links = ['api-root', '%s-list' % base_name, '%s-detail' % base_name] + exception_links = ['api-root'] + list_links = ['api-root'] + retrieve_links = ['api-root', '%s-list' % base_name] + # Determine any `@action` or `@link` decorated methods on the viewset + for methodname in dir(viewset): + method = getattr(viewset, methodname) + view_name = '%s-%s' % (base_name, methodname.replace('_', '-')) + if hasattr(method, 'collection_bind_to_methods'): + list_links.append(view_name) + retrieve_links.append(view_name) + setattr(viewset, methodname, link_wrap(method, collection_links)) + elif hasattr(method, 'bind_to_methods'): + retrieve_links.append(view_name) + setattr(viewset, methodname, link_wrap(method, object_links)) + viewset.handle_exception = link_wrap(viewset.handle_exception, exception_links) + viewset.list = link_wrap(viewset.list, list_links) + viewset.retrieve = link_wrap(viewset.retrieve, retrieve_links) diff --git a/orchestra/api/options.py b/orchestra/api/options.py new file mode 100644 index 00000000..30c71a21 --- /dev/null +++ b/orchestra/api/options.py @@ -0,0 +1,139 @@ +from django import conf +from django.core.exceptions import ImproperlyConfigured +from rest_framework import views +from rest_framework.response import Response +from rest_framework.reverse import reverse +from rest_framework.routers import DefaultRouter, Route, flatten, replace_methodname + +from .. import settings +from ..utils.apps import autodiscover as module_autodiscover +from .helpers import insert_links, replace_collectionmethodname + + +def collectionlink(**kwargs): + """ + Used to mark a method on a ViewSet collection that should be routed for GET requests. + """ + def decorator(func): + func.collection_bind_to_methods = ['get'] + func.kwargs = kwargs + return func + return decorator + + +class LinkHeaderRouter(DefaultRouter): + def __init__(self, *args, **kwargs): + """ collection view method route """ + super(LinkHeaderRouter, self).__init__(*args, **kwargs) + self.routes.insert(0, Route( + url=r'^{prefix}/{collectionmethodname}{trailing_slash}$', + mapping={ + '{httpmethod}': '{collectionmethodname}', + }, + name='{basename}-{methodnamehyphen}', + initkwargs={} + )) + + def get_routes(self, viewset): + """ allow links and actions to be bound to a collection view """ + known_actions = flatten([route.mapping.values() for route in self.routes]) + dynamic_routes = [] + collection_dynamic_routes = [] + for methodname in dir(viewset): + attr = getattr(viewset, methodname) + bind = getattr(attr, 'bind_to_methods', None) + httpmethods = getattr(attr, 'collection_bind_to_methods', bind) + if httpmethods: + if methodname in known_actions: + msg = ('Cannot use @action or @link decorator on method "%s" ' + 'as it is an existing route' % methodname) + raise ImproperlyConfigured(msg) + httpmethods = [method.lower() for method in httpmethods] + if bind: + dynamic_routes.append((httpmethods, methodname)) + else: + collection_dynamic_routes.append((httpmethods, methodname)) + + ret = [] + for route in self.routes: + # Dynamic routes (@link or @action decorator) + if route.mapping == {'{httpmethod}': '{methodname}'}: + replace = replace_methodname + routes = dynamic_routes + elif route.mapping == {'{httpmethod}': '{collectionmethodname}'}: + replace = replace_collectionmethodname + routes = collection_dynamic_routes + else: + ret.append(route) + continue + for httpmethods, methodname in routes: + initkwargs = route.initkwargs.copy() + initkwargs.update(getattr(viewset, methodname).kwargs) + ret.append(Route( + url=replace(route.url, methodname), + mapping={ httpmethod: methodname for httpmethod in httpmethods }, + name=replace(route.name, methodname), + initkwargs=initkwargs, + )) + return ret + + def get_api_root_view(self): + """ returns the root view, with all the linked collections """ + class APIRoot(views.APIView): + def get(instance, request, format=None): + root_url = reverse('api-root', request=request, format=format) + token_url = reverse('api-token-auth', request=request, format=format) + links = [ + '<%s>; rel="%s"' % (root_url, 'api-root'), + '<%s>; rel="%s"' % (token_url, 'api-get-auth-token'), + ] + if not request.user.is_anonymous(): + list_name = '{basename}-list' + detail_name = '{basename}-detail' + for prefix, viewset, basename in self.registry: + singleton_pk = getattr(viewset, 'singleton_pk', False) + if singleton_pk: + url_name = detail_name.format(basename=basename) + kwargs = { 'pk': singleton_pk(viewset(), request) } + else: + url_name = list_name.format(basename=basename) + kwargs = {} + url = reverse(url_name, request=request, format=format, kwargs=kwargs) + links.append('<%s>; rel="%s"' % (url, url_name)) + # Add user link + url_name = detail_name.format(basename='user') + kwargs = { 'pk': request.user.pk } + url = reverse(url_name, request=request, format=format, kwargs=kwargs) + links.append('<%s>; rel="%s"' % (url, url_name)) + headers = { 'Link': ', '.join(links) } + content = { + name: getattr(settings, name, None) + for name in ['SITE_NAME', 'SITE_VERBOSE_NAME'] + } + content['INSTALLED_APPS'] = conf.settings.INSTALLED_APPS + return Response(content, headers=headers) + return APIRoot.as_view() + + def register(self, prefix, viewset, base_name=None): + """ inserts link headers on every viewset """ + if base_name is None: + base_name = self.get_default_base_name(viewset) + insert_links(viewset, base_name) + self.registry.append((prefix, viewset, base_name)) + + def insert(self, prefix, name, field, **kwargs): + """ Dynamically add new fields to an existing serializer """ + for _prefix, viewset, basename in self.registry: + if _prefix == prefix: + if viewset.serializer_class is None: + viewset.serializer_class = viewset().get_serializer_class() + viewset.serializer_class.Meta.fields += (name,) + viewset.serializer_class.base_fields.update({name: field(**kwargs)}) + setattr(viewset, 'inserted', getattr(viewset, 'inserted', [])) + viewset.inserted.append(name) + + +# Create a router and register our viewsets with it. +router = LinkHeaderRouter() + +autodiscover = lambda: (module_autodiscover('api'), module_autodiscover('serializers')) diff --git a/orchestra/api/serializers.py b/orchestra/api/serializers.py new file mode 100644 index 00000000..817431d5 --- /dev/null +++ b/orchestra/api/serializers.py @@ -0,0 +1,33 @@ +from django.forms import widgets +from django.utils.translation import ugettext, ugettext_lazy as _ +from rest_framework import serializers + +from ..core.validators import validate_password + + +class SetPasswordSerializer(serializers.Serializer): + password = serializers.CharField(max_length=128, label=_('Password'), + widget=widgets.PasswordInput, validators=[validate_password]) + + +class MultiSelectField(serializers.ChoiceField): + widget = widgets.CheckboxSelectMultiple + + def field_from_native(self, data, files, field_name, into): + """ convert multiselect data into comma separated string """ + if field_name in data: + data = data.copy() + try: + # data is a querydict when using forms + data[field_name] = ','.join(data.getlist(field_name)) + except AttributeError: + data[field_name] = ','.join(data[field_name]) + return super(MultiSelectField, self).field_from_native(data, files, field_name, into) + + def valid_value(self, value): + """ checks for each item if is a valid value """ + for val in value.split(','): + valid = super(MultiSelectField, self).valid_value(val) + if not valid: + return False + return True diff --git a/orchestra/apps/__init__.py b/orchestra/apps/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/orchestra/apps/accounts/__init__.py b/orchestra/apps/accounts/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/orchestra/apps/accounts/admin.py b/orchestra/apps/accounts/admin.py new file mode 100644 index 00000000..6076dbb7 --- /dev/null +++ b/orchestra/apps/accounts/admin.py @@ -0,0 +1,212 @@ +from django import forms +from django.conf.urls import patterns, url +from django.contrib import admin, messages +from django.contrib.admin.util import unquote +from django.core.urlresolvers import reverse +from django.http import HttpResponseRedirect +from django.utils.safestring import mark_safe +from django.utils.translation import ugettext_lazy as _ + +from orchestra.admin import ExtendedModelAdmin +from orchestra.admin.utils import wrap_admin_view, link +from orchestra.core import services + +from .filters import HasMainUserListFilter +from .forms import AccountCreationForm, AccountChangeForm +from .models import Account + + +class AccountAdmin(ExtendedModelAdmin): + list_display = ('name', 'user_link', 'type', 'is_active') + list_filter = ( + 'type', 'is_active', HasMainUserListFilter + ) + add_fieldsets = ( + (_("User"), { + 'fields': ('username', 'password1', 'password2',), + }), + (_("Account info"), { + 'fields': (('type', 'language'), 'comments'), + }), + ) + fieldsets = ( + (_("User"), { + 'fields': ('user_link', 'password',), + }), + (_("Account info"), { + 'fields': (('type', 'language'), 'comments'), + }), + ) + readonly_fields = ('user_link',) + search_fields = ('users__username',) + add_form = AccountCreationForm + form = AccountChangeForm + + user_link = link('user', order='user__username') + + def name(self, account): + return account.name + name.admin_order_field = 'user__username' + + def formfield_for_dbfield(self, db_field, **kwargs): + """ Make value input widget bigger """ + if db_field.name == 'comments': + kwargs['widget'] = forms.Textarea(attrs={'cols': 70, 'rows': 4}) + return super(AccountAdmin, self).formfield_for_dbfield(db_field, **kwargs) + + def change_view(self, request, object_id, form_url='', extra_context=None): + if request.method == 'GET': + account = self.get_object(request, unquote(object_id)) + if not account.is_active: + messages.warning(request, 'This account is disabled.') + context = { + 'services': sorted( + [ model._meta for model in services.get().keys() ], + key=lambda i: i.verbose_name_plural.lower() + ) + } + context.update(extra_context or {}) + return super(AccountAdmin, self).change_view(request, object_id, + form_url=form_url, extra_context=context) + + def save_model(self, request, obj, form, change): + """ Save user and account, they are interdependent """ + obj.user.save() + obj.user_id = obj.user.pk + obj.save() + obj.user.account = obj + obj.user.save() + + def queryset(self, request): + """ Select related for performance """ + # TODO move invoicecontact to contacts + related = ('user', 'invoicecontact') + return super(AccountAdmin, self).queryset(request).select_related(*related) + + +admin.site.register(Account, AccountAdmin) + + +class AccountListAdmin(AccountAdmin): + """ Account list to allow account selection when creating new services """ + list_display = ('select_account', 'type', 'user') + actions = None + search_fields = ['user__username',] + ordering = ('user__username',) + + def select_account(self, instance): + context = { + 'url': '../?account=' + str(instance.pk), + 'name': instance.name + } + return '%(name)s' % context + select_account.short_description = _("account") + select_account.allow_tags = True + select_account.order_admin_field = 'user__username' + + def changelist_view(self, request, extra_context=None): + opts = self.model._meta + original_app_label = request.META['PATH_INFO'].split('/')[-5] + original_model = request.META['PATH_INFO'].split('/')[-4] + context = { + 'title': _("Select account for adding a new %s") % (original_model), + 'original_app_label': original_app_label, + 'original_model': original_model, + } + context.update(extra_context or {}) + return super(AccountListAdmin, self).changelist_view(request, + extra_context=context) + + +class AccountAdminMixin(object): + """ Provide basic account support to ModelAdmin and AdminInline classes """ + readonly_fields = ('account_link',) + filter_by_account_fields = [] + + def account_link(self, instance): + account = instance.account if instance.pk else self.account + url = reverse('admin:accounts_account_change', args=(account.pk,)) + return '%s' % (url, account.name) + account_link.short_description = _("account") + account_link.allow_tags = True + account_link.admin_order_field = 'account__user__username' + + def queryset(self, request): + """ Select related for performance """ + qs = super(AccountAdminMixin, self).queryset(request) + return qs.select_related('account__user') + + def formfield_for_dbfield(self, db_field, **kwargs): + """ Improve performance of account field and filter by account """ + if db_field.name == 'account': + qs = kwargs.get('queryset', db_field.rel.to.objects) + kwargs['queryset'] = qs.select_related('user') + formfield = super(AccountAdminMixin, self).formfield_for_dbfield(db_field, **kwargs) + if db_field.name in self.filter_by_account_fields: + if hasattr(self, 'account'): + # Hack widget render in order to append ?account=id to the add url + old_render = formfield.widget.render + def render(*args, **kwargs): + output = old_render(*args, **kwargs) + output = output.replace('/add/"', '/add/?account=%s"' % self.account.pk) + return mark_safe(output) + formfield.widget.render = render + # Filter related object by account + formfield.queryset = formfield.queryset.filter(account=self.account) + return formfield + + +class SelectAccountAdminMixin(AccountAdminMixin): + """ Provides support for accounts on ModelAdmin """ + def get_readonly_fields(self, request, obj=None): + if obj: + self.account = obj.account + return super(AccountAdminMixin, self).get_readonly_fields(request, obj=obj) + + def get_inline_instances(self, request, obj=None): + inlines = super(AccountAdminMixin, self).get_inline_instances(request, obj=obj) + if hasattr(self, 'account'): + account = self.account + else: + account = Account.objects.get(pk=request.GET['account']) + [ setattr(inline, 'account', account) for inline in inlines ] + return inlines + + def get_urls(self): + """ Hooks select account url """ + urls = super(AccountAdminMixin, self).get_urls() + admin_site = self.admin_site + opts = self.model._meta + info = opts.app_label, opts.module_name + account_list = AccountListAdmin(Account, admin_site).changelist_view + select_urls = patterns("", + url("/select-account/$", + wrap_admin_view(self, account_list), + name='%s_%s_select_account' % info), + ) + return select_urls + urls + + def add_view(self, request, form_url='', extra_context=None): + """ Redirects to select account view if required """ + if request.user.is_superuser: + if 'account' in request.GET or Account.objects.count() == 1: + kwargs = {} + if 'account' in request.GET: + kwargs = dict(pk=request.GET['account']) + self.account = Account.objects.get(**kwargs) + opts = self.model._meta + context = { + 'title': _("Add %s for %s") % (opts.verbose_name, self.account.name) + } + context.update(extra_context or {}) + return super(AccountAdminMixin, self).add_view(request, + form_url=form_url, extra_context=context) + return HttpResponseRedirect('./select-account/') + + def save_model(self, request, obj, form, change): + """ + Given a model instance save it to the database. + """ + if not change: + obj.account_id = self.account.pk + obj.save() diff --git a/orchestra/apps/accounts/api.py b/orchestra/apps/accounts/api.py new file mode 100644 index 00000000..e1b363d4 --- /dev/null +++ b/orchestra/apps/accounts/api.py @@ -0,0 +1,25 @@ +from rest_framework import viewsets + +from orchestra.api import router + +from .models import Account +from .serializers import AccountSerializer + + +class AccountApiMixin(object): + def get_queryset(self): + qs = super(AccountApiMixin, self).get_queryset() + return qs.filter(account=self.request.user.account_id) + + +class AccountViewSet(viewsets.ModelViewSet): + model = Account + serializer_class = AccountSerializer + singleton_pk = lambda _,request: request.user.account.pk + + def get_queryset(self): + qs = super(AccountViewSet, self).get_queryset() + return qs.filter(id=self.request.user.account_id) + + +router.register(r'accounts', AccountViewSet) diff --git a/orchestra/apps/accounts/filters.py b/orchestra/apps/accounts/filters.py new file mode 100644 index 00000000..84a27831 --- /dev/null +++ b/orchestra/apps/accounts/filters.py @@ -0,0 +1,20 @@ +from django.contrib.admin import SimpleListFilter +from django.utils.translation import ugettext_lazy as _ + + +class HasMainUserListFilter(SimpleListFilter): + """ Filter Nodes by group according to request.user """ + title = _("has main user") + parameter_name = 'mainuser' + + def lookups(self, request, model_admin): + return ( + ('True', _("Yes")), + ('False', _("No")), + ) + + def queryset(self, request, queryset): + if self.value() == 'True': + return queryset.filter(users__isnull=False).distinct() + if self.value() == 'False': + return queryset.filter(users__isnull=True).distinct() diff --git a/orchestra/apps/accounts/forms.py b/orchestra/apps/accounts/forms.py new file mode 100644 index 00000000..bb3eabad --- /dev/null +++ b/orchestra/apps/accounts/forms.py @@ -0,0 +1,55 @@ +from django import forms +from django.contrib import auth +from django.utils.translation import ugettext_lazy as _ + +from orchestra.core.validators import validate_password +from orchestra.forms.widgets import ReadOnlyWidget + +User = auth.get_user_model() + + +class AccountCreationForm(auth.forms.UserCreationForm): + def __init__(self, *args, **kwargs): + super(AccountCreationForm, self).__init__(*args, **kwargs) + self.fields['password1'].validators.append(validate_password) + + def clean_username(self): + # Since User.username is unique, this check is redundant, + # but it sets a nicer error message than the ORM. See #13147. + username = self.cleaned_data["username"] + try: + User._default_manager.get(username=username) + except User.DoesNotExist: + return username + raise forms.ValidationError(self.error_messages['duplicate_username']) + + def save(self, commit=True): + account = super(auth.forms.UserCreationForm, self).save(commit=False) + user = User(username=self.cleaned_data['username'], is_admin=True) + user.set_password(self.cleaned_data['password1']) + user.account = account + account.user = user + if commit: + user.save() + account.save() + return account + + +class AccountChangeForm(forms.ModelForm): + username = forms.CharField() + password = auth.forms.ReadOnlyPasswordHashField(label=_("Password"), + help_text=_("Raw passwords are not stored, so there is no way to see " + "this user's password, but you can change the password " + "using this form.")) + + def __init__(self, *args, **kwargs): + super(AccountChangeForm, self).__init__(*args, **kwargs) + account = kwargs.get('instance') + self.fields['username'].widget = ReadOnlyWidget(account.user.username) + self.fields['password'].initial = account.user.password + + def clean_password(self): + # Regardless of what the user provides, return the initial value. + # This is done here, rather than on the field, because the + # field does not have access to the initial value + return self.fields['password'].initial diff --git a/orchestra/apps/accounts/management/__init__.py b/orchestra/apps/accounts/management/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/orchestra/apps/accounts/management/commands/__init__.py b/orchestra/apps/accounts/management/commands/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/orchestra/apps/accounts/management/commands/createinitialaccount.py b/orchestra/apps/accounts/management/commands/createinitialaccount.py new file mode 100644 index 00000000..135f7de8 --- /dev/null +++ b/orchestra/apps/accounts/management/commands/createinitialaccount.py @@ -0,0 +1,36 @@ +from optparse import make_option + +from django.core.management.base import BaseCommand, CommandError +from django.db import transaction + +from orchestra.apps.accounts.models import Account +from orchestra.apps.users.models import User + + +class Command(BaseCommand): + def __init__(self, *args, **kwargs): + super(Command, self).__init__(*args, **kwargs) + self.option_list = BaseCommand.option_list + ( + make_option('--noinput', action='store_false', dest='interactive', + default=True), + make_option('--username', action='store', dest='username'), + make_option('--password', action='store', dest='password'), + make_option('--email', action='store', dest='email'), + ) + + option_list = BaseCommand.option_list + help = 'Used to create an initial account and its user.' + + @transaction.atomic + def handle(self, *args, **options): + interactive = options.get('interactive') + if not interactive: + email = options.get('email') + username = options.get('username') + password = options.get('password') + user = User.objects.create_superuser(username, email, password, account=account, + is_main=True) + account = Account.objects.create(user=user) + user.account = account + user.save() + diff --git a/orchestra/apps/accounts/management/commands/createsuperuser.py b/orchestra/apps/accounts/management/commands/createsuperuser.py new file mode 100644 index 00000000..20d05244 --- /dev/null +++ b/orchestra/apps/accounts/management/commands/createsuperuser.py @@ -0,0 +1,14 @@ +from django.contrib.auth import get_user_model +from django.contrib.auth.management.commands import createsuperuser + +from orchestra.apps.accounts.models import Account + + +class Command(createsuperuser.Command): + def handle(self, *args, **options): + super(Command, self).handle(*args, **options) + users = get_user_model().objects.filter() + if len(users) == 1 and not Account.objects.all().exists(): + user = users[0] + user.account = Account.objects.create(user=user) + user.save() diff --git a/orchestra/apps/accounts/models.py b/orchestra/apps/accounts/models.py new file mode 100644 index 00000000..9fa42335 --- /dev/null +++ b/orchestra/apps/accounts/models.py @@ -0,0 +1,25 @@ +from django.db import models +from django.contrib.auth import get_user_model +from django.utils.translation import ugettext_lazy as _ + +from . import settings + + +class Account(models.Model): + user = models.OneToOneField(get_user_model(), related_name='accounts') + type = models.CharField(_("type"), max_length=32, choices=settings.ACCOUNTS_TYPES, + default=settings.ACCOUNTS_DEFAULT_TYPE) + language = models.CharField(_("language"), max_length=2, + choices=settings.ACCOUNTS_LANGUAGES, + default=settings.ACCOUNTS_DEFAULT_LANGUAGE) + register_date = models.DateTimeField(_("register date"), auto_now_add=True) + comments = models.TextField(_("comments"), max_length=256, blank=True) + is_active = models.BooleanField(default=True) + + def __unicode__(self): + return self.name + + @property + def name(self): + self._cached_name = getattr(self, '_cached_name', self.user.username) + return self._cached_name diff --git a/orchestra/apps/accounts/serializers.py b/orchestra/apps/accounts/serializers.py new file mode 100644 index 00000000..8da396ba --- /dev/null +++ b/orchestra/apps/accounts/serializers.py @@ -0,0 +1,17 @@ +from rest_framework import serializers + +from .models import Account + + +class AccountSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = Account + fields = ( + 'url', 'user', 'type', 'language', 'register_date', 'is_active' + ) + + +class AccountSerializerMixin(object): + def save_object(self, obj, **kwargs): + obj.account = self.context['request'].user.account + super(AccountSerializerMixin, self).save_object(obj, **kwargs) diff --git a/orchestra/apps/accounts/settings.py b/orchestra/apps/accounts/settings.py new file mode 100644 index 00000000..5d82ed2e --- /dev/null +++ b/orchestra/apps/accounts/settings.py @@ -0,0 +1,20 @@ +from django.conf import settings +from django.utils.translation import ugettext_lazy as _ + + +ACCOUNTS_TYPES = getattr(settings, 'ACCOUNTS_TYPES', ( + ('INDIVIDUAL', _("Individual")), + ('ASSOCIATION', _("Association")), + ('COMPANY', _("Company")), + ('PUBLICBODY', _("Public body")), +)) + +ACCOUNTS_DEFAULT_TYPE = getattr(settings, 'ACCOUNTS_DEFAULT_TYPE', 'INDIVIDUAL') + + +ACCOUNTS_LANGUAGES = getattr(settings, 'ACCOUNTS_LANGUAGES', ( + ('en', _('English')), +)) + + +ACCOUNTS_DEFAULT_LANGUAGE = getattr(settings, 'ACCOUNTS_DEFAULT_LANGUAGE', 'en') diff --git a/orchestra/apps/accounts/templates/admin/accounts/account/change_form.html b/orchestra/apps/accounts/templates/admin/accounts/account/change_form.html new file mode 100644 index 00000000..52598a41 --- /dev/null +++ b/orchestra/apps/accounts/templates/admin/accounts/account/change_form.html @@ -0,0 +1,20 @@ +{% extends "admin/change_form.html" %} +{% load i18n admin_urls admin_static admin_modify %} + + +{% block object-tools-items %} + {% for service in services %} +
  • + {{ service.verbose_name_plural|capfirst }} +
  • + {% endfor %} +
  • + {% trans "Disable" %} +
  • +
  • + {% url opts|admin_urlname:'history' original.pk|admin_urlquote as history_url %} + {% trans "History" %} +
  • +{% if has_absolute_url %}
  • {% trans "View on site" %}
  • {% endif%} +{% endblock %} + diff --git a/orchestra/apps/contacts/__init__.py b/orchestra/apps/contacts/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/orchestra/apps/contacts/admin.py b/orchestra/apps/contacts/admin.py new file mode 100644 index 00000000..d3620143 --- /dev/null +++ b/orchestra/apps/contacts/admin.py @@ -0,0 +1,67 @@ +from django import forms +from django.contrib import admin + +from orchestra.admin import AtLeastOneRequiredInlineFormSet +from orchestra.admin.utils import insertattr +from orchestra.apps.accounts.admin import AccountAdmin, AccountAdminMixin + +from .filters import HasInvoiceContactListFilter +from .models import Contact, InvoiceContact + + +class ContactAdmin(AccountAdminMixin, admin.ModelAdmin): + list_display = ( + 'short_name', 'full_name', 'email', 'phone', 'phone2', 'country', + 'account_link' + ) + list_filter = ('email_usage',) + search_fields = ( + 'contact__user__username', 'short_name', 'full_name', 'phone', 'phone2', + 'email' + ) + + +admin.site.register(Contact, ContactAdmin) + + +class InvoiceContactInline(admin.StackedInline): + model = InvoiceContact + fields = ('name', 'address', ('city', 'zipcode'), 'country', 'vat') + + def formfield_for_dbfield(self, db_field, **kwargs): + """ Make value input widget bigger """ + if db_field.name == 'address': + kwargs['widget'] = forms.Textarea(attrs={'cols': 70, 'rows': 2}) + return super(InvoiceContactInline, self).formfield_for_dbfield(db_field, **kwargs) + + +class ContactInline(InvoiceContactInline): + model = Contact + formset = AtLeastOneRequiredInlineFormSet + extra = 0 + fields = ( + 'short_name', 'full_name', 'email', 'email_usage', ('phone', 'phone2'), + 'address', ('city', 'zipcode'), 'country', + ) + + def get_extra(self, request, obj=None, **kwargs): + return 0 if obj and obj.contacts.exists() else 1 + + +def has_invoice(account): + try: + account.invoicecontact.get() + except InvoiceContact.DoesNotExist: + return False + return True +has_invoice.boolean = True +has_invoice.admin_order_field = 'invoicecontact' + + +insertattr(AccountAdmin, 'inlines', ContactInline) +insertattr(AccountAdmin, 'inlines', InvoiceContactInline) +insertattr(AccountAdmin, 'list_display', has_invoice) +insertattr(AccountAdmin, 'list_filter', HasInvoiceContactListFilter) +for field in ('contacts__short_name', 'contacts__full_name', 'contacts__phone', + 'contacts__phone2', 'contacts__email'): + insertattr(AccountAdmin, 'search_fields', field) diff --git a/orchestra/apps/contacts/api.py b/orchestra/apps/contacts/api.py new file mode 100644 index 00000000..29332da6 --- /dev/null +++ b/orchestra/apps/contacts/api.py @@ -0,0 +1,21 @@ +from rest_framework import viewsets + +from orchestra.api import router +from orchestra.apps.accounts.api import AccountApiMixin + +from .models import Contact, InvoiceContact +from .serializers import ContactSerializer, InvoiceContactSerializer + + +class ContactViewSet(AccountApiMixin, viewsets.ModelViewSet): + model = Contact + serializer_class = ContactSerializer + + +class InvoiceContactViewSet(AccountApiMixin, viewsets.ModelViewSet): + model = InvoiceContact + serializer_class = InvoiceContactSerializer + + +router.register(r'contacts', ContactViewSet) +router.register(r'invoicecontacts', InvoiceContactViewSet) diff --git a/orchestra/apps/contacts/filters.py b/orchestra/apps/contacts/filters.py new file mode 100644 index 00000000..eade8b81 --- /dev/null +++ b/orchestra/apps/contacts/filters.py @@ -0,0 +1,20 @@ +from django.contrib.admin import SimpleListFilter +from django.utils.translation import ugettext_lazy as _ + + +class HasInvoiceContactListFilter(SimpleListFilter): + """ Filter Nodes by group according to request.user """ + title = _("has invoice contact") + parameter_name = 'invoice' + + def lookups(self, request, model_admin): + return ( + ('True', _("Yes")), + ('False', _("No")), + ) + + def queryset(self, request, queryset): + if self.value() == 'True': + return queryset.filter(invoicecontact__isnull=False) + if self.value() == 'False': + return queryset.filter(invoicecontact__isnull=True) diff --git a/orchestra/apps/contacts/models.py b/orchestra/apps/contacts/models.py new file mode 100644 index 00000000..517165ad --- /dev/null +++ b/orchestra/apps/contacts/models.py @@ -0,0 +1,41 @@ +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from orchestra.models.fields import MultiSelectField + +from . import settings + + +class Contact(models.Model): + account = models.ForeignKey('accounts.Account', verbose_name=_("Account"), + related_name='contacts', null=True) + short_name = models.CharField(_("short name"), max_length=128) + full_name = models.CharField(_("full name"), max_length=256, blank=True) + email = models.EmailField() + email_usage = MultiSelectField(_("email usage"), max_length=256, blank=True, + choices=settings.CONTACTS_EMAIL_USAGES, + default=settings.CONTACTS_DEFAULT_EMAIL_USAGES) + phone = models.CharField(_("Phone"), max_length=32, blank=True) + phone2 = models.CharField(_("Alternative Phone"), max_length=32, blank=True) + address = models.TextField(_("address"), blank=True) + city = models.CharField(_("city"), max_length=128, blank=True, + default=settings.CONTACTS_DEFAULT_CITY) + zipcode = models.PositiveIntegerField(_("zip code"), null=True, blank=True) + country = models.CharField(_("country"), max_length=20, blank=True, + default=settings.CONTACTS_DEFAULT_COUNTRY) + + def __unicode__(self): + return self.short_name + + +class InvoiceContact(models.Model): + account = models.OneToOneField('accounts.Account', verbose_name=_("account"), + related_name='invoicecontact') + name = models.CharField(_("name"), max_length=256) + address = models.TextField(_("address")) + city = models.CharField(_("city"), max_length=128, + default=settings.CONTACTS_DEFAULT_CITY) + zipcode = models.PositiveIntegerField(_("zip code")) + country = models.CharField(_("country"), max_length=20, + default=settings.CONTACTS_DEFAULT_COUNTRY) + vat = models.CharField(_("VAT number"), max_length=64) diff --git a/orchestra/apps/contacts/serializers.py b/orchestra/apps/contacts/serializers.py new file mode 100644 index 00000000..f0449823 --- /dev/null +++ b/orchestra/apps/contacts/serializers.py @@ -0,0 +1,23 @@ +from rest_framework import serializers + +from orchestra.api.serializers import MultiSelectField +from orchestra.apps.accounts.serializers import AccountSerializerMixin + +from . import settings +from .models import Contact, InvoiceContact + + +class ContactSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer): + email_usage = MultiSelectField(choices=settings.CONTACTS_EMAIL_USAGES) + class Meta: + model = Contact + fields = ( + 'url', 'short_name', 'full_name', 'email', 'email_usage', 'phone', + 'phone2', 'address', 'city', 'zipcode', 'country' + ) + + +class InvoiceContactSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer): + class Meta: + model = InvoiceContact + fields = ('url', 'name', 'address', 'city', 'zipcode', 'country', 'vat') diff --git a/orchestra/apps/contacts/settings.py b/orchestra/apps/contacts/settings.py new file mode 100644 index 00000000..653d9b36 --- /dev/null +++ b/orchestra/apps/contacts/settings.py @@ -0,0 +1,24 @@ +from django.conf import settings +from django.utils.translation import ugettext_lazy as _ + + +CONTACTS_EMAIL_USAGES = getattr(settings, 'CONTACTS_EMAIL_USAGES', ( + ('SUPPORT', _("Support tickets")), + ('ADMIN', _("Administrative")), + ('BILL', _("Billing")), + ('TECH', _("Technical")), + ('ADDS', _("Announcements")), + ('EMERGENCY', _("Emergency contact")), +)) + + +CONTACTS_DEFAULT_EMAIL_USAGES = getattr(settings, 'CONTACTS_DEFAULT_EMAIL_USAGES', + ('SUPPORT', 'ADMIN', 'BILL', 'TECH', 'ADDS', 'EMERGENCY') +) + + +CONTACTS_DEFAULT_CITY = getattr(settings, 'CONTACTS_DEFAULT_CITY', 'Barcelona') + +CONTACTS_DEFAULT_PROVINCE = getattr(settings, 'CONTACTS_DEFAULT_PROVINCE', 'Barcelona') + +CONTACTS_DEFAULT_COUNTRY = getattr(settings, 'CONTACTS_DEFAULT_COUNTRY', 'Spain') diff --git a/orchestra/apps/databases/__init__.py b/orchestra/apps/databases/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/orchestra/apps/databases/admin.py b/orchestra/apps/databases/admin.py new file mode 100644 index 00000000..b3771b59 --- /dev/null +++ b/orchestra/apps/databases/admin.py @@ -0,0 +1,135 @@ +from django.db import models +from django.conf.urls import patterns +from django.contrib import admin +from django.contrib.auth.admin import UserAdmin +from django.core.urlresolvers import reverse +from django.utils.safestring import mark_safe +from django.utils.translation import ugettext_lazy as _ + +from orchestra.admin import ExtendedModelAdmin +from orchestra.admin.utils import link +from orchestra.apps.accounts.admin import AccountAdminMixin, SelectAccountAdminMixin + +from .forms import (DatabaseUserChangeForm, DatabaseUserCreationForm, + DatabaseCreationForm) +from .models import Database, Role, DatabaseUser + + +class UserInline(admin.TabularInline): + model = Role + verbose_name_plural = _("Users") + readonly_fields = ('user_link',) + extra = 0 + + user_link = link('user') + + def formfield_for_dbfield(self, db_field, **kwargs): + """ Make value input widget bigger """ + if db_field.name == 'user': + users = db_field.rel.to.objects.filter(type=self.parent_object.type) + kwargs['queryset'] = users.filter(account=self.account) + return super(UserInline, self).formfield_for_dbfield(db_field, **kwargs) + + +class PermissionInline(AccountAdminMixin, admin.TabularInline): + model = Role + verbose_name_plural = _("Permissions") + readonly_fields = ('database_link',) + extra = 0 + filter_by_account_fields = ['database'] + + database_link = link('database', popup=True) + + def formfield_for_dbfield(self, db_field, **kwargs): + """ Make value input widget bigger """ + formfield = super(PermissionInline, self).formfield_for_dbfield(db_field, **kwargs) + if db_field.name == 'database': + # Hack widget render in order to append ?account=id to the add url + db_type = self.parent_object.type + old_render = formfield.widget.render + def render(*args, **kwargs): + output = old_render(*args, **kwargs) + output = output.replace('/add/?', '/add/?type=%s&' % db_type) + return mark_safe(output) + formfield.widget.render = render + formfield.queryset = formfield.queryset.filter(type=db_type) + return formfield + + +class DatabaseAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): + list_display = ('name', 'type', 'account_link') + list_filter = ('type',) + search_fields = ['name', 'account__user__username'] + inlines = [UserInline] + add_inlines = [] + change_readonly_fields = ('name', 'type') + extra = 1 + fieldsets = ( + (None, { + 'classes': ('extrapretty',), + 'fields': ('account_link', 'name', 'type'), + }), + ) + add_fieldsets = ( + (None, { + 'classes': ('wide',), + 'fields': ('account_link', 'name', 'type') + }), + (_("Create new user"), { + 'classes': ('wide',), + 'fields': ('username', 'password1', 'password2'), + }), + (_("Use existing user"), { + 'classes': ('wide',), + 'fields': ('user',) + }), + ) + add_form = DatabaseCreationForm + + def save_model(self, request, obj, form, change): + super(DatabaseAdmin, self).save_model(request, obj, form, change) + if not change: + user = form.cleaned_data['user'] + if not user: + user = DatabaseUser.objects.create( + username=form.cleaned_data['username'], + type=obj.type, + account_id = obj.account.pk, + ) + user.set_password(form.cleaned_data["password1"]) + user.save() + Role.objects.create(database=obj, user=user, is_owner=True) + + +class DatabaseUserAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): + list_display = ('username', 'type', 'account_link') + list_filter = ('type',) + search_fields = ['username', 'account__user__username'] + form = DatabaseUserChangeForm + add_form = DatabaseUserCreationForm + change_readonly_fields = ('username', 'type') + inlines = [PermissionInline] + add_inlines = [] + fieldsets = ( + (None, { + 'classes': ('extrapretty',), + 'fields': ('account_link', 'username', 'password', 'type') + }), + ) + add_fieldsets = ( + (None, { + 'classes': ('extrapretty',), + 'fields': ('account_link', 'username', 'password1', 'password2', 'type') + }), + ) + + def get_urls(self): + useradmin = UserAdmin(DatabaseUser, self.admin_site) + return patterns('', + (r'^(\d+)/password/$', + self.admin_site.admin_view(useradmin.user_change_password)) + ) + super(DatabaseUserAdmin, self).get_urls() + + +admin.site.register(Database, DatabaseAdmin) +admin.site.register(DatabaseUser, DatabaseUserAdmin) diff --git a/orchestra/apps/databases/api.py b/orchestra/apps/databases/api.py new file mode 100644 index 00000000..2cec89c4 --- /dev/null +++ b/orchestra/apps/databases/api.py @@ -0,0 +1,26 @@ +from rest_framework import viewsets +from rest_framework import status +from rest_framework.decorators import action +from rest_framework.response import Response + +from orchestra.api import router, SetPasswordApiMixin +from orchestra.apps.accounts.api import AccountApiMixin + +from .models import Database, DatabaseUser +from .serializers import DatabaseSerializer, DatabaseUserSerializer + + +class DatabaseViewSet(AccountApiMixin, viewsets.ModelViewSet): + model = Database + serializer_class = DatabaseSerializer + filter_fields = ('name',) + + +class DatabaseUserViewSet(AccountApiMixin, SetPasswordApiMixin, viewsets.ModelViewSet): + model = DatabaseUser + serializer_class = DatabaseUserSerializer + filter_fields = ('username',) + + +router.register(r'databases', DatabaseViewSet) +router.register(r'databaseusers', DatabaseUserViewSet) diff --git a/orchestra/apps/databases/backends.py b/orchestra/apps/databases/backends.py new file mode 100644 index 00000000..90598afc --- /dev/null +++ b/orchestra/apps/databases/backends.py @@ -0,0 +1,60 @@ +from orchestra.apps.orchestration import ServiceBackend + +from . import settings + + +class MySQLDBBackend(ServiceBackend): + verbose_name = "MySQL database" + model = 'databases.Database' + + def save(self, database): + if database.type == database.MYSQL: + context = self.get_context(database) + self.append("mysql -e 'CREATE DATABASE `%(database)s`;'" % context) + self.append("mysql -e 'GRANT ALL PRIVILEGES ON `%(database)s`.* " + " TO \"%(owner)s\"@\"%(host)s\" WITH GRANT OPTION;'" % context) + + def delete(self, database): + if database.type == database.MYSQL: + context = self.get_context(database) + self.append("mysql -e 'DROP DATABASE `%(database)s`;'" % context) + + def commit(self): + self.append("mysql -e 'FLUSH PRIVILEGES;'") + + def get_context(self, database): + return { + 'owner': database.owner.username, + 'database': database.name, + 'host': settings.DATABASES_DEFAULT_HOST, + } + + +class MySQLUserBackend(ServiceBackend): + verbose_name = "MySQL user" + model = 'databases.DatabaseUser' + + def save(self, database): + if database.type == database.MYSQL: + context = self.get_context(database) + self.append("mysql -e 'CREATE USER \"%(username)s\"@\"%(host)s\";'" % context) + self.append("mysql -e 'UPDATE mysql.user SET Password=\"%(password)s\" " + " WHERE User=\"%(username)s\";'" % context) + + def delete(self, database): + if database.type == database.MYSQL: + context = self.get_context(database) + self.append("mysql -e 'DROP USER \"%(username)s\"@\"%(host)s\";'" % context) + + def get_context(self, database): + return { + 'username': database.username, + 'password': database.password, + 'host': settings.DATABASES_DEFAULT_HOST, + } + + +class MySQLPermissionBackend(ServiceBackend): + model = 'databases.UserDatabaseRelation' + verbose_name = "MySQL permission" + diff --git a/orchestra/apps/databases/forms.py b/orchestra/apps/databases/forms.py new file mode 100644 index 00000000..1688da05 --- /dev/null +++ b/orchestra/apps/databases/forms.py @@ -0,0 +1,135 @@ +from django import forms +from django.contrib.auth.forms import UserCreationForm, ReadOnlyPasswordHashField +from django.utils.html import format_html +from django.utils.safestring import mark_safe +from django.utils.translation import ugettext_lazy as _ + +from orchestra.core.validators import validate_password + +from .models import DatabaseUser, Database, Role + + +class DatabaseUserCreationForm(forms.ModelForm): + password1 = forms.CharField(label=_("Password"), required=False, + widget=forms.PasswordInput, validators=[validate_password]) + password2 = forms.CharField(label=_("Password confirmation"), required=False, + widget=forms.PasswordInput, + help_text=_("Enter the same password as above, for verification.")) + + class Meta: + model = DatabaseUser + fields = ('username', 'account', 'type') + + def clean_password2(self): + password1 = self.cleaned_data.get("password1") + password2 = self.cleaned_data.get("password2") + if password1 and password2 and password1 != password2: + msg = _("The two password fields didn't match.") + raise forms.ValidationError(msg) + return password2 + + def save(self, commit=True): + user = super(DatabaseUserCreationForm, self).save(commit=False) + user.set_password(self.cleaned_data["password1"]) + if commit: + user.save() + return user + + +class DatabaseCreationForm(DatabaseUserCreationForm): + username = forms.RegexField(label=_("Username"), max_length=30, + required=False, regex=r'^[\w.@+-]+$', + help_text=_("Required. 30 characters or fewer. Letters, digits and " + "@/./+/-/_ only."), + error_messages={ + 'invalid': _("This value may contain only letters, numbers and " + "@/./+/-/_ characters.")}) + user = forms.ModelChoiceField(required=False, queryset=DatabaseUser.objects) + + class Meta: + model = Database + fields = ('username', 'account', 'type') + + def __init__(self, *args, **kwargs): + super(DatabaseCreationForm, self).__init__(*args, **kwargs) + account_id = self.initial.get('account', None) + if account_id: + qs = self.fields['user'].queryset.filter(account=account_id) + choices = [ (u.pk, "%s (%s)" % (u, u.get_type_display())) for u in qs ] + self.fields['user'].queryset = qs + self.fields['user'].choices = [(None, '--------'),] + choices + + def clean_password2(self): + username = self.cleaned_data.get('username') + password1 = self.cleaned_data.get('password1') + password2 = self.cleaned_data.get('password2') + if username and not (password1 and password2): + raise forms.ValidationError(_("Missing password")) + if password1 and password2 and password1 != password2: + msg = _("The two password fields didn't match.") + raise forms.ValidationError(msg) + return password2 + + def clean_user(self): + user = self.cleaned_data.get('user') + if user and user.type != self.cleaned_data.get('type'): + msg = _("Database type and user type doesn't match") + raise forms.ValidationError(msg) + return user + + def clean(self): + cleaned_data = super(DatabaseCreationForm, self).clean() + if 'user' in cleaned_data and 'username' in cleaned_data: + msg = _("Use existing user or create a new one?") + if cleaned_data['user'] and self.cleaned_data['username']: + raise forms.ValidationError(msg) + elif not (cleaned_data['username'] or cleaned_data['user']): + raise forms.ValidationError(msg) + return cleaned_data + + def save(self, commit=True): + db = super(DatabaseUserCreationForm, self).save(commit=False) + user = self.cleaned_data['user'] + if commit: + if not user: + user = DatabaseUser( + username=self.cleaned_data['username'], + type=self.cleaned_data['type'], + ) + user.set_password(self.cleaned_data["password1"]) + user.save() + role, __ = Role.objects.get_or_create(database=db, user=user) + return db + + +class ReadOnlySQLPasswordHashField(ReadOnlyPasswordHashField): + class ReadOnlyPasswordHashWidget(forms.Widget): + def render(self, name, value, attrs): + original = ReadOnlyPasswordHashField.widget().render(name, value, attrs) + if 'Invalid' not in original: + return original + encoded = value + final_attrs = self.build_attrs(attrs) + if not encoded: + summary = mark_safe("%s" % _("No password set.")) + else: + size = len(value) + summary = value[:size/2] + '*'*(size-size/2) + summary = "hash: %s" % summary + if value.startswith('*'): + summary = "algorithm: sha1_bin_hex %s" % summary + return format_html("
    %s
    " % summary) + widget = ReadOnlyPasswordHashWidget + + +class DatabaseUserChangeForm(forms.ModelForm): + password = ReadOnlySQLPasswordHashField(label=_("Password"), + help_text=_("Raw passwords are not stored, so there is no way to see " + "this user's password, but you can change the password " + "using this form.")) + + class Meta: + model = DatabaseUser + + def clean_password(self): + return self.initial["password"] diff --git a/orchestra/apps/databases/models.py b/orchestra/apps/databases/models.py new file mode 100644 index 00000000..bf9ba555 --- /dev/null +++ b/orchestra/apps/databases/models.py @@ -0,0 +1,91 @@ +import hashlib + +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from orchestra.core import validators, services + +from . import settings + + +class Database(models.Model): + """ Represents a basic database for a web application """ + MYSQL = 'mysql' + POSTGRESQL = 'postgresql' + + name = models.CharField(_("name"), max_length=128, + validators=[validators.validate_name]) + users = models.ManyToManyField('databases.DatabaseUser', verbose_name=_("users"), + through='databases.Role', related_name='users') + type = models.CharField(_("type"), max_length=32, + choices=settings.DATABASES_TYPE_CHOICES, + default=settings.DATABASES_DEFAULT_TYPE) + account = models.ForeignKey('accounts.Account', verbose_name=_("Account"), + related_name='databases') + + class Meta: + unique_together = ('name', 'type') + + def __unicode__(self): + return "%s" % self.name + + @property + def owner(self): + self.users.get(is_owner=True) + + +class Role(models.Model): + database = models.ForeignKey(Database, verbose_name=_("database"), + related_name='roles') + user = models.ForeignKey('databases.DatabaseUser', verbose_name=_("user"), + related_name='roles') + is_owner = models.BooleanField(_("is owener"), default=False) + + class Meta: + unique_together = ('database', 'user') + + def __unicode__(self): + return "%s@%s" % (self.user, self.database) + + def clean(self): + if self.user.type != self.database.type: + msg = _("Database and user type doesn't match") + raise validators.ValidationError(msg) + + +class DatabaseUser(models.Model): + MYSQL = 'mysql' + POSTGRESQL = 'postgresql' + + username = models.CharField(_("username"), max_length=128, + validators=[validators.validate_name]) + password = models.CharField(_("password"), max_length=128) + type = models.CharField(_("type"), max_length=32, + choices=settings.DATABASES_TYPE_CHOICES, + default=settings.DATABASES_DEFAULT_TYPE) + account = models.ForeignKey('accounts.Account', verbose_name=_("Account"), + related_name='databaseusers') + + class Meta: + verbose_name_plural = _("DB users") + unique_together = ('username', 'type') + + def __unicode__(self): + return self.username + + def get_username(self): + return self.username + + def set_password(self, password): + if self.type == self.MYSQL: + # MySQL stores sha1(sha1(password).binary).hex + binary = hashlib.sha1(password).digest() + hexdigest = hashlib.sha1(binary).hexdigest() + password = '*%s' % hexdigest.upper() + self.password = password + else: + raise TypeError("Database type '%s' not supported" % self.type) + + +services.register(Database) +services.register(DatabaseUser, verbose_name_plural=_("Database users")) diff --git a/orchestra/apps/databases/serializers.py b/orchestra/apps/databases/serializers.py new file mode 100644 index 00000000..5dbad818 --- /dev/null +++ b/orchestra/apps/databases/serializers.py @@ -0,0 +1,40 @@ +from django.forms import widgets +from django.utils.translation import ugettext, ugettext_lazy as _ +from rest_framework import serializers + +from orchestra.apps.accounts.serializers import AccountSerializerMixin +from orchestra.core.validators import validate_password + +from .models import Database, DatabaseUser, Role + + +class UserSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = Role + fields = ('user', 'is_owner',) + + +class PermissionSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = Role + fields = ('database', 'is_owner',) + + +class DatabaseSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer): + users = UserSerializer(source='roles', many=True) + + class Meta: + model = Database + fields = ('url', 'name', 'type', 'users') + + +class DatabaseUserSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer): + password = serializers.CharField(max_length=128, label=_('Password'), + validators=[validate_password], write_only=True, + widget=widgets.PasswordInput) + permission = PermissionSerializer(source='roles', many=True) + + class Meta: + model = DatabaseUser + fields = ('url', 'username', 'password', 'type', 'permission') + write_only_fields = ('username',) diff --git a/orchestra/apps/databases/settings.py b/orchestra/apps/databases/settings.py new file mode 100644 index 00000000..9fa3d7f4 --- /dev/null +++ b/orchestra/apps/databases/settings.py @@ -0,0 +1,14 @@ +from django.conf import settings + + + +DATABASES_TYPE_CHOICES = getattr(settings, 'DATABASES_TYPE_CHOICES', ( + ('mysql', 'MySQL'), + ('postgres', 'PostgreSQL'), +)) + + +DATABASES_DEFAULT_TYPE = getattr(settings, 'DATABASES_DEFAULT_TYPE', 'mysql') + + +DATABASES_DEFAULT_HOST = getattr(settings, 'DATABASES_DEFAULT_HOST', 'localhost') diff --git a/orchestra/apps/domains/__init__.py b/orchestra/apps/domains/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/orchestra/apps/domains/admin.py b/orchestra/apps/domains/admin.py new file mode 100644 index 00000000..0bbc4e20 --- /dev/null +++ b/orchestra/apps/domains/admin.py @@ -0,0 +1,125 @@ +from django import forms +from django.conf.urls import patterns, url +from django.contrib import admin +from django.contrib.admin.util import unquote +from django.core.urlresolvers import reverse +from django.db.models import F +from django.template.response import TemplateResponse +from django.utils.translation import ugettext_lazy as _ + +from orchestra.admin import ChangeListDefaultFilter, ExtendedModelAdmin +from orchestra.admin.utils import wrap_admin_view, link +from orchestra.apps.accounts.admin import AccountAdminMixin +from orchestra.utils import apps + +from .forms import RecordInlineFormSet, DomainAdminForm +from .filters import TopDomainListFilter +from .models import Domain, Record + + +class RecordInline(admin.TabularInline): + model = Record + formset = RecordInlineFormSet + verbose_name_plural = _("Extra records") + + class Media: + css = { + 'all': ('orchestra/css/hide-inline-id.css',) + } + + def formfield_for_dbfield(self, db_field, **kwargs): + """ Make value input widget bigger """ + if db_field.name == 'value': + kwargs['widget'] = forms.TextInput(attrs={'size':'100'}) + return super(RecordInline, self).formfield_for_dbfield(db_field, **kwargs) + + +class DomainInline(admin.TabularInline): + model = Domain + fields = ('domain_link',) + readonly_fields = ('domain_link',) + extra = 0 + verbose_name_plural = _("Subdomains") + + domain_link = link() + domain_link.short_description = _("Name") + + def has_add_permission(self, *args, **kwargs): + return False + + +class DomainAdmin(ChangeListDefaultFilter, AccountAdminMixin, ExtendedModelAdmin): + fields = ('name', 'account') + list_display = ('structured_name', 'is_top', 'websites', 'account_link') + inlines = [RecordInline, DomainInline] + list_filter = [TopDomainListFilter] + change_readonly_fields = ('name',) + search_fields = ['name', 'account__user__username'] + default_changelist_filters = (('top_domain', 'True'),) + form = DomainAdminForm + + def structured_name(self, domain): + if not self.is_top(domain): + return ' '*4 + domain.name + return domain.name + structured_name.short_description = _("name") + structured_name.allow_tags = True + structured_name.admin_order_field = 'structured_name' + + def is_top(self, domain): + return not bool(domain.top) + is_top.boolean = True + is_top.admin_order_field = 'top' + + def websites(self, domain): + if apps.isinstalled('orchestra.apps.websites'): + webs = domain.websites.all() + if webs: + links = [] + for web in webs: + url = reverse('admin:websites_website_change', args=(web.pk,)) + links.append('%s' % (url, web.name)) + return '
    '.join(links) + return _("No website") + websites.admin_order_field = 'websites__name' + websites.short_description = _("Websites") + websites.allow_tags = True + + def get_urls(self): + """ Returns the additional urls for the change view links """ + urls = super(DomainAdmin, self).get_urls() + admin_site = self.admin_site + opts = self.model._meta + urls = patterns("", + url('^(\d+)/view-zone/$', + wrap_admin_view(self, self.view_zone_view), + name='domains_domain_view_zone') + ) + urls + return urls + + def view_zone_view(self, request, object_id): + zone = self.get_object(request, unquote(object_id)) + context = { + 'opts': self.model._meta, + 'object': zone, + 'title': _("%s zone content") % zone.origin.name + } + return TemplateResponse(request, 'admin/domains/domain/view_zone.html', + context) + + def queryset(self, request): + """ Order by structured name and imporve performance """ + qs = super(DomainAdmin, self).queryset(request) + qs = qs.select_related('top', 'account__user') +# qs = qs.select_related('top') + # For some reason if we do this we know for sure that join table will be called T4 + __ = str(qs.query) + qs = qs.extra( + select={'structured_name': 'CONCAT(T4.name, domains_domain.name)'}, + ).order_by('structured_name') + if apps.isinstalled('orchestra.apps.websites'): + qs = qs.prefetch_related('websites') + return qs + + +admin.site.register(Domain, DomainAdmin) diff --git a/orchestra/apps/domains/api.py b/orchestra/apps/domains/api.py new file mode 100644 index 00000000..989a77bc --- /dev/null +++ b/orchestra/apps/domains/api.py @@ -0,0 +1,35 @@ +from rest_framework import viewsets +from rest_framework.decorators import link +from rest_framework.response import Response + +from orchestra.api import router, collectionlink +from orchestra.apps.accounts.api import AccountApiMixin + +from . import settings +from .models import Domain +from .serializers import DomainSerializer + + +class DomainViewSet(AccountApiMixin, viewsets.ModelViewSet): + model = Domain + serializer_class = DomainSerializer + filter_fields = ('name',) + + def get_queryset(self): + qs = super(DomainViewSet, self).get_queryset() + return qs.prefetch_related('records') + + @collectionlink() + def configuration(self, request): + names = ['DOMAINS_DEFAULT_A', 'DOMAINS_DEFAULT_MX', 'DOMAINS_DEFAULT_NS'] + return Response({ + name: getattr(settings, name, None) for name in names + }) + + @link() + def view_zone(self, request, pk=None): + domain = self.get_object() + return Response({'zone': domain.render_zone()}) + + +router.register(r'domains', DomainViewSet) diff --git a/orchestra/apps/domains/backends.py b/orchestra/apps/domains/backends.py new file mode 100644 index 00000000..aea7206b --- /dev/null +++ b/orchestra/apps/domains/backends.py @@ -0,0 +1,104 @@ +import os + +from django.utils.translation import ugettext_lazy as _ + +from . import settings + +from orchestra.apps.orchestration import ServiceBackend + + +class Bind9MasterDomainBackend(ServiceBackend): + verbose_name = _("Bind9 master domain") + model = 'domains.Domain' + related_models = ( + ('domains.Record', 'domain__origin'), + ('domains.Domain', 'origin'), + ) + + @classmethod + def is_main(cls, obj): + """ work around Domain.top self relationship """ + if super(Bind9MasterDomainBackend, cls).is_main(obj): + return not obj.top + + def save(self, domain): + context = self.get_context(domain) + domain.refresh_serial() + context['zone'] = ';; %(banner)s\n' % context + context['zone'] += domain.render_zone() + self.append("{ echo -e '%(zone)s' | diff -N -I'^;;' %(zone_path)s - ; } ||" + " { echo -e '%(zone)s' > %(zone_path)s; UPDATED=1; }" % context) + self.update_conf(context) + + def update_conf(self, context): + self.append("grep '\s*zone\s*\"%(name)s\"\s*{' %(conf_path)s > /dev/null ||" + " { echo -e '%(conf)s' >> %(conf_path)s; UPDATED=1; }" % context) + for subdomain in context['subdomains']: + context['name'] = subdomain.name + self.delete_conf(context) + + def delete(self, domain): + context = self.get_context(domain) + self.append('rm -f %(zone_path)s;' % context) + self.delete_conf(context) + + def delete_conf(self, context): + self.append('awk -v s=%(name)s \'BEGIN {' + ' RS=""; s="zone \\""s"\\""' + '} $0!~s{ print $0"\\n" }\' %(conf_path)s > %(conf_path)s.tmp' + % context) + self.append('diff -I"^\s*//" %(conf_path)s.tmp %(conf_path)s || UPDATED=1' % context) + self.append('mv %(conf_path)s.tmp %(conf_path)s' % context) + + def commit(self): + """ reload bind if needed """ + self.append('[[ $UPDATED == 1 ]] && service bind9 reload') + + def get_context(self, domain): + context = { + 'name': domain.name, + 'zone_path': settings.DOMAINS_ZONE_PATH % {'name': domain.name}, + 'subdomains': domain.get_subdomains(), + 'banner': self.get_banner(), + } + context.update({ + 'conf_path': settings.DOMAINS_MASTERS_PATH, + 'conf': 'zone "%(name)s" {\n' + ' // %(banner)s\n' + ' type master;\n' + ' file "%(zone_path)s";\n' + '};\n' % context + }) + return context + + +class Bind9SlaveDomainBackend(Bind9MasterDomainBackend): + verbose_name = _("Bind9 slave domain") + related_models = (('domains.Domain', 'origin'),) + def save(self, domain): + context = self.get_context(domain) + self.update_conf(context) + + def delete(self, domain): + context = self.get_context(domain) + self.delete_conf(context) + + def commit(self): + """ ideally slave should be restarted after master """ + self.append('[[ $UPDATED == 1 ]] && { sleep 1 && service bind9 reload; } &') + + def get_context(self, domain): + context = { + 'name': domain.name, + 'masters': '; '.join(settings.DOMAINS_MASTERS), + 'subdomains': domain.get_subdomains() + } + context.update({ + 'conf_path': settings.DOMAINS_SLAVES_PATH, + 'conf': 'zone "%(name)s" {\n' + ' type slave;\n' + ' file "%(name)s";\n' + ' masters { %(masters)s; };\n' + '};\n' % context + }) + return context diff --git a/orchestra/apps/domains/filters.py b/orchestra/apps/domains/filters.py new file mode 100644 index 00000000..71d933f5 --- /dev/null +++ b/orchestra/apps/domains/filters.py @@ -0,0 +1,35 @@ +from django.contrib.admin import SimpleListFilter +from django.utils.encoding import force_text +from django.utils.translation import ugettext_lazy as _ + + +class TopDomainListFilter(SimpleListFilter): + """ Filter Nodes by group according to request.user """ + title = _("Top domains") + parameter_name = 'top_domain' + + def lookups(self, request, model_admin): + return ( + ('True', _("Top domains")), + ('False', _("All")), + ) + + def queryset(self, request, queryset): + if self.value() == 'True': + return queryset.filter(top__isnull=True) + + def choices(self, cl): + """ Enable default selection different than All """ + for lookup, title in self.lookup_choices: + title = title._proxy____args[0] + selected = self.value() == force_text(lookup) + if not selected and title == "Top domains" and self.value() is None: + selected = True + # end of workaround + yield { + 'selected': selected, + 'query_string': cl.get_query_string({ + self.parameter_name: lookup, + }, []), + 'display': title, + } diff --git a/orchestra/apps/domains/forms.py b/orchestra/apps/domains/forms.py new file mode 100644 index 00000000..19e09927 --- /dev/null +++ b/orchestra/apps/domains/forms.py @@ -0,0 +1,56 @@ +from django import forms +from django.core.exceptions import ValidationError +from django.utils.translation import ugettext_lazy as _ + +from . import validators +from .helpers import domain_for_validation +from .models import Domain + + +class DomainAdminForm(forms.ModelForm): + def clean(self): + """ inherit related top domain account, when exists """ + cleaned_data = super(DomainAdminForm, self).clean() + if not cleaned_data['account']: + domain = Domain(name=cleaned_data['name']) + top = domain.get_top() + if not top: + # Fake an account to make django validation happy + Account = self.fields['account']._queryset.model + cleaned_data['account'] = Account() + msg = _("An account should be provided for top domain names") + raise ValidationError(msg) + cleaned_data['account'] = top.account + return cleaned_data + + +class RecordInlineFormSet(forms.models.BaseInlineFormSet): + def clean(self): + """ Checks if everything is consistent """ + if any(self.errors): + return + if self.instance.name: + records = [] + for form in self.forms: + data = form.cleaned_data + if data and not data['DELETE']: + records.append(data) + domain = domain_for_validation(self.instance, records) + validators.validate_zone(domain.render_zone()) + + +class DomainIterator(forms.models.ModelChoiceIterator): + """ Group ticket owner by superusers, ticket.group and regular users """ + def __init__(self, *args, **kwargs): + self.account = kwargs.pop('account') + self.domains = kwargs.pop('domains') + super(forms.models.ModelChoiceIterator, self).__init__(*args, **kwargs) + + def __iter__(self): + yield ('', '---------') + account_domains = self.domains.filter(account=self.account) + account_domains = account_domains.values_list('pk', 'name') + yield (_("Account"), list(account_domains)) + domains = self.domains.exclude(account=self.account) + domains = domains.values_list('pk', 'name') + yield (_("Other"), list(domains)) diff --git a/orchestra/apps/domains/helpers.py b/orchestra/apps/domains/helpers.py new file mode 100644 index 00000000..2e6eac91 --- /dev/null +++ b/orchestra/apps/domains/helpers.py @@ -0,0 +1,24 @@ +import copy + +from .models import Domain, Record + + +def domain_for_validation(instance, records): + """ Create a fake zone in order to generate the whole zone file and check it """ + domain = copy.copy(instance) + if not domain.pk: + domain.top = domain.get_top() + def get_records(): + for data in records: + yield Record(type=data['type'], value=data['value']) + domain.get_records = get_records + if domain.top: + subdomains = domain.get_topsubdomains().exclude(pk=instance.pk) + domain.top.get_subdomains = lambda: list(subdomains) + [domain] + elif not domain.pk: + subdomains = [] + for subdomain in Domain.objects.filter(name__endswith=domain.name): + subdomain.top = domain + subdomains.append(subdomain) + domain.get_subdomains = lambda: subdomains + return domain diff --git a/orchestra/apps/domains/models.py b/orchestra/apps/domains/models.py new file mode 100644 index 00000000..21aef911 --- /dev/null +++ b/orchestra/apps/domains/models.py @@ -0,0 +1,174 @@ +from django.core.exceptions import ValidationError +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from orchestra.core import services +from orchestra.core.validators import (validate_ipv4_address, validate_ipv6_address, + validate_hostname, validate_ascii) +from orchestra.utils.functional import cached + +from . import settings, validators, utils + + +class Domain(models.Model): + name = models.CharField(_("name"), max_length=256, unique=True, + validators=[validate_hostname, validators.validate_allowed_domain]) + account = models.ForeignKey('accounts.Account', verbose_name=_("Account"), + related_name='domains', blank=True) + top = models.ForeignKey('domains.Domain', null=True, related_name='subdomains') + serial = models.IntegerField(_("serial"), default=utils.generate_zone_serial, + help_text=_("Serial number")) + + def __unicode__(self): + return self.name + + @property + @cached + def origin(self): + return self.top or self + + def get_records(self): + """ proxy method, needed for input validation """ + return self.records.all() + + def get_topsubdomains(self): + """ proxy method, needed for input validation """ + return self.origin.subdomains.all() + + def get_subdomains(self): + return self.get_topsubdomains().filter(name__regex=r'.%s$' % self.name) + + def render_zone(self): + origin = self.origin + zone = origin.render_records() + for subdomain in origin.get_topsubdomains(): + zone += subdomain.render_records() + return zone + + def refresh_serial(self): + """ Increases the domain serial number by one """ + serial = utils.generate_zone_serial() + if serial <= self.serial: + num = int(str(self.serial)[8:]) + 1 + if num >= 99: + raise ValueError('No more serial numbers for today') + serial = str(self.serial)[:8] + '%.2d' % num + serial = int(serial) + self.serial = serial + self.save() + + def render_records(self): + types = {} + records = [] + for record in self.get_records(): + types[record.type] = True + if record.type == record.SOA: + # Update serial and insert at 0 + value = record.value.split() + value[2] = str(self.serial) + records.insert(0, (record.SOA, ' '.join(value))) + else: + records.append((record.type, record.value)) + if not self.top: + if Record.NS not in types: + for ns in settings.DOMAINS_DEFAULT_NS: + records.append((Record.NS, ns)) + if Record.SOA not in types: + soa = [ + "%s." % settings.DOMAINS_DEFAULT_NAME_SERVER, + utils.format_hostmaster(settings.DOMAINS_DEFAULT_HOSTMASTER), + str(self.serial), + settings.DOMAINS_DEFAULT_REFRESH, + settings.DOMAINS_DEFAULT_RETRY, + settings.DOMAINS_DEFAULT_EXPIRATION, + settings.DOMAINS_DEFAULT_MIN_CACHING_TIME + ] + records.insert(0, (Record.SOA, ' '.join(soa))) + no_cname = Record.CNAME not in types + if Record.MX not in types and no_cname: + for mx in settings.DOMAINS_DEFAULT_MX: + records.append((Record.MX, mx)) + if (Record.A not in types and Record.AAAA not in types) and no_cname: + records.append((Record.A, settings.DOMAINS_DEFAULT_A)) + result = '' + for type, value in records: + name = '%s.%s' % (self.name, ' '*(37-len(self.name))) + type = '%s %s' % (type, ' '*(7-len(type))) + result += '%s IN %s %s\n' % (name, type, value) + return result + + def save(self, *args, **kwargs): + """ create top relation """ + update = False + if not self.pk: + top = self.get_top() + if top: + self.top = top + else: + update = True + super(Domain, self).save(*args, **kwargs) + if update: + domains = Domain.objects.exclude(pk=self.pk) + for domain in domains.filter(name__endswith=self.name): + domain.top = self + domain.save() + self.get_subdomains().update(account=self.account) + + def get_top(self): + split = self.name.split('.') + top = None + for i in range(1, len(split)-1): + name = '.'.join(split[i:]) + domain = Domain.objects.filter(name=name) + if domain: + top = domain.get() + return top + + +class Record(models.Model): + """ Represents a domain resource record """ + MX = 'MX' + NS = 'NS' + CNAME = 'CNAME' + A = 'A' + AAAA = 'AAAA' + SRV = 'SRV' + TXT = 'TXT' + SOA = 'SOA' + + TYPE_CHOICES = ( + (MX, "MX"), + (NS, "NS"), + (CNAME, "CNAME"), + (A, _("A (IPv4 address)")), + (AAAA, _("AAAA (IPv6 address)")), + (SRV, "SRV"), + (TXT, "TXT"), + (SOA, "SOA"), + ) + + # TODO TTL + domain = models.ForeignKey(Domain, verbose_name=_("domain"), related_name='records') + type = models.CharField(max_length=32, choices=TYPE_CHOICES) + value = models.CharField(max_length=256) + + def __unicode__(self): + return "%s IN %s %s" % (self.domain, self.type, self.value) + + def clean(self): + """ validates record value based on its type """ + # validate value + mapp = { + self.MX: validators.validate_mx_record, + self.NS: validators.validate_zone_label, + self.A: validate_ipv4_address, + self.AAAA: validate_ipv6_address, + self.CNAME: validators.validate_zone_label, + self.TXT: validate_ascii, + self.SRV: validators.validate_srv_record, + self.SOA: validators.validate_soa_record, + } + mapp[self.type](self.value) + + +services.register(Domain) diff --git a/orchestra/apps/domains/serializers.py b/orchestra/apps/domains/serializers.py new file mode 100644 index 00000000..75550f38 --- /dev/null +++ b/orchestra/apps/domains/serializers.py @@ -0,0 +1,40 @@ +from django.core.exceptions import ValidationError +from rest_framework import serializers + +from orchestra.apps.accounts.serializers import AccountSerializerMixin + +from .helpers import domain_for_validation +from .models import Domain, Record +from . import validators + + +class RecordSerializer(serializers.ModelSerializer): + class Meta: + model = Record + fields = ('type', 'value') + + def get_identity(self, data): + return data.get('value') + + +class DomainSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer): + """ Validates if this zone generates a correct zone file """ + records = RecordSerializer(required=False, many=True, allow_add_remove=True) + + class Meta: + model = Domain + fields = ('url', 'id', 'name', 'records') + + def full_clean(self, instance): + """ Checks if everything is consistent """ + instance = super(DomainSerializer, self).full_clean(instance) + if instance and instance.name: + records = self.init_data['records'] + domain = domain_for_validation(instance, records) + try: + validators.validate_zone(domain.render_zone()) + except ValidationError as err: + self._errors = { 'all': err.message } + return None + return instance + diff --git a/orchestra/apps/domains/settings.py b/orchestra/apps/domains/settings.py new file mode 100644 index 00000000..3c31f571 --- /dev/null +++ b/orchestra/apps/domains/settings.py @@ -0,0 +1,51 @@ +from django.conf import settings + + +DOMAINS_DEFAULT_NAME_SERVER = getattr(settings, 'DOMAINS_DEFAULT_NAME_SERVER', + 'ns.example.com') + +DOMAINS_DEFAULT_HOSTMASTER = getattr(settings, 'DOMAINS_DEFAULT_HOSTMASTER', + 'hostmaster@example.com') + +DOMAINS_DEFAULT_TTL = getattr(settings, 'DOMAINS_DEFAULT_TTL', '1h') + +DOMAINS_DEFAULT_REFRESH = getattr(settings, 'DOMAINS_DEFAULT_REFRESH', '1d') + +DOMAINS_DEFAULT_RETRY = getattr(settings, 'DOMAINS_DEFAULT_RETRY', '2h') + +DOMAINS_DEFAULT_EXPIRATION = getattr(settings, 'DOMAINS_DEFAULT_EXPIRATION', '4w') + +DOMAINS_DEFAULT_MIN_CACHING_TIME = getattr(settings, 'DOMAINS_DEFAULT_MIN_CACHING_TIME', '1h') + +DOMAINS_ZONE_PATH = getattr(settings, 'DOMAINS_ZONE_PATH', '/etc/bind/master/%(name)s') + +DOMAINS_MASTERS_PATH = getattr(settings, 'DOMAINS_MASTERS_PATH', '/etc/bind/named.conf.local') + +DOMAINS_SLAVES_PATH = getattr(settings, 'DOMAINS_SLAVES_PATH', '/etc/bind/named.conf.local') + +DOMAINS_MASTERS = getattr(settings, 'DOMAINS_MASTERS', ['10.0.3.13']) + +DOMAINS_CHECKZONE_BIN_PATH = getattr(settings, 'DOMAINS_CHECKZONE_BIN_PATH', + '/usr/sbin/named-checkzone -i local') + +DOMAINS_CHECKZONE_PATH = getattr(settings, 'DOMAINS_CHECKZONE_PATH', '/dev/shm') + +DOMAINS_DEFAULT_A = getattr(settings, 'DOMAINS_DEFAULT_A', '10.0.3.13') + +DOMAINS_DEFAULT_MX = getattr(settings, 'DOMAINS_DEFAULT_MX', ( + '10 mail.orchestra.lan.', + '10 mail2.orchestra.lan.', +)) + +DOMAINS_DEFAULT_NS = getattr(settings, 'DOMAINS_DEFAULT_NS', ( + 'ns1.orchestra.lan.', + 'ns2.orchestra.lan.', +)) + +DOMAINS_FORBIDDEN = getattr(settings, 'DOMAINS_FORBIDDEN', + # This setting prevents users from providing random domain names, i.e. google.com + # You can generate a 5K forbidden domains list from Alexa's top 1M + # wget http://s3.amazonaws.com/alexa-static/top-1m.csv.zip -O /tmp/top-1m.csv.zip + # unzip -p /tmp/top-1m.csv.zip | head -n 5000 | sed "s/^.*,//" > forbidden_domains.list + # '%(site_root)s/forbidden_domains.list') + '') diff --git a/orchestra/apps/domains/templates/admin/domains/domain/change_form.html b/orchestra/apps/domains/templates/admin/domains/domain/change_form.html new file mode 100644 index 00000000..c14c9254 --- /dev/null +++ b/orchestra/apps/domains/templates/admin/domains/domain/change_form.html @@ -0,0 +1,15 @@ +{% extends "admin/change_form.html" %} +{% load i18n admin_urls admin_static admin_modify %} + + +{% block object-tools-items %} +
  • + {% trans "View zone" %} +
  • +
  • + {% url opts|admin_urlname:'history' original.pk|admin_urlquote as history_url %} + {% trans "History" %} +
  • +{% if has_absolute_url %}
  • {% trans "View on site" %}
  • {% endif%} +{% endblock %} + diff --git a/orchestra/apps/domains/templates/admin/domains/domain/view_zone.html b/orchestra/apps/domains/templates/admin/domains/domain/view_zone.html new file mode 100644 index 00000000..838e1073 --- /dev/null +++ b/orchestra/apps/domains/templates/admin/domains/domain/view_zone.html @@ -0,0 +1,22 @@ +{% extends "admin/base_site.html" %} +{% load i18n admin_urls %} + +{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation{% endblock %} + +{% block breadcrumbs %} + +{% endblock %} + +{% block content %} + +
    +{{ object.render_zone }}
    +
    +{% endblock %} + diff --git a/orchestra/apps/domains/tests/__init__.py b/orchestra/apps/domains/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/orchestra/apps/domains/tests/functional_tests/__init__.py b/orchestra/apps/domains/tests/functional_tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/orchestra/apps/domains/tests/functional_tests/tests.py b/orchestra/apps/domains/tests/functional_tests/tests.py new file mode 100644 index 00000000..b9d341d9 --- /dev/null +++ b/orchestra/apps/domains/tests/functional_tests/tests.py @@ -0,0 +1,299 @@ +import functools +import os +import time + +from selenium.webdriver.support.select import Select + +from orchestra.apps.orchestration.models import Server, Route +from orchestra.utils.tests import BaseLiveServerTestCase, random_ascii +from orchestra.utils.system import run + +from orchestra.apps.domains import settings, utils, backends +from orchestra.apps.domains.models import Domain, Record + + +run = functools.partial(run, display=False) + + +class DomainTestMixin(object): + def setUp(self): + super(DomainTestMixin, self).setUp() + self.MASTER_ADDR = os.environ['ORCHESTRA_DNS_MASTER_ADDR'] + self.SLAVE_ADDR = os.environ['ORCHESTRA_DNS_SLAVE_ADDR'] + self.domain_name = 'orchestra%s.lan' % random_ascii(10) + self.domain_records = ( + (Record.MX, '10 mail.orchestra.lan.'), + (Record.MX, '20 mail2.orchestra.lan.'), + (Record.NS, 'ns1.%s.' % self.domain_name), + (Record.NS, 'ns2.%s.' % self.domain_name), + ) + self.domain_update_records = ( + (Record.MX, '30 mail3.orchestra.lan.'), + (Record.MX, '40 mail4.orchestra.lan.'), + (Record.NS, 'ns1.%s.' % self.domain_name), + (Record.NS, 'ns2.%s.' % self.domain_name), + ) + self.subdomain1_name = 'ns1.%s' % self.domain_name + self.subdomain1_records = ( + (Record.A, '%s' % self.SLAVE_ADDR), + ) + self.subdomain2_name = 'ns2.%s' % self.domain_name + self.subdomain2_records = ( + (Record.A, '%s' % self.MASTER_ADDR), + ) + self.subdomain3_name = 'www.%s' % self.domain_name + self.subdomain3_records = ( + (Record.CNAME, 'external.server.org.'), + ) + self.second_domain_name = 'django%s.lan' % random_ascii(10) + + def tearDown(self): + try: + self.delete(self.domain_name) + except Domain.DoesNotExist: + pass + super(DomainTestMixin, self).tearDown() + + def add_route(self): + raise NotImplementedError + + def add(self, domain_name, records): + raise NotImplementedError + + def delete(self, domain_name, records): + raise NotImplementedError + + def update(self, domain_name, records): + raise NotImplementedError + + 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 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, error_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]) + + def validate_update(self, server_addr, domain_name): + domain = Domain.objects.get(name=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 + 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"' + mx = run(dig_mx % context).stdout.split() + # testdomain.org. 3600 IN MX 10 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], ['30', '40']) + self.assertIn(mx[5], ['mail3.orchestra.lan.', 'mail4.orchestra.lan.']) + + 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]) + + def test_add(self): + self.add(self.subdomain1_name, self.subdomain1_records) + self.add(self.subdomain2_name, self.subdomain2_records) + self.add(self.domain_name, self.domain_records) + self.validate_add(self.MASTER_ADDR, self.domain_name) + self.validate_add(self.SLAVE_ADDR, self.domain_name) + + def test_delete(self): + self.add(self.subdomain1_name, self.subdomain1_records) + self.add(self.subdomain2_name, self.subdomain2_records) + self.add(self.domain_name, self.domain_records) + self.delete(self.domain_name) + for name in [self.domain_name, self.subdomain1_name, self.subdomain2_name]: + self.validate_delete(self.MASTER_ADDR, name) + self.validate_delete(self.SLAVE_ADDR, name) + + def test_update(self): + self.add(self.subdomain1_name, self.subdomain1_records) + self.add(self.subdomain2_name, self.subdomain2_records) + self.add(self.domain_name, self.domain_records) + self.update(self.domain_name, self.domain_update_records) + self.add(self.subdomain3_name, self.subdomain3_records) + self.validate_update(self.MASTER_ADDR, self.domain_name) + time.sleep(5) + self.validate_update(self.SLAVE_ADDR, self.domain_name) + + def test_add_add_delete_delete(self): + self.add(self.subdomain1_name, self.subdomain1_records) + self.add(self.subdomain2_name, self.subdomain2_records) + self.add(self.domain_name, self.domain_records) + self.add(self.second_domain_name, self.domain_records) + self.delete(self.domain_name) + self.validate_add(self.MASTER_ADDR, self.second_domain_name) + self.validate_add(self.SLAVE_ADDR, self.second_domain_name) + self.delete(self.second_domain_name) + self.validate_delete(self.MASTER_ADDR, self.second_domain_name) + self.validate_delete(self.SLAVE_ADDR, self.second_domain_name) + + +class AdminDomainMixin(DomainTestMixin): + def setUp(self): + super(AdminDomainMixin, self).setUp() + self.add_route() + self.admin_login() + + def _add_records(self, records): + self.selenium.find_element_by_link_text('Add another Record').click() + for i, record in zip(range(0, len(records)), records): + type, value = record + type_input = self.selenium.find_element_by_id('id_records-%d-type' % i) + type_select = Select(type_input) + type_select.select_by_value(type) + value_input = self.selenium.find_element_by_id('id_records-%d-value' % i) + value_input.clear() + value_input.send_keys(value) + return value_input + + def add(self, domain_name, records): + url = self.live_server_url + '/admin/domains/domain/add/' + self.selenium.get(url) + name = self.selenium.find_element_by_id('id_name') + name.send_keys(domain_name) + value_input = self._add_records(records) + value_input.submit() + self.assertNotEqual(url, self.selenium.current_url) + + def delete(self, domain_name): + domain = Domain.objects.get(name=domain_name) + url = self.live_server_url + '/admin/domains/domain/%d/delete/' % domain.pk + self.selenium.get(url) + form = self.selenium.find_element_by_name('post') + form.submit() + self.assertNotEqual(url, self.selenium.current_url) + + def update(self, domain_name, records): + domain = Domain.objects.get(name=domain_name) + url = self.live_server_url + '/admin/domains/domain/%d/' % domain.pk + self.selenium.get(url) + value_input = self._add_records(records) + value_input.submit() + self.assertNotEqual(url, self.selenium.current_url) + + +class RESTDomainMixin(DomainTestMixin): + def setUp(self): + super(RESTDomainMixin, self).setUp() + self.rest_login() + self.add_route() + + def add(self, domain_name, records): + records = [ dict(type=type, value=value) for type,value in records ] + self.rest.domains.create(name=domain_name, records=records) + + def delete(self, domain_name): + domain = Domain.objects.get(name=domain_name) + domain = self.rest.domains.retrieve(id=domain.pk) + domain.delete() + + def update(self, domain_name, records): + records = [ dict(type=type, value=value) for type,value in records ] + domains = self.rest.domains.retrieve(name=domain_name) + domain = domains.get() + domain.update(records=records) + + +class Bind9BackendMixin(object): + DEPENDENCIES = ( + 'orchestra.apps.orchestration', + ) + + def add_route(self): + master = Server.objects.create(name=self.MASTER_ADDR) + backend = backends.Bind9MasterDomainBackend.get_name() + Route.objects.create(backend=backend, match=True, host=master) + slave = Server.objects.create(name=self.SLAVE_ADDR) + backend = backends.Bind9SlaveDomainBackend.get_name() + Route.objects.create(backend=backend, match=True, host=slave) + + +class RESTBind9BackendDomainTest(Bind9BackendMixin, RESTDomainMixin, BaseLiveServerTestCase): + pass + + +class AdminBind9BackendDomainest(Bind9BackendMixin, AdminDomainMixin, BaseLiveServerTestCase): + pass diff --git a/orchestra/apps/domains/tests/test_domains.py b/orchestra/apps/domains/tests/test_domains.py new file mode 100644 index 00000000..e370bda3 --- /dev/null +++ b/orchestra/apps/domains/tests/test_domains.py @@ -0,0 +1,18 @@ +from django.db import IntegrityError, transaction +from django.test import TestCase + +from ..models import Domain + + +class DomainTests(TestCase): + def setUp(self): + self.domain = Domain.objects.create(name='rostrepalid.org') + Domain.objects.create(name='www.rostrepalid.org') + Domain.objects.create(name='mail.rostrepalid.org') + + def test_top_relation(self): + self.assertEqual(2, len(self.domain.subdomains.all())) + + def test_render_zone(self): + print self.domain.render_zone() + diff --git a/orchestra/apps/domains/utils.py b/orchestra/apps/domains/utils.py new file mode 100644 index 00000000..a5fdcb49 --- /dev/null +++ b/orchestra/apps/domains/utils.py @@ -0,0 +1,26 @@ +import datetime + + +def generate_zone_serial(): + today = datetime.date.today() + return int("%.4d%.2d%.2d%.2d" % (today.year, today.month, today.day, 0)) + + +def format_hostmaster(hostmaster): + """ + The DNS encodes the as a single label, and encodes the + as a domain name. The single label from the + is prefaced to the domain name from to form the domain + name corresponding to the mailbox. Thus the mailbox HOSTMASTER@SRI- + NIC.ARPA is mapped into the domain name HOSTMASTER.SRI-NIC.ARPA. If the + contains dots or other special characters, its + representation in a master file will require the use of backslash + quoting to ensure that the domain name is properly encoded. For + example, the mailbox Action.domains@ISI.EDU would be represented as + Action\.domains.ISI.EDU. + http://www.ietf.org/rfc/rfc1035.txt + """ + name, domain = hostmaster.split('@') + if '.' in name: + name = name.replace('.', '\.') + return "%s.%s." % (name, domain) diff --git a/orchestra/apps/domains/validators.py b/orchestra/apps/domains/validators.py new file mode 100644 index 00000000..891476f3 --- /dev/null +++ b/orchestra/apps/domains/validators.py @@ -0,0 +1,108 @@ +import os +import re + +from django.core.exceptions import ValidationError +from django.utils.translation import ugettext_lazy as _ + +from orchestra.utils import paths +from orchestra.utils.system import run + +from . import settings + + +def validate_allowed_domain(value): + context = { + 'site_root': paths.get_site_root() + } + fname = settings.DOMAINS_FORBIDDEN + if fname: + fname = fname % context + with open(fname, 'r') as forbidden: + for domain in forbidden.readlines(): + if re.match(r'^(.*\.)*%s$' % domain.strip(), value): + raise ValidationError(_("This domain name is not allowed")) + + +def validate_zone_interval(value): + try: + int(value) + except ValueError: + value, magnitude = value[:-1], value[-1] + if magnitude not in ('s', 'm', 'h', 'd', 'w') or not value.isdigit(): + msg = _("%s is not an appropiate zone interval value") % value + raise ValidationError(msg) + + +def validate_zone_label(value): + """ + http://www.ietf.org/rfc/rfc1035.txt + The labels must follow the rules for ARPANET host names. They must + start with a letter, end with a letter or digit, and have as interior + characters only letters, digits, and hyphen. There are also some + restrictions on the length. Labels must be 63 characters or less. + """ + if not re.match(r'^[a-z][\.\-0-9a-z]*[\.0-9a-z]$', value): + msg = _("Labels must start with a letter, end with a letter or digit, " + "and have as interior characters only letters, digits, and hyphen") + raise ValidationError(msg) + if not value.endswith('.'): + msg = _("Use a fully expanded domain name ending with a dot") + raise ValidationError(msg) + if len(value) > 63: + raise ValidationError(_("Labels must be 63 characters or less")) + + +def validate_mx_record(value): + msg = _("%s is not an appropiate MX record value") % value + value = value.split() + if len(value) == 1: + value = value[0] + elif len(value) == 2: + try: + int(value[0]) + except ValueError: + raise ValidationError(msg) + value = value[1] + elif len(value) > 2: + raise ValidationError(msg) + validate_zone_label(value) + + +def validate_srv_record(value): + # 1 0 9 server.example.com. + msg = _("%s is not an appropiate SRV record value") % value + value = value.split() + for i in [0,1,2]: + try: + int(value[i]) + except ValueError: + raise ValidationError(msg) + validate_zone_label(value[-1]) + + +def validate_soa_record(value): + # ns1.pangea.ORG. hostmaster.pangea.ORG. 2012010401 28800 7200 604800 86400 + msg = _("%s is not an appropiate SRV record value") % value + values = value.split() + if len(values) != 7: + raise ValidationError(msg) + validate_zone_label(values[0]) + validate_zone_label(values[1]) + for value in values[2:]: + try: + int(value) + except ValueError: + raise ValidationError(msg) + + +def validate_zone(zone): + """ Ultimate zone file validation using named-checkzone """ + zone_name = zone.split()[0][:-1] + path = os.path.join(settings.DOMAINS_CHECKZONE_PATH, zone_name) + with open(path, 'wb') as f: + f.write(zone) + checkzone = settings.DOMAINS_CHECKZONE_BIN_PATH + check = run(' '.join([checkzone, zone_name, path]), error_codes=[0,1], display=False) + if check.return_code == 1: + errors = re.compile(r'zone.*: (.*)').findall(check.stdout)[:-1] + raise ValidationError(', '.join(errors)) diff --git a/orchestra/apps/issues/__init__.py b/orchestra/apps/issues/__init__.py new file mode 100644 index 00000000..edea65c4 --- /dev/null +++ b/orchestra/apps/issues/__init__.py @@ -0,0 +1 @@ +REQUIRED_APPS = ['slices'] diff --git a/orchestra/apps/issues/actions.py b/orchestra/apps/issues/actions.py new file mode 100644 index 00000000..64ac89ed --- /dev/null +++ b/orchestra/apps/issues/actions.py @@ -0,0 +1,109 @@ +import sys + +from django.contrib import messages +from django.db import transaction + +from orchestra.admin.decorators import action_with_confirmation + +from .forms import ChangeReasonForm +from .helpers import markdown_formated_changes +from .models import Queue, Ticket + + +def change_ticket_state_factory(action, final_state): + context = { + 'action': action, + 'form': ChangeReasonForm() + } + @transaction.atomic + @action_with_confirmation(action, extra_context=context) + def change_ticket_state(modeladmin, request, queryset, action=action, final_state=final_state): + form = ChangeReasonForm(request.POST) + if form.is_valid(): + reason = form.cleaned_data['reason'] + for ticket in queryset: + if ticket.state != final_state: + changes = {'state': (ticket.state, final_state)} + is_read = ticket.is_read_by(request.user) + getattr(ticket, action)() + modeladmin.log_change(request, ticket, "Marked as %s" % final_state.lower()) + content = markdown_formated_changes(changes) + content += reason + ticket.messages.create(content=content, author=request.user) + if is_read and not ticket.is_read_by(request.user): + ticket.mark_as_read_by(request.user) + msg = "%s selected tickets are now %s." % (queryset.count(), final_state.lower()) + modeladmin.message_user(request, msg) + else: + context['form'] = form + # action_with_confirmation must display form validation errors + return True + change_ticket_state.url_name = action + change_ticket_state.verbose_name = u'%s\u2026' % action + change_ticket_state.short_description = '%s selected tickets' % action.capitalize() + change_ticket_state.description = 'Mark ticket as %s.' % final_state.lower() + change_ticket_state.__name__ = action + return change_ticket_state + + +action_map = { + Ticket.RESOLVED: 'resolve', + Ticket.REJECTED: 'reject', + Ticket.CLOSED: 'close' } + + +thismodule = sys.modules[__name__] +for state, name in action_map.items(): + action = change_ticket_state_factory(name, state) + setattr(thismodule, '%s_tickets' % name, action) + + +@transaction.atomic +def take_tickets(modeladmin, request, queryset): + for ticket in queryset: + if ticket.owner != request.user: + changes = {'owner': (ticket.owner, request.user)} + is_read = ticket.is_read_by(request.user) + ticket.take(request.user) + modeladmin.log_change(request, ticket, "Taken") + content = markdown_formated_changes(changes) + ticket.messages.create(content=content, author=request.user) + if is_read and not ticket.is_read_by(request.user): + ticket.mark_as_read_by(request.user) + msg = "%s selected tickets are now owned by %s." % (queryset.count(), request.user) + modeladmin.message_user(request, msg) +take_tickets.url_name = 'take' +take_tickets.short_description = 'Take selected tickets' +take_tickets.description = 'Make yourself owner of the ticket.' + + +@transaction.atomic +def mark_as_unread(modeladmin, request, queryset): + """ Mark a tickets as unread """ + for ticket in queryset: + ticket.mark_as_unread_by(request.user) + msg = "%s selected tickets have been marked as unread." % queryset.count() + modeladmin.message_user(request, msg) + + +@transaction.atomic +def mark_as_read(modeladmin, request, queryset): + """ Mark a tickets as unread """ + for ticket in queryset: + ticket.mark_as_read_by(request.user) + msg = "%s selected tickets have been marked as read." % queryset.count() + modeladmin.message_user(request, msg) + + +@transaction.atomic +def set_default_queue(modeladmin, request, queryset): + """ Set a queue as default issues queue """ + if queryset.count() != 1: + messages.warning(request, "Please, select only one queue.") + return + Queue.objects.filter(default=True).update(default=False) + queue = queryset.get() + queue.default = True + queue.save() + modeladmin.log_change(request, queue, "Chosen as default.") + messages.info(request, "Chosen '%s' as default queue." % queue) diff --git a/orchestra/apps/issues/admin.py b/orchestra/apps/issues/admin.py new file mode 100644 index 00000000..7d50bdc1 --- /dev/null +++ b/orchestra/apps/issues/admin.py @@ -0,0 +1,337 @@ +from __future__ import absolute_import + +from django import forms +from django.conf.urls import patterns +from django.contrib import admin +from django.core.urlresolvers import reverse +from django.db import models +from django.http import HttpResponse +from django.shortcuts import get_object_or_404 +from django.utils.html import strip_tags +from django.utils.safestring import mark_safe +from django.utils.translation import ugettext_lazy as _ +from markdown import markdown + +from orchestra.admin import ChangeListDefaultFilter, ExtendedModelAdmin#, ChangeViewActions +from orchestra.admin.utils import (link, colored, wrap_admin_view, display_timesince) + +from .actions import (reject_tickets, resolve_tickets, take_tickets, close_tickets, + mark_as_unread, mark_as_read, set_default_queue) +from .filters import MyTicketsListFilter, TicketStateListFilter +from .forms import MessageInlineForm, TicketForm +from .helpers import get_ticket_changes, markdown_formated_changes, filter_actions +from .models import Ticket, Queue, Message + + +PRIORITY_COLORS = { + Ticket.HIGH: 'red', + Ticket.MEDIUM: 'darkorange', + Ticket.LOW: 'green', +} + + +STATE_COLORS = { + Ticket.NEW: 'grey', + Ticket.IN_PROGRESS: 'darkorange', + Ticket.FEEDBACK: 'purple', + Ticket.RESOLVED: 'green', + Ticket.REJECTED: 'firebrick', + Ticket.CLOSED: 'grey', +} + + +class MessageReadOnlyInline(admin.TabularInline): + model = Message + extra = 0 + can_delete = False + fields = ['content_html'] + readonly_fields = ['content_html'] + + class Media: + css = { + 'all': ('orchestra/css/hide-inline-id.css',) + } + + def content_html(self, obj): + context = { + 'num': obj.num, + 'time': display_timesince(obj.created_on), + 'author': link('author')(self, obj), + } + summary = _("#%(num)i Updated by %(author)s about %(time)s") % context + header = '%s
    ' % summary + content = markdown(obj.content) + content = content.replace('>\n', '>') + return header + content + content_html.short_description = _("Content") + content_html.allow_tags = True + + + def has_add_permission(self, request): + return False + + def has_delete_permission(self, request, obj=None): + return False + + +class MessageInline(admin.TabularInline): + model = Message + extra = 1 + max_num = 1 + form = MessageInlineForm + can_delete = False + fields = ['content'] + + def get_formset(self, request, obj=None, **kwargs): + """ hook request.user on the inline form """ + self.form.user = request.user + return super(MessageInline, self).get_formset(request, obj, **kwargs) + + def queryset(self, request): + """ Don't show any message """ + qs = super(MessageInline, self).queryset(request) + return qs.none() + + +class TicketInline(admin.TabularInline): + fields = [ + 'ticket_id', 'subject', 'creator_link', 'owner_link', 'colored_state', + 'colored_priority', 'created', 'last_modified' + ] + readonly_fields = [ + 'ticket_id', 'subject', 'creator_link', 'owner_link', 'colored_state', + 'colored_priority', 'created', 'last_modified' + ] + model = Ticket + extra = 0 + max_num = 0 + + creator_link = link('creator') + owner_link = link('owner') + + def ticket_id(self, instance): + return mark_safe('%s' % link()(self, instance)) + ticket_id.short_description = '#' + + def colored_state(self, instance): + return colored('state', STATE_COLORS, bold=False)(instance) + colored_state.short_description = _("State") + + def colored_priority(self, instance): + return colored('priority', PRIORITY_COLORS, bold=False)(instance) + colored_priority.short_description = _("Priority") + + def created(self, instance): + return display_timesince(instance.created_on) + + def last_modified(self, instance): + return display_timesince(instance.last_modified_on) + + +class TicketAdmin(ChangeListDefaultFilter, ExtendedModelAdmin): #TODO ChangeViewActions, + list_display = [ + 'unbold_id', 'bold_subject', 'display_creator', 'display_owner', + 'display_queue', 'display_priority', 'display_state', 'last_modified' + ] + list_display_links = ('unbold_id', 'bold_subject') + list_filter = [ + MyTicketsListFilter, 'queue__name', 'priority', TicketStateListFilter, + ] + default_changelist_filters = ( + ('my_tickets', lambda r: 'True' if not r.user.is_superuser else 'False'), + ('state', 'OPEN') + ) + date_hierarchy = 'created_on' + search_fields = [ + 'id', 'subject', 'creator__username', 'creator__email', 'queue__name', + 'owner__username' + ] + actions = [ + mark_as_unread, mark_as_read, 'delete_selected', reject_tickets, + resolve_tickets, close_tickets, take_tickets + ] + sudo_actions = ['delete_selected'] + change_view_actions = [ + resolve_tickets, close_tickets, reject_tickets, take_tickets + ] +# change_form_template = "admin/orchestra/change_form.html" + form = TicketForm + add_inlines = [] + inlines = [ MessageReadOnlyInline, MessageInline ] + readonly_fields = ( + 'display_summary', 'display_queue', 'display_owner', 'display_state', + 'display_priority' + ) + readonly_fieldsets = ( + (None, { + 'classes': ('wide',), + 'fields': ('display_summary', + ('display_queue', 'display_owner'), + ('display_state', 'display_priority'), + 'display_description') + }), + ) + fieldsets = readonly_fieldsets + ( + ('Update', { + 'classes': ('collapse', 'wide'), + 'fields': ('subject', + ('queue', 'owner',), + ('state', 'priority'), + 'description') + }), + ) + add_fieldsets = ( + (None, { + 'classes': ('wide',), + 'fields': ('subject', + ('queue', 'owner',), + ('state', 'priority'), + 'description') + }), + ) + + class Media: + css = { + 'all': ('issues/css/ticket-admin.css',) + } + js = ( + 'issues/js/ticket-admin.js', + ) + + display_creator = link('creator') + display_queue = link('queue') + display_owner = link('owner') + + def display_summary(self, ticket): + author_url = link('creator')(self, ticket) + created = display_timesince(ticket.created_on) + messages = ticket.messages.order_by('-created_on') + updated = '' + if messages: + updated_on = display_timesince(messages[0].created_on) + updated_by = link('author')(self, messages[0]) + updated = '. Updated by %s about %s' % (updated_by, updated_on) + msg = '

    Added by %s about %s%s

    ' % (author_url, created, updated) + return mark_safe(msg) + display_summary.short_description = 'Summary' + + def display_priority(self, ticket): + """ State colored for change_form """ + return colored('priority', PRIORITY_COLORS, bold=False, verbose=True)(ticket) + display_priority.short_description = _("Priority") + display_priority.admin_order_field = 'priority' + + def display_state(self, ticket): + """ State colored for change_form """ + return colored('state', STATE_COLORS, bold=False, verbose=True)(ticket) + display_state.short_description = _("State") + display_state.admin_order_field = 'state' + + def unbold_id(self, ticket): + """ Unbold id if ticket is read """ + if ticket.is_read_by(self.user): + return '%s' % ticket.pk + return ticket.pk + unbold_id.allow_tags = True + unbold_id.short_description = "#" + unbold_id.admin_order_field = 'id' + + def bold_subject(self, ticket): + """ Bold subject when tickets are unread for request.user """ + if ticket.is_read_by(self.user): + return ticket.subject + return "%s" % ticket.subject + bold_subject.allow_tags = True + bold_subject.short_description = _("Subject") + bold_subject.admin_order_field = 'subject' + + def last_modified(self, instance): + return display_timesince(instance.last_modified_on) + last_modified.admin_order_field = 'last_modified_on' + + def formfield_for_dbfield(self, db_field, **kwargs): + """ Make value input widget bigger """ + if db_field.name == 'subject': + kwargs['widget'] = forms.TextInput(attrs={'size':'120'}) + return super(TicketAdmin, self).formfield_for_dbfield(db_field, **kwargs) + + def save_model(self, request, obj, *args, **kwargs): + """ Define creator for new tickets """ + if not obj.pk: + obj.creator = request.user + super(TicketAdmin, self).save_model(request, obj, *args, **kwargs) + obj.mark_as_read_by(request.user) + + def get_urls(self): + """ add markdown preview url """ + urls = super(TicketAdmin, self).get_urls() + my_urls = patterns('', + (r'^preview/$', wrap_admin_view(self, self.message_preview_view)) + ) + return my_urls + urls + + def add_view(self, request, form_url='', extra_context=None): + """ Do not sow message inlines """ + return super(TicketAdmin, self).add_view(request, form_url, extra_context) + + def change_view(self, request, object_id, form_url='', extra_context=None): + """ Change view actions based on ticket state """ + ticket = get_object_or_404(Ticket, pk=object_id) + # Change view actions based on ticket state + self.change_view_actions = filter_actions(self, ticket, request) + if request.method == 'POST': + # Hack: Include the ticket changes on the request.POST + # other approaches get really messy + changes = get_ticket_changes(self, request, ticket) + if changes: + content = markdown_formated_changes(changes) + content += request.POST[u'messages-2-0-content'] + request.POST[u'messages-2-0-content'] = content + ticket.mark_as_read_by(request.user) + context = {'title': "Issue #%i - %s" % (ticket.id, ticket.subject)} + context.update(extra_context or {}) + return super(TicketAdmin, self).change_view( + request, object_id, form_url, extra_context=context) + + def changelist_view(self, request, extra_context=None): + # Hook user for bold_subject + self.user = request.user + return super(TicketAdmin,self).changelist_view(request, extra_context=extra_context) + + def message_preview_view(self, request): + """ markdown preview render via ajax """ + data = request.POST.get("data") + data_formated = markdown(strip_tags(data)) + return HttpResponse(data_formated) + + +class QueueAdmin(admin.ModelAdmin): + # TODO notify + list_display = [ + 'name', 'default', 'num_tickets' + ] + actions = [set_default_queue] + inlines = [TicketInline] + ordering = ['name'] + + class Media: + css = { + 'all': ('orchestra/css/hide-inline-id.css',) + } + + def num_tickets(self, queue): + num = queue.tickets.count() + url = reverse('admin:issues_ticket_changelist') + url += '?my_tickets=False&queue=%i' % queue.pk + return mark_safe('%d' % (url, num)) + num_tickets.short_description = _("Tickets") + num_tickets.admin_order_field = 'tickets__count' + + def queryset(self, request): + qs = super(QueueAdmin, self).queryset(request) + qs = qs.annotate(models.Count('tickets')) + return qs + + +admin.site.register(Ticket, TicketAdmin) +admin.site.register(Queue, QueueAdmin) diff --git a/orchestra/apps/issues/filters.py b/orchestra/apps/issues/filters.py new file mode 100644 index 00000000..8782574d --- /dev/null +++ b/orchestra/apps/issues/filters.py @@ -0,0 +1,43 @@ +from django.contrib.admin import SimpleListFilter + +from .models import Ticket + + +class MyTicketsListFilter(SimpleListFilter): + """ Filter tickets by created_by according to request.user """ + title = 'Tickets' + parameter_name = 'my_tickets' + + def lookups(self, request, model_admin): + return ( + ('True', 'My Tickets'), + ('False', 'All'), + ) + + def queryset(self, request, queryset): + if self.value() == 'True': + return queryset.involved_by(request.user) + + +class TicketStateListFilter(SimpleListFilter): + title = 'State' + parameter_name = 'state' + + def lookups(self, request, model_admin): + return ( + ('OPEN', "Open"), + (Ticket.NEW, "New"), + (Ticket.IN_PROGRESS, "In Progress"), + (Ticket.RESOLVED, "Resolved"), + (Ticket.FEEDBACK, "Feedback"), + (Ticket.REJECTED, "Rejected"), + (Ticket.CLOSED, "Closed"), + ('False', 'All'), + ) + + def queryset(self, request, queryset): + if self.value() == 'OPEN': + return queryset.exclude(state__in=[Ticket.CLOSED, Ticket.REJECTED]) + elif self.value() == 'False': + return queryset + return queryset.filter(state=self.value()) diff --git a/orchestra/apps/issues/forms.py b/orchestra/apps/issues/forms.py new file mode 100644 index 00000000..42984681 --- /dev/null +++ b/orchestra/apps/issues/forms.py @@ -0,0 +1,106 @@ +from django import forms +from django.core.urlresolvers import reverse +from django.utils.html import strip_tags +from django.utils.safestring import mark_safe +from django.utils.translation import ugettext_lazy as _ +from markdown import markdown + +from orchestra.apps.users.models import User +from orchestra.forms.widgets import ReadOnlyWidget + +from .models import Queue, Ticket + + +class MarkDownWidget(forms.Textarea): + """ MarkDown textarea widget with syntax preview """ + + markdown_url = '/static/issues/markdown_syntax.html' + markdown_help_text = ( + 'markdown format' % (markdown_url, markdown_url) + ) + markdown_help_text = 'HTML not allowed, you can use %s' % markdown_help_text + + def render(self, name, value, attrs): + widget_id = attrs['id'] if attrs and 'id' in attrs else 'id_%s' % name + textarea = super(MarkDownWidget, self).render(name, value, attrs) + preview = ('preview'\ + '
    '.format(widget_id)) + return mark_safe('

    %s
    %s
    %s

    ' % ( + self.markdown_help_text, textarea, preview)) + + +class MessageInlineForm(forms.ModelForm): + """ Add message form """ + created_on = forms.CharField(label="Created On", required=False) + content = forms.CharField(widget=MarkDownWidget(), required=False) + + def __init__(self, *args, **kwargs): + super(MessageInlineForm, self).__init__(*args, **kwargs) + admin_link = reverse('admin:users_user_change', args=(self.user.pk,)) + self.fields['created_on'].widget = ReadOnlyWidget('') + + def clean_content(self): + """ clean HTML tags """ + return strip_tags(self.cleaned_data['content']) + + def save(self, *args, **kwargs): + if self.instance.pk is None: + self.instance.author = self.user + return super(MessageInlineForm, self).save(*args, **kwargs) + + +class UsersIterator(forms.models.ModelChoiceIterator): + """ Group ticket owner by superusers, ticket.group and regular users """ + def __init__(self, *args, **kwargs): + self.ticket = kwargs.pop('ticket', False) + super(forms.models.ModelChoiceIterator, self).__init__(*args, **kwargs) + + def __iter__(self): + yield ('', '---------') + users = User.objects.exclude(is_active=False).order_by('name') + superusers = users.filter(is_superuser=True) + if superusers: + yield ('Operators', list(superusers.values_list('pk', 'name'))) + users = users.exclude(is_superuser=True) + if users: + yield ('Other', list(users.values_list('pk', 'name'))) + + +class TicketForm(forms.ModelForm): + display_description = forms.CharField(label=_("Description"), required=False) + description = forms.CharField(widget=MarkDownWidget(attrs={'class':'vLargeTextField'})) + + class Meta: + model = Ticket + + def __init__(self, *args, **kwargs): + super(TicketForm, self).__init__(*args, **kwargs) + ticket = kwargs.get('instance', False) + users = self.fields['owner'].queryset + self.fields['owner'].queryset = users.filter(is_superuser=True) + if not ticket: + # Provide default ticket queue for new ticket + try: + self.initial['queue'] = Queue.objects.get(default=True).id + except Queue.DoesNotExist: + pass + else: + description = markdown(ticket.description) + # some hacks for better line breaking + description = description.replace('>\n', '#Ha9G9-?8') + description = description.replace('\n', '
    ') + description = description.replace('#Ha9G9-?8', '>\n') + description = '
    %s
    ' % description + widget = ReadOnlyWidget(description, description) + self.fields['display_description'].widget = widget + + def clean_description(self): + """ clean HTML tags """ + return strip_tags(self.cleaned_data['description']) + + +class ChangeReasonForm(forms.Form): + reason = forms.CharField(widget=forms.Textarea(attrs={'cols': '100', 'rows': '10'}), + required=False) diff --git a/orchestra/apps/issues/helpers.py b/orchestra/apps/issues/helpers.py new file mode 100644 index 00000000..d15f4325 --- /dev/null +++ b/orchestra/apps/issues/helpers.py @@ -0,0 +1,36 @@ +def filter_actions(modeladmin, ticket, request): + if not hasattr(modeladmin, 'change_view_actions_backup'): + modeladmin.change_view_actions_backup = list(modeladmin.change_view_actions) + actions = modeladmin.change_view_actions_backup + if ticket.state == modeladmin.model.CLOSED: + del_actions = actions + else: + from .actions import action_map + del_actions = [action_map.get(ticket.state, None)] + if ticket.owner == request.user: + del_actions.append('take') + exclude = lambda a: not (a == action or a.url_name == action) + for action in del_actions: + actions = filter(exclude, actions) + return actions + + +def markdown_formated_changes(changes): + markdown = '' + for name, values in changes.items(): + context = (name.capitalize(), values[0], values[1]) + markdown += '* **%s** changed from _%s_ to _%s_\n' % context + return markdown + '\n' + + +def get_ticket_changes(modeladmin, request, ticket): + ModelForm = modeladmin.get_form(request, ticket) + form = ModelForm(request.POST, request.FILES) + changes = {} + if form.is_valid(): + for attr in ['state', 'priority', 'owner', 'queue']: + old_value = getattr(ticket, attr) + new_value = form.cleaned_data[attr] + if old_value != new_value: + changes[attr] = (old_value, new_value) + return changes diff --git a/orchestra/apps/issues/models.py b/orchestra/apps/issues/models.py new file mode 100644 index 00000000..73a2a31f --- /dev/null +++ b/orchestra/apps/issues/models.py @@ -0,0 +1,185 @@ +from django.contrib.auth import get_user_model +from django.db import models +from django.db.models import Q +from django.utils.translation import ugettext_lazy as _ + +from orchestra.apps.contacts import settings as contacts_settings +from orchestra.models.fields import MultiSelectField +from orchestra.utils import send_email_template + +from . import settings + + +class Queue(models.Model): + name = models.CharField(_("name"), max_length=128, unique=True) + default = models.BooleanField(_("default"), default=False) + notify = MultiSelectField(_("notify"), max_length=256, blank=True, + choices=contacts_settings.CONTACTS_EMAIL_USAGES, + default=contacts_settings.CONTACTS_DEFAULT_EMAIL_USAGES, + help_text=_("Contacts to notify by email")) + + def __unicode__(self): + return self.name + + def save(self, *args, **kwargs): + """ mark as default queue if needed """ + existing_default = Queue.objects.filter(default=True) + if self.default: + existing_default.update(default=False) + elif not existing_default: + self.default = True + super(Queue, self).save(*args, **kwargs) + + +class Ticket(models.Model): + HIGH = 'HIGH' + MEDIUM = 'MEDIUM' + LOW = 'LOW' + PRIORITIES = ( + (HIGH, 'High'), + (MEDIUM, 'Medium'), + (LOW, 'Low'), + ) + + NEW = 'NEW' + IN_PROGRESS = 'IN_PROGRESS' + RESOLVED = 'RESOLVED' + FEEDBACK = 'FEEDBACK' + REJECTED = 'REJECTED' + CLOSED = 'CLOSED' + STATES = ( + (NEW, 'New'), + (IN_PROGRESS, 'In Progress'), + (RESOLVED, 'Resolved'), + (FEEDBACK, 'Feedback'), + (REJECTED, 'Rejected'), + (CLOSED, 'Closed'), + ) + + creator = models.ForeignKey(get_user_model(), verbose_name=_("created by"), + related_name='tickets_created') + owner = models.ForeignKey(get_user_model(), null=True, blank=True, + related_name='tickets_owned', verbose_name=_("assigned to")) + queue = models.ForeignKey(Queue, related_name='tickets', null=True, blank=True) + subject = models.CharField(_("subject"), max_length=256) + description = models.TextField(_("description")) + priority = models.CharField(_("priority"), max_length=32, choices=PRIORITIES, + default=MEDIUM) + state = models.CharField(_("state"), max_length=32, choices=STATES, default=NEW) + created_on = models.DateTimeField(_("created on"), auto_now_add=True) + last_modified_on = models.DateTimeField(_("last modified on"), auto_now=True) + cc = models.TextField("CC", help_text=_("emails to send a carbon copy to"), + blank=True) + + class Meta: + ordering = ["-last_modified_on"] + + def __unicode__(self): + return unicode(self.pk) + + def get_notification_emails(self): + """ Get emails of the users related to the ticket """ + emails = list(settings.ISSUES_SUPPORT_EMAILS) + emails.append(self.creator.email) + if self.owner: + emails.append(self.owner.email) + for contact in self.creator.account.contacts.all(): + if self.queue and set(contact.email_usage).union(set(self.queue.nofify)): + emails.append(contact.email) + for message in self.messages.distinct('author'): + emails.append(message.author.email) + return set(emails + self.get_cc_emails()) + + def notify(self, message=None, content=None): + """ Send an email to ticket stakeholders notifying an state update """ + emails = self.get_notification_emails() + template = 'issues/ticket_notification.mail' + html_template = 'issues/ticket_notification_html.mail' + context = { + 'ticket': self, + 'ticket_message': message + } + send_email_template(template, context, emails, html=html_template) + + def save(self, *args, **kwargs): + """ notify stakeholders of new ticket """ + new_issue = not self.pk + super(Ticket, self).save(*args, **kwargs) + if new_issue: + # PK should be available for rendering the template + self.notify() + + def is_involved_by(self, user): + """ returns whether user has participated or is referenced on the ticket + as owner or member of the group + """ + return Ticket.objects.filter(pk=self.pk).involved_by(user).exists() + + def is_visible_by(self, user): + """ returns whether ticket is visible by user """ + return Ticket.objects.filter(pk=self.pk).visible_by(user).exists() + + def get_cc_emails(self): + return self.cc.split(',') if self.cc else [] + + def mark_as_read_by(self, user): + TicketTracker.objects.get_or_create(ticket=self, user=user) + + def mark_as_unread_by(self, user): + TicketTracker.objects.filter(ticket=self, user=user).delete() + + def mark_as_unread(self): + TicketTracker.objects.filter(ticket=self).delete() + + def is_read_by(self, user): + return TicketTracker.objects.filter(ticket=self, user=user).exists() + + def reject(self): + self.state = Ticket.REJECTED + self.save() + + def resolve(self): + self.state = Ticket.RESOLVED + self.save() + + def close(self): + self.state = Ticket.CLOSED + self.save() + + def take(self, user): + self.owner = user + self.save() + + +class Message(models.Model): + ticket = models.ForeignKey('issues.Ticket', verbose_name=_("ticket"), + related_name='messages') + author = models.ForeignKey(get_user_model(), verbose_name=_("author"), + related_name='ticket_messages') + content = models.TextField(_("content")) + created_on = models.DateTimeField(_("created on"), auto_now_add=True) + + def __unicode__(self): + return u"#%i" % self.id + + def save(self, *args, **kwargs): + """ notify stakeholders of ticket update """ + if not self.pk: + self.ticket.mark_as_unread() + self.ticket.notify(message=self) + super(Message, self).save(*args, **kwargs) + + @property + def num(self): + return self.ticket.messages.filter(id__lte=self.id).count() + + +class TicketTracker(models.Model): + """ Keeps track of user read tickets """ + ticket = models.ForeignKey(Ticket, verbose_name=_("ticket"), + related_name='trackers') + user = models.ForeignKey(get_user_model(), verbose_name=_("user"), + related_name='ticket_trackers') + + class Meta: + unique_together = (('ticket', 'user'),) diff --git a/orchestra/apps/issues/settings.py b/orchestra/apps/issues/settings.py new file mode 100644 index 00000000..642dd767 --- /dev/null +++ b/orchestra/apps/issues/settings.py @@ -0,0 +1,7 @@ +from django.conf import settings + + +ISSUES_SUPPORT_EMAILS = getattr(settings, 'ISSUES_SUPPORT_EMAILS', []) + + +ISSUES_NOTIFY_SUPERUSERS = getattr(settings, 'ISSUES_NOTIFY_SUPERUSERS', True) diff --git a/orchestra/apps/issues/static/issues/css/ticket-admin.css b/orchestra/apps/issues/static/issues/css/ticket-admin.css new file mode 100644 index 00000000..b52da12a --- /dev/null +++ b/orchestra/apps/issues/static/issues/css/ticket-admin.css @@ -0,0 +1,67 @@ +fieldset .field-box { + float: left; + margin-right: 20px; + width: 300px; +} + +hr { + background-color: #B6B6B6; +} + +h4 { + color: #666; +} + +form .field-display_description p, form .field-display_description ul { + margin-left: 0; + padding-left: 12px; +} + +form .field-display_description ul { + margin-left: 24px; +} + +ul li { + list-style-type: disc; + padding: 0; +} + +/*** messages format ***/ +#messages-group { + margin-bottom: 0; +} + +#messages-2-group { + margin-top: 0; +} + +#messages-2-group h2, #messages-2-group thead { + display: none; +} + +#id_messages-2-0-content { + width: 99%; +} + +/** ticket.description preview CSS overrides **/ +.content-preview { + border: 1px solid #ccc; + padding: 2px 5px; +} + +.aligned .content-preview p { + margin-left: 5px; + padding-left: 0; +} +.module .content-preview ol, +.module .content-preview ul { + margin-left: 5px; +} + +/** unread messages admin changelist **/ +strong.unread { + display: inline-block; + padding-left: 21px; + background: url(../images/unread_ticket.gif) no-repeat left; +} + diff --git a/orchestra/apps/issues/static/issues/images/btn_edit.gif b/orchestra/apps/issues/static/issues/images/btn_edit.gif new file mode 100644 index 0000000000000000000000000000000000000000..1a6f83c58c140f9b0c58c7077ae9fe65b39fee85 GIT binary patch literal 204 zcmZ?wbhEHb6l4%!I3muFmzVeJ*RSQvmp^&(||i|e4yHwk~!~6;@q@X4BFa3mz)?k_^=D6M2cSPGnm29I*H+;gp2BQ_vM=; nOl?_u+?q6nMHqzjxF3rCnYE_tD38Gj7yH)VzUwbLGgt!vkDXIH literal 0 HcmV?d00001 diff --git a/orchestra/apps/issues/static/issues/images/unread_ticket.gif b/orchestra/apps/issues/static/issues/images/unread_ticket.gif new file mode 100644 index 0000000000000000000000000000000000000000..62bc6ffd4b5c9bb3606a102ec7ad2460a96ca7df GIT binary patch literal 260 zcmV+f0sH<(Nk%w1VGsZi0K^{vb&{>L*!!u}{Exfi`uzRc?Dfmj z+J&jaZ=1rS(EFav`|I`oQeb(e%IulJ=0i(dk+jd5z}Aeg(J3)QK1Ef&-uLpu7M7|NsC0A^8LW0018VEC2ui01yBW000Gn;3tk`X`ZB~u57DrRYfgy2i$-G zsTJY>K!RfDp%5q + + + + +Markdown formatting + + + + +

    Markdown Syntax Quick Reference

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Font Styles
    **Strong**Strong
    _Italic_Italic
    > QuoteQuote
         4 or more spacesCode block
    Break Lines
    end a line with 2 or more spaces  first line
    new line
    type an empty line
     
    (or containing only spaces)
    first line
    new line
    Lists
    * Item 1
    * Item 2
    • Item 1
    • Item 2
    1. Item 1
    2. Item 2
    1. Item 1
    2. Item 2
    Headings
    # Title 1 #

    Title 1

    ## Title ##

    Title 2

    Links
    <http://foo.bar>http://foo.bar
    [link](http://foo.bar/)link
    [relative link](/about/)relative link
    + +

    + Full reference of markdown syntax. +

    + + + diff --git a/orchestra/apps/issues/templates/issues/ticket_notification.mail b/orchestra/apps/issues/templates/issues/ticket_notification.mail new file mode 100644 index 00000000..5e329672 --- /dev/null +++ b/orchestra/apps/issues/templates/issues/ticket_notification.mail @@ -0,0 +1,36 @@ +{% if subject %} +{% if not ticket_message %} +[{{ site.name }} - Issue #{{ ticket.pk }}] ({{ ticket.get_state_display }}) {{ ticket.subject }} +{% else %} +[{{ site.name }} - Issue #{{ ticket.pk }}] {% if '**State** changed' in ticket_message.content %}({{ ticket.get_state_display }}) {% endif %}{{ ticket.subject }} +{% endif %} +{% endif %} + +{% if message %} +{% if not ticket_message %} +Issue #{{ ticket.id }} has been reported by {{ ticket.created_by }}. +{% else %} +Issue #{{ ticket.id }} has been updated by {{ ticket_message.author }}. +{% autoescape off %} +{{ ticket_message.content }} +{% endautoescape %} +{% endif %} +----------------------------------------------------------------- +Issue #{{ ticket.pk }}: {{ ticket.subject }} + + * Author: {{ ticket.created_by }} + * Status: {{ ticket.get_state_display }} + * Priority: {{ ticket.get_priority_display }} + * Visibility: {{ ticket.get_visibility_display }} + * Group: {% if ticket.group %}{{ ticket.group }}{% endif %} + * Assigned to: {% if ticket.owner %}{{ ticket.owner }}{% endif %} + * Queue: {{ ticket.queue }} + +{% autoescape off %} +{{ ticket.description }} +{% endautoescape %} +----------------------------------------------------------------- +You have received this notification because you have either subscribed to it, or are involved in it. +To change your notification preferences, please visit: {{ site.scheme }}://{{ site.domain }}{% url 'admin:issues_ticket_change' ticket.id %} +{% endif %} + diff --git a/orchestra/apps/issues/templates/issues/ticket_notification_html.mail b/orchestra/apps/issues/templates/issues/ticket_notification_html.mail new file mode 100644 index 00000000..8c801fc1 --- /dev/null +++ b/orchestra/apps/issues/templates/issues/ticket_notification_html.mail @@ -0,0 +1,60 @@ +{% load markdown %} + +{% if message %} + + + + + +{% if not ticket_message %} +Issue #{{ ticket.id }} has been reported by {{ ticket.created_by }}. +{% else %} +Issue #{{ ticket.id }} has been updated by {{ ticket_message.author }}. +{% autoescape off %} +{{ ticket_message.content|markdown }} +{% endautoescape %} +{% endif %} +
    +

    Issue #{{ ticket.pk }}: {{ ticket.subject }}

    + +
      +
    • Author: {{ ticket.created_by }}
    • +
    • Status: {{ ticket.get_state_display }}
    • +
    • Priority: {{ ticket.get_priority_display }}
    • +
    • Visibility: {{ ticket.get_visibility_display }}
    • +
    • Group: {% if ticket.group %}{{ ticket.group }}{% endif %}
    • +
    • Assigned to: {% if ticket.owner %}{{ ticket.owner }}{% endif %}
    • +
    • Queue: {{ ticket.queue }}
    • +
    +{% autoescape off %} +{{ ticket.description|markdown }} +{% endautoescape %} +
    +

    You have received this notification because you have either subscribed to it, or are involved in it.
    +To change your notification preferences, please click here: {{ site.scheme }}://{{ site.domain }}{% url 'admin:issues_ticket_change' ticket.id %}

    + + +{% endif %} diff --git a/orchestra/apps/issues/tests.py b/orchestra/apps/issues/tests.py new file mode 100644 index 00000000..501deb77 --- /dev/null +++ b/orchestra/apps/issues/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/orchestra/apps/lists/__init__.py b/orchestra/apps/lists/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/orchestra/apps/lists/admin.py b/orchestra/apps/lists/admin.py new file mode 100644 index 00000000..dfcf2e54 --- /dev/null +++ b/orchestra/apps/lists/admin.py @@ -0,0 +1,60 @@ +from django.contrib import admin +from django.conf.urls import patterns +from django.contrib.auth.admin import UserAdmin +from django.utils.translation import ugettext, ugettext_lazy as _ + +from orchestra.admin import ExtendedModelAdmin +from orchestra.admin.utils import link +from orchestra.apps.accounts.admin import SelectAccountAdminMixin + +from .forms import ListCreationForm, ListChangeForm +from .models import List + + +class ListAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): + list_display = ('name', 'address_name', 'address_domain_link', 'account_link') + add_fieldsets = ( + (None, { + 'classes': ('wide',), + 'fields': ('account_link', 'name',) + }), + (_("Address"), { + 'classes': ('wide',), + 'fields': (('address_name', 'address_domain'),) + }), + (_("Admin"), { + 'classes': ('wide',), + 'fields': ('admin_email', 'password1', 'password2'), + }), + ) + fieldsets = ( + (None, { + 'classes': ('wide',), + 'fields': ('account_link', 'name',) + }), + (_("Address"), { + 'classes': ('wide',), + 'fields': (('address_name', 'address_domain'),) + }), + (_("Admin"), { + 'classes': ('wide',), + 'fields': ('admin_email', 'password',), + }), + ) + readonly_fields = ('account_link',) + change_readonly_fields = ('name',) + form = ListChangeForm + add_form = ListCreationForm + filter_by_account_fields = ['address_domain'] + + address_domain_link = link('address_domain', order='address_domain__name') + + def get_urls(self): + useradmin = UserAdmin(List, self.admin_site) + return patterns('', + (r'^(\d+)/password/$', + self.admin_site.admin_view(useradmin.user_change_password)) + ) + super(ListAdmin, self).get_urls() + + +admin.site.register(List, ListAdmin) diff --git a/orchestra/apps/lists/api.py b/orchestra/apps/lists/api.py new file mode 100644 index 00000000..d8061893 --- /dev/null +++ b/orchestra/apps/lists/api.py @@ -0,0 +1,16 @@ +from rest_framework import viewsets + +from orchestra.api import router, SetPasswordApiMixin +from orchestra.apps.accounts.api import AccountApiMixin + +from .models import List +from .serializers import ListSerializer + + +class ListViewSet(AccountApiMixin, SetPasswordApiMixin, viewsets.ModelViewSet): + model = List + serializer_class = ListSerializer + filter_fields = ('name',) + + +router.register(r'lists', ListViewSet) diff --git a/orchestra/apps/lists/backends.py b/orchestra/apps/lists/backends.py new file mode 100644 index 00000000..368780f3 --- /dev/null +++ b/orchestra/apps/lists/backends.py @@ -0,0 +1,11 @@ +from django.template import Template, Context + +from orchestra.apps.orchestration import ServiceBackend + + +class MailmanBackend(ServiceBackend): + verbose_name = "Mailman" + model = 'lists.List' + + def save(self, mailinglist): + pass diff --git a/orchestra/apps/lists/forms.py b/orchestra/apps/lists/forms.py new file mode 100644 index 00000000..828b15bc --- /dev/null +++ b/orchestra/apps/lists/forms.py @@ -0,0 +1,51 @@ +from django import forms +from django.utils.translation import ugettext_lazy as _ + +from orchestra.core.validators import validate_password +from orchestra.forms.widgets import ReadOnlyWidget + + +class CleanAddressMixin(object): + def clean_address_domain(self): + name = self.cleaned_data.get('address_name') + domain = self.cleaned_data.get('address_domain') + if name and not domain: + msg = _("Domain should be selected for provided address name") + raise forms.ValidationError(msg) + elif not name and domain: + msg = _("Address name should be provided for this selected domain") + raise forms.ValidationError(msg) + return domain + + +class ListCreationForm(CleanAddressMixin, forms.ModelForm): + password1 = forms.CharField(label=_("Password"), + widget=forms.PasswordInput) + password2 = forms.CharField(label=_("Password confirmation"), + widget=forms.PasswordInput, + help_text=_("Enter the same password as above, for verification.")) + + def __init__(self, *args, **kwargs): + super(ListAdminForm, self).__init__(*args, **kwargs) + self.fields['password1'].validators.append(validate_password) + + def clean_password2(self): + password1 = self.cleaned_data.get("password1") + password2 = self.cleaned_data.get("password2") + if password1 and password2 and password1 != password2: + msg = _("The two password fields didn't match.") + raise forms.ValidationError(msg) + return password2 + + def save(self, commit=True): + obj = super(ListAdminForm, self).save(commit=commit) + obj.set_password(self.cleaned_data["password1"]) + return obj + + +class ListChangeForm(CleanAddressMixin, forms.ModelForm): + password = forms.CharField(label=_("Password"), + widget=ReadOnlyWidget('Unknown password'), + help_text=_("List passwords are not stored, so there is no way to see this " + "list's password, but you can change the password using " + "this form.")) diff --git a/orchestra/apps/lists/models.py b/orchestra/apps/lists/models.py new file mode 100644 index 00000000..410e10fd --- /dev/null +++ b/orchestra/apps/lists/models.py @@ -0,0 +1,35 @@ +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from orchestra.core import services +from orchestra.core.validators import validate_name + +from . import settings + + +class List(models.Model): + name = models.CharField(_("name"), max_length=128, unique=True, + validators=[validate_name]) + address_name = models.CharField(_("address name"), max_length=128, + validators=[validate_name], blank=True) + address_domain = models.ForeignKey(settings.LISTS_DOMAIN_MODEL, + verbose_name=_("address domain"), blank=True, null=True) + admin_email = models.EmailField(_("admin email"), + help_text=_("Administration email address")) + account = models.ForeignKey('accounts.Account', verbose_name=_("Account"), + related_name='lists') + + class Meta: + unique_together = ('address_name', 'address_domain') + + def __unicode__(self): + return "%s@%s" % (self.address_name, self.address_domain) + + def get_username(self): + return self.name + + def set_password(self, password): + self.password = password + + +services.register(List) diff --git a/orchestra/apps/lists/serializers.py b/orchestra/apps/lists/serializers.py new file mode 100644 index 00000000..a92629f0 --- /dev/null +++ b/orchestra/apps/lists/serializers.py @@ -0,0 +1,11 @@ +from rest_framework import serializers + +from orchestra.apps.accounts.serializers import AccountSerializerMixin + +from .models import List + + +class ListSerializer(AccountSerializerMixin, serializers.ModelSerializer): + class Meta: + model = List + fields = ('name', 'address_name', 'address_domain',) diff --git a/orchestra/apps/lists/settings.py b/orchestra/apps/lists/settings.py new file mode 100644 index 00000000..46548933 --- /dev/null +++ b/orchestra/apps/lists/settings.py @@ -0,0 +1,8 @@ +from django.conf import settings + + +# Data access + +LISTS_DOMAIN_MODEL = getattr(settings, 'LISTS_DOMAIN_MODEL', 'domains.Domain') + +LISTS_DEFAULT_DOMAIN = getattr(settings, 'LIST_DEFAULT_DOMAIN', 'grups.orchestra.lan') diff --git a/orchestra/apps/orchestration/README.md b/orchestra/apps/orchestration/README.md new file mode 100644 index 00000000..da635563 --- /dev/null +++ b/orchestra/apps/orchestration/README.md @@ -0,0 +1,88 @@ +# Orchestration + +This module handles the management of the services controlled by Orchestra. + +Orchestration module has the following pieces: + +* `Operation` encapsulates an operation, storing the related object, the action and the backend +* `OperationsMiddleware` collects and executes all save and delete operations, more on [next section](#operationsmiddleware) +* `manager` it manage the execution of the operations +* `backends` defines the logic that will be executed on the servers in order to control a particular service +* `router` determines in which server an operation should be executed +* `Server` defines a server hosting services +* `methods` script execution methods, e.g. SSH +* `ScriptLog` it logs the script execution + +Routes +====== + +This application provides support for mapping services to server machines accross the network. + +It supports _routing_ based on Python expression, which means that you can efectively +control services that are distributed accross several machines. For example, different +websites that are distributed accross _n_ web servers on a _shared hosting_ +environment. + +### OperationsMiddleware + +When enabled, `middlewares.OperationsMiddleware` automatically executes the service backends when a change on the data model occurs. The main steps that performs are: + +1. Collect all `save` and `delete` model signals triggered on each HTTP request +2. Find related backends using the routing backend +3. Generate a single script per server (_unit of work_) +4. Execute the scripts on the servers + + +### Service Management Properties + +We can identify three different characteristics regarding service management: + +* **Authority**: Whether or not Orchestra is the only source of the service configuration. When Orchestra is the authority then service configuration is _completely generated_ from the Orchestra database (or services are configured to read their configuration directly from Orchestra database). Otherwise Orchestra will execute small tasks translating model changes into configuration changes, allowing manual configurations to be preserved. +* **Flow**: _push_, when Orchestra drives the execution or _pull_, when external services connects to Orchestra. +* **Execution**: _synchronous_, when the execution blocks the HTTP request, or _asynchronous_ when it doesn't. Asynchronous execution means concurrency, and concurrency scalability. + + +_Sorry for the bad terminology, I was not able to find more appropriate terms on the literature._ + + +### Registry vs Synchronization vs Task +From the above management properties we can extract three main service management strategies: (a) _registry based management_, (b) _synchronization based management_ and (c) _task based management_. Orchestra provides support for all of them, it is left to you to decide which one suits your requirements better. + +Following a brief description and evaluation of the tradeoffs to help on your decision making. + + +#### a. Registry Based Management +When Orchestra acts as a pure **configuration registry (authority)**, doing nothing more than store service's configuration on the database. The configuration is **pulled** from Orchestra by the servers themselves, so it is **asynchronous** by nature. + +This strategy considers two different implementations: + +- The service is configured to read the configuration directly from Orchestra database (or REST API). This approach simplifies configuration management but also can make Orchestra a single point of failure on your architecture. +- A client-side application periodically fetches the service configuration from the Orchestra database and regenerates the service configuration files. This approach is very tolerant to failures, since the services will keep on working, and the new configuration will be applied after recovering. A delay may occur until the changes are applied to the services (_eventual consistency_), but it can be mitigated by notifying the application when a relevant change occur. + + +#### b. Synchronization Based Management +When Orchestra is the configuration **authority** and also _the responsible of applying the changes_ on the servers (**push** flow). The configuration files are **regenerated** every time by Orchestra, deleting any existing manual configuration. This model is very consistent since it only depends on the current state of the system (_stateless_). Therefore, it makes sense to execute the synchronization operation in **asynchronous** fashion. + +In contrast to registry based management, synchronization management is _fully centralized_, all the management operations are driven by Orchestra so you don't need to install nor configure anything on your servers. + + + +#### c. Task Based Management +This model refers when Orchestra is _not the only source of configuration_. Therefore, Orchestra translates isolated data model changes directly into localized changes on the service configuration, and executing them using a **push** strategy. For example `save()` or `delete()` object-level operations may have sibling configuration management operations. In contrast to synchronization, tasks are able to preserve configuration not performed by Orchestra. + +This model is intuitive, efficient and also very consistent when tasks are execute **synchronously** with the request/response cycle. However, **asynchronous** task execution can have _consistency issues_; tasks have state, and this state can be lost when: +- A failure occur while applying some changes, e.g. network error or worker crash while deleting a service +- Scripts are executed out of order, e.g. create and delete a service is applied in inverse order + +In general, _synchornous execution of tasks is preferred_ over asynchornous, unless response delays are not tolerable. + +##### What state does actually mean? +Lets assume you have deleted a mailbox, and Orchestra has created an script that deletes that mailbox on the mail server. However a failure has occurred and the mailbox deletion task has been lost. Since the state has also been lost it is not easy to tell what to do now in order to maintain consistency. + + +### Additional Notes +* The script that manage the service needs to be idempotent, i.e. the outcome of running the script is always the same, no matter how many times it is executed. + +* Renaming of attributes may lead to undesirable effects, e.g. changing a database name will create a new database rather than just changing its name. + +* The system does not magically perform data migrations between servers when its _route_ has changed diff --git a/orchestra/apps/orchestration/__init__.py b/orchestra/apps/orchestration/__init__.py new file mode 100644 index 00000000..49bb7f9d --- /dev/null +++ b/orchestra/apps/orchestration/__init__.py @@ -0,0 +1 @@ +from .backends import ServiceBackend diff --git a/orchestra/apps/orchestration/admin.py b/orchestra/apps/orchestration/admin.py new file mode 100644 index 00000000..eb3576db --- /dev/null +++ b/orchestra/apps/orchestration/admin.py @@ -0,0 +1,125 @@ +from django.contrib import admin +from django.core.urlresolvers import reverse +from django.utils.html import escape +from django.utils.translation import ugettext_lazy as _ + +from orchestra.admin.html import monospace_format +from orchestra.admin.utils import link + +from .models import Server, Route, BackendLog, BackendOperation + + +STATE_COLORS = { + BackendLog.RECEIVED: 'darkorange', + BackendLog.TIMEOUT: 'red', + BackendLog.STARTED: 'blue', + BackendLog.SUCCESS: 'green', + BackendLog.FAILURE: 'red', + BackendLog.ERROR: 'red', + BackendLog.REVOKED: 'magenta', +} + + +class RouteAdmin(admin.ModelAdmin): + list_display = [ + 'id', 'backend', 'host', 'match', 'display_model', 'is_active' + ] + list_editable = ['backend', 'host', 'match', 'is_active'] + list_filter = ['backend', 'host', 'is_active'] + + def display_model(self, route): + try: + return route.get_backend().model + except KeyError: + return "NOT AVAILABLE" + display_model.short_description = _("model") + display_model.allow_tags = True + + +class BackendOperationInline(admin.TabularInline): + model = BackendOperation + fields = ('action', 'instance_link') + readonly_fields = ('action', 'instance_link') + extra = 0 + can_delete = False + + class Media: + css = { + 'all': ('orchestra/css/hide-inline-id.css',) + } + + def instance_link(self, operation): + try: + return link('instance')(self, operation) + except: + return _("deleted %s %s") % (operation.content_type, operation.object_id) + instance_link.allow_tags = True + instance_link.short_description = _("Instance") + + def has_add_permission(self, *args, **kwargs): + return False + + +class BackendLogAdmin(admin.ModelAdmin): + list_display = ( + 'id', 'backend', 'server_link', 'display_state', 'exit_code', 'created', + 'execution_time', + ) + list_display_links = ('id', 'backend') + list_filter = ('state', 'backend') + date_hierarchy = 'last_update' + inlines = [BackendOperationInline] + fields = [ + 'backend', 'server', 'state', 'mono_script', 'mono_stdout', 'mono_stderr', + 'mono_traceback', 'exit_code', 'task_id', 'created', 'last_update', + 'execution_time' + ] + readonly_fields = [ + 'backend', 'server', 'state', 'mono_script', 'mono_stdout', 'mono_stderr', + 'mono_traceback', 'exit_code', 'task_id', 'created', 'last_update', + 'execution_time' + ] + + def server_link(self, log): + url = reverse('admin:orchestration_server_change', args=(log.server.pk,)) + return '%s' % (url, log.server.name) + server_link.short_description = _("server") + server_link.allow_tags = True + + def display_state(self, log): + color = STATE_COLORS.get(log.state, 'grey') + return '%s' % (color, log.state) + display_state.short_description = _("state") + display_state.allow_tags = True + display_state.admin_order_field = 'state' + + def mono_script(self, log): + return monospace_format(escape(log.script)) + mono_script.short_description = _("script") + + def mono_stdout(self, log): + return monospace_format(escape(log.stdout)) + mono_stdout.short_description = _("stdout") + + def mono_stderr(self, log): + return monospace_format(escape(log.stderr)) + mono_stderr.short_description = _("stderr") + + def mono_traceback(self, log): + return monospace_format(escape(log.traceback)) + mono_traceback.short_description = _("traceback") + + def queryset(self, request): + """ Order by structured name and imporve performance """ + qs = super(BackendLogAdmin, self).queryset(request) + return qs.select_related('server') + + +class ServerAdmin(admin.ModelAdmin): + list_display = ('name', 'address', 'os') + list_filter = ('os',) + + +admin.site.register(Server, ServerAdmin) +admin.site.register(BackendLog, BackendLogAdmin) +admin.site.register(Route, RouteAdmin) diff --git a/orchestra/apps/orchestration/backends.py b/orchestra/apps/orchestration/backends.py new file mode 100644 index 00000000..2f79cabf --- /dev/null +++ b/orchestra/apps/orchestration/backends.py @@ -0,0 +1,104 @@ +from datetime import datetime +from functools import partial + +from django.utils.translation import ugettext_lazy as _ + +from orchestra.utils import plugins + +from . import methods + + +class ServiceBackend(object): + """ + Service management backend base class + + It uses the _unit of work_ design principle, which allows bulk operations to + be conviniently supported. Each backend generates the configuration for all + the changes of all modified objects, reloading the daemon just once. + """ + verbose_name = None + model = None + related_models = () # ((model, accessor__attribute),) + script_method = methods.BashSSH + function_method = methods.Python + type = 'task' # 'sync' + ignore_fields = [] + + # TODO type: 'script', execution:'task' + + __metaclass__ = plugins.PluginMount + + def __unicode__(self): + return type(self).__name__ + + def __str__(self): + return unicode(self) + + def __init__(self): + self.cmds = [] + + @classmethod + def get_name(cls): + return cls.__name__ + + @classmethod + def is_main(cls, obj): + opts = obj._meta + return cls.model == '%s.%s' % (opts.app_label, opts.object_name) + + @classmethod + def get_related(cls, obj): + opts = obj._meta + model = '%s.%s' % (opts.app_label, opts.object_name) + for rel_model, field in cls.related_models: + if rel_model == model: + related = obj + for attribute in field.split('__'): + related = getattr(related, attribute) + return related + return None + + @classmethod + def get_backends(cls): + return cls.plugins + + @classmethod + def get_choices(cls): + backends = cls.get_backends() + choices = ( (b.get_name(), b.verbose_name or b.get_name()) for b in backends ) + return sorted(choices, key=lambda e: e[1]) + + def get_banner(self): + time = datetime.now().strftime("%h %d, %Y %I:%M:%S") + return "Generated by Orchestra %s" % time + + def execute(self, server): + from .models import BackendLog + state = BackendLog.STARTED if self.cmds else BackendLog.SUCCESS + log = BackendLog.objects.create(backend=self.get_name(), state=state, server=server) + for method, cmds in self.cmds: + method(log, server, cmds) + if log.state != BackendLog.SUCCESS: + break + return log + + def append(self, *cmd): + # aggregate commands acording to its execution method + if isinstance(cmd[0], basestring): + method = self.script_method + cmd = cmd[0] + else: + method = self.function_method + cmd = partial(*cmd) + if not self.cmds or self.cmds[-1][0] != method: + self.cmds.append((method, [cmd])) + else: + self.cmds[-1][1].append(cmd) + + def commit(self): + """ + apply the configuration, usually reloading a service + reloading a service is done in a separated method in order to reload + the service once in bulk operations + """ + pass diff --git a/orchestra/apps/orchestration/helpers.py b/orchestra/apps/orchestration/helpers.py new file mode 100644 index 00000000..6196e4e8 --- /dev/null +++ b/orchestra/apps/orchestration/helpers.py @@ -0,0 +1,46 @@ +from django.contrib import messages +from django.core.mail import mail_admins +from django.utils.html import escape +from django.utils.translation import ugettext_lazy as _ + + +def send_report(method, args, log): + backend = method.im_class().get_name() + server = args[0] + subject = '[Orchestra] %s execution %s on %s' + subject = subject % (backend, log.state, server) + separator = "\n%s\n\n" % ('~ '*40,) + message = separator.join([ + "[EXIT CODE] %s" % log.exit_code, + "[STDERR]\n%s" % log.stderr, + "[STDOUT]\n%s" % log.stdout, + "[SCRIPT]\n%s" % log.script, + "[TRACEBACK]\n%s" % log.traceback, + ]) + html_message = '\n\n'.join([ + '

    Exit code %s

    ' % log.exit_code, + '

    Stderr

    ' + '
    %s
    ' % escape(log.stderr), + '

    Stdout

    ' + '
    %s
    ' % escape(log.stdout), + '

    Script

    ' + '
    %s
    ' % escape(log.script), + '

    Traceback

    ' + '
    %s
    ' % escape(log.traceback), + ]) + mail_admins(subject, message, html_message=html_message) + + +def message_user(request, logs): + total = len(logs) + successes = [ log for log in logs if log.state == log.SUCCESS ] + successes = len(successes) + errors = total-successes + if errors: + msg = 'backends have' if errors > 1 else 'backend has' + msg = _("%d out of %d {0} fail to executed".format(msg)) + messages.warning(request, msg % (errors, total)) + else: + msg = 'backends have' if successes > 1 else 'backend has' + msg = _("%d {0} been successfully executed".format(msg)) + messages.success(request, msg % successes) diff --git a/orchestra/apps/orchestration/manager.py b/orchestra/apps/orchestration/manager.py new file mode 100644 index 00000000..ff8aee30 --- /dev/null +++ b/orchestra/apps/orchestration/manager.py @@ -0,0 +1,73 @@ +import threading + +from django import db + +from . import settings +from .helpers import send_report + + +def get_router(): + module = '.'.join(settings.ORCHESTRATION_ROUTER.split('.')[:-1]) + cls = settings.ORCHESTRATION_ROUTER.split('.')[-1] + module = __import__(module, fromlist=[module]) + return getattr(module, cls) + + +def as_task(execute): + def wrapper(*args, **kwargs): + with db.transaction.commit_manually(): + log = execute(*args, **kwargs) + db.transaction.commit() + if log.state != log.SUCCESS: + send_report(execute, args, log) + return log + return wrapper + + +def close_connection(execute): + """ Threads have their own connection pool, closing it when finishing """ + # TODO rewrite as context manager + def wrapper(*args, **kwargs): + log = execute(*args, **kwargs) + db.connection.close() + # Using the wrapper function as threader messenger for the execute output + wrapper.log = log + return wrapper + + +def execute(operations): + """ generates and executes the operations on the servers """ + router = get_router() + # Generate scripts per server+backend + scripts = {} + for operation in operations: + servers = router.get_servers(operation) + for server in servers: + key = (server, operation.backend) + if key not in scripts: + scripts[key] = (operation.backend(), [operation]) + else: + scripts[key][1].append(operation) + method = getattr(scripts[key][0], operation.action) + method(operation.instance) + # Execute scripts on each server + threads = [] + executions = [] + for key, value in scripts.iteritems(): + server, __ = key + backend, operations = value + backend.commit() + execute = as_task(backend.execute) + execute = close_connection(execute) + thread = threading.Thread(target=execute, args=(server,)) + thread.start() + threads.append(thread) + executions.append((execute, operations)) + [ thread.join() for thread in threads ] + logs = [] + for execution, operations in executions: + for operation in operations: + operation.log = execution.log + operation.save() + logs.append(execution.log) + return logs diff --git a/orchestra/apps/orchestration/methods.py b/orchestra/apps/orchestration/methods.py new file mode 100644 index 00000000..8ac97a80 --- /dev/null +++ b/orchestra/apps/orchestration/methods.py @@ -0,0 +1,102 @@ +import hashlib +import json +import os +import socket +import sys +import select + +import paramiko +from celery.datastructures import ExceptionInfo + +from . import settings + + +def BashSSH(backend, log, server, cmds): + from .models import BackendLog + script = '\n\n'.join(['set -e'] + cmds + ['exit 0']) + script = script.replace('\r', '') + log.script = script + log.save() + + try: + # In order to avoid "Argument list too long" we while generate first a + # script file, then scp the escript and safely execute in remote + digest = hashlib.md5(script).hexdigest() + path = os.path.join(settings.ORCHESTRATION_TEMP_SCRIPT_PATH, digest) + with open(path, 'w') as script_file: + script_file.write(script) + # ssh connection + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + addr = server.get_address() + try: + ssh.connect(addr, username='root', + key_filename=settings.ORCHESTRATION_SSH_KEY_PATH) + except socket.error: + log.state = BackendLog.TIMEOUT + log.save() + return + transport = ssh.get_transport() + channel = transport.open_session() + + sftp = paramiko.SFTPClient.from_transport(transport) + sftp.put(path, path) + sftp.close() + os.remove(path) + + context = { + 'path': path, + 'digest': digest + } + cmd = ( + "[[ $(md5sum %(path)s|awk {'print $1'}) == %(digest)s ]] && bash %(path)s\n" + "RETURN_CODE=$?\n" +# "rm -fr %(path)s\n" + "exit $RETURN_CODE" % context + ) + channel.exec_command(cmd) + if True: # TODO if not async + log.stdout += channel.makefile('rb', -1).read().decode('utf-8') + log.stderr += channel.makefile_stderr('rb', -1).read().decode('utf-8') + else: + while True: + # Non-blocking is the secret ingridient in the async sauce + select.select([channel], [], []) + if channel.recv_ready(): + log.stdout += channel.recv(1024) + if channel.recv_stderr_ready(): + log.stderr += channel.recv_stderr(1024) + log.save() + if channel.exit_status_ready(): + break + log.exit_code = exit_code = channel.recv_exit_status() + log.state = BackendLog.SUCCESS if exit_code == 0 else BackendLog.FAILURE + channel.close() + ssh.close() + log.save() + except: + log.state = BackendLog.ERROR + log.traceback = ExceptionInfo(sys.exc_info()).traceback + log.save() + + +def Python(backend, log, server, cmds): + from .models import BackendLog + script = [ str(cmd.func.func_name) + str(cmd.args) for cmd in cmds ] + script = json.dumps(script, indent=4).replace('"', '') + log.script = '\n'.join([log.script, script]) + log.save() + stdout = '' + try: + for cmd in cmds: + result = cmd(server) + stdout += str(result) + except: + log.exit_code = 1 + log.state = BackendLog.FAILURE + log.traceback = ExceptionInfo(sys.exc_info()).traceback + else: + log.exit_code = 0 + log.state = BackendLog.SUCCESS + log.stdout += stdout + log.save() diff --git a/orchestra/apps/orchestration/middlewares.py b/orchestra/apps/orchestration/middlewares.py new file mode 100644 index 00000000..cd86cb1a --- /dev/null +++ b/orchestra/apps/orchestration/middlewares.py @@ -0,0 +1,96 @@ +import copy +from threading import local + +from django.db.models.signals import pre_delete, post_save +from django.dispatch import receiver +from django.http.response import HttpResponseServerError +from orchestra.utils.python import OrderedSet + +from .backends import ServiceBackend +from .helpers import message_user +from .models import BackendLog +from .models import BackendOperation as Operation + + +@receiver(post_save) +def post_save_collector(sender, *args, **kwargs): + if sender != BackendLog: + OperationsMiddleware.collect(Operation.SAVE, **kwargs) + +@receiver(pre_delete) +def pre_delete_collector(sender, *args, **kwargs): + if sender != BackendLog: + OperationsMiddleware.collect(Operation.DELETE, **kwargs) + + +class OperationsMiddleware(object): + """ + Stores all the operations derived from save and delete signals and executes them + at the end of the request/response cycle + """ + # Thread local is used because request object is not available on model signals + thread_locals = local() + + @classmethod + def get_pending_operations(cls): + # Check if an error poped up before OperationsMiddleware.process_request() + if hasattr(cls.thread_locals, 'request'): + request = cls.thread_locals.request + if not hasattr(request, 'pending_operations'): + request.pending_operations = OrderedSet() + return request.pending_operations + return set() + + @classmethod + def collect(cls, action, **kwargs): + """ Collects all pending operations derived from model signals """ + request = getattr(cls.thread_locals, 'request', None) + if request is None: + return + pending_operations = cls.get_pending_operations() + for backend in ServiceBackend.get_backends(): + instance = None + if backend.is_main(kwargs['instance']): + instance = kwargs['instance'] + else: + candidate = backend.get_related(kwargs['instance']) + if candidate: + delete = Operation.create(backend, candidate, Operation.DELETE) + if delete not in pending_operations: + instance = candidate + # related objects with backend.model trigger save() + action = Operation.SAVE + if instance is not None: + # Prevent creating a deleted instance by deleting existing saves + if action == Operation.DELETE: + save = Operation.create(backend, instance, Operation.SAVE) + try: + pending_operations.remove(save) + except KeyError: + pass + else: + update_fields = kwargs.get('update_fields', None) + if update_fields: + append = False + for field in kwargs.get('update_fields', [None]): + if field not in backend.ignore_fields: + append = True + break + if not append: + continue + instance = copy.copy(instance) + pending_operations.add(Operation.create(backend, instance, action)) + + def process_request(self, request): + """ Store request on a thread local variable """ + type(self).thread_locals.request = request + + def process_response(self, request, response): + """ Processes pending backend operations """ + if not isinstance(response, HttpResponseServerError): + operations = type(self).get_pending_operations() + if operations: + logs = Operation.execute(operations) + if logs: + message_user(request, logs) + return response diff --git a/orchestra/apps/orchestration/models.py b/orchestra/apps/orchestration/models.py new file mode 100644 index 00000000..255aad50 --- /dev/null +++ b/orchestra/apps/orchestration/models.py @@ -0,0 +1,179 @@ +from django.contrib.contenttypes import generic +from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import ValidationError +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from orchestra.utils.apps import autodiscover + +from . import settings, manager +from .backends import ServiceBackend + + +class Server(models.Model): + """ Machine runing daemons (services) """ + name = models.CharField(_("name"), max_length=256, unique=True) + # TODO unique address with blank=True (nullablecharfield) + address = models.CharField(_("address"), max_length=256, blank=True, + help_text=_("IP address or domain name")) + description = models.TextField(_("description"), blank=True) + os = models.CharField(_("operative system"), max_length=32, + choices=settings.ORCHESTRATION_OS_CHOICES, + default=settings.ORCHESTRATION_DEFAULT_OS) + + def __unicode__(self): + return self.name + + def get_address(self): + if self.address: + return self.address + return self.name + + +class BackendLog(models.Model): + RECEIVED = 'RECEIVED' + TIMEOUT = 'TIMEOUT' + STARTED = 'STARTED' + SUCCESS = 'SUCCESS' + FAILURE = 'FAILURE' + ERROR = 'ERROR' + REVOKED = 'REVOKED' + + STATES = ( + (RECEIVED, RECEIVED), + (TIMEOUT, TIMEOUT), + (STARTED, STARTED), + (SUCCESS, SUCCESS), + (FAILURE, FAILURE), + (ERROR, ERROR), + (REVOKED, REVOKED), + ) + + backend = models.CharField(_("backend"), max_length=256) + state = models.CharField(_("state"), max_length=16, choices=STATES, + default=RECEIVED) + server = models.ForeignKey(Server, verbose_name=_("server"), + related_name='execution_logs') + script = models.TextField(_("script")) + stdout = models.TextField() + stderr = models.TextField() + traceback = models.TextField(_("traceback")) + exit_code = models.IntegerField(_("exit code"), null=True) + task_id = models.CharField(_("task ID"), max_length=36, unique=True, null=True, + help_text="Celery task ID") + created = models.DateTimeField(_("created"), auto_now_add=True) + last_update = models.DateTimeField(_("last update"), auto_now=True) + + class Meta: + get_latest_by = 'created' + + @property + def execution_time(self): + return (self.last_update-self.created).total_seconds() + + +class BackendOperation(models.Model): + """ + Encapsulates an operation, storing its related object, the action and the backend. + """ + SAVE = 'save' + DELETE = 'delete' + ACTIONS = ( + (SAVE, _("save")), + (DELETE, _("delete")), + ) + + log = models.ForeignKey('orchestration.BackendLog', related_name='operations') + backend_class = models.CharField(_("backend"), max_length=256) + action = models.CharField(_("action"), max_length=64, choices=ACTIONS) + content_type = models.ForeignKey(ContentType) + object_id = models.PositiveIntegerField() + instance = generic.GenericForeignKey('content_type', 'object_id') + + class Meta: + verbose_name = _("Operation") + verbose_name_plural = _("Operations") + + def __unicode__(self): + return '%s.%s(%s)' % (self.backend_class, self.action, self.instance) + + def __hash__(self): + """ set() """ + backend = getattr(self, 'backend', self.backend_class) + return hash(backend) + hash(self.instance) + hash(self.action) + + def __eq__(self, operation): + """ set() """ + return hash(self) == hash(operation) + + @classmethod + def create(cls, backend, instance, action): + op = cls(backend_class=backend.get_name(), instance=instance, action=action) + op.backend = backend + return op + + @classmethod + def execute(cls, operations): + return manager.execute(operations) + + +autodiscover('backends') + + +class Route(models.Model): + """ + Defines the routing that determine in which server a backend is executed + """ + backend = models.CharField(_("backend"), max_length=256, + choices=ServiceBackend.get_choices()) + host = models.ForeignKey(Server, verbose_name=_("host")) + match = models.CharField(_("match"), max_length=256, blank=True, default='True', + help_text=_("Python expression used for selecting the targe host, " + "instance referes to the current object.")) +# async = models.BooleanField(default=False) +# method = models.CharField(_("method"), max_lenght=32, choices=method_choices, +# default=MethodBackend.get_default()) + is_active = models.BooleanField(_("is active"), default=True) + + class Meta: + unique_together = ('backend', 'host') + + def __unicode__(self): + return "%s@%s" % (self.backend, self.host) + +# def clean(self): +# backend, method = self.get_backend_class(), self.get_method_class() +# if not backend.type in method.types: +# msg = _("%s backend is not compatible with %s method") +# raise ValidationError(msg % (self.backend, self.method) + + @classmethod + def get_servers(cls, operation): + backend_name = operation.backend.get_name() + try: + routes = cls.objects.filter(is_active=True, backend=backend_name) + except cls.DoesNotExist: + return [] + safe_locals = { 'instance': operation.instance } + pks = [ route.pk for route in routes.all() if eval(route.match, safe_locals) ] + return [ route.host for route in routes.filter(pk__in=pks) ] + + def get_backend(self): + for backend in ServiceBackend.get_backends(): + if backend.get_name() == self.backend: + return backend + raise KeyError('This backend is not registered') + +# def get_method_class(self): +# for method in MethodBackend.get_backends(): +# if method.get_name() == self.method: +# return method +# raise ValueError('This method is not registered') + + def enable(self): + self.is_active = True + self.save() + + def disable(self): + self.is_active = False + self.save() diff --git a/orchestra/apps/orchestration/settings.py b/orchestra/apps/orchestration/settings.py new file mode 100644 index 00000000..1a2d0b45 --- /dev/null +++ b/orchestra/apps/orchestration/settings.py @@ -0,0 +1,21 @@ +from os import path + +from django.conf import settings + + +ORCHESTRATION_OS_CHOICES = getattr(settings, 'ORCHESTRATION_OS_CHOICES', ( + ('LINUX', "Linux"), +)) + +ORCHESTRATION_DEFAULT_OS = getattr(settings, 'ORCHESTRATION_DEFAULT_OS', 'LINUX') + +ORCHESTRATION_SSH_KEY_PATH = getattr(settings, 'ORCHESTRATION_SSH_KEY_PATH', + path.join(path.expanduser('~'), '.ssh/id_rsa')) + +ORCHESTRATION_ROUTER = getattr(settings, 'ORCHESTRATION_ROUTER', + 'orchestra.apps.orchestration.models.Route' +) + +ORCHESTRATION_TEMP_SCRIPT_PATH = getattr(settings, 'ORCHESTRATION_TEMP_SCRIPT_PATH', + '/dev/shm' +) diff --git a/orchestra/apps/orchestration/tests/__init__.py b/orchestra/apps/orchestration/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/orchestra/apps/orchestration/tests/test_route.py b/orchestra/apps/orchestration/tests/test_route.py new file mode 100644 index 00000000..34767203 --- /dev/null +++ b/orchestra/apps/orchestration/tests/test_route.py @@ -0,0 +1,44 @@ +from django.db import IntegrityError, transaction + +from orchestra.utils.tests import BaseTestCase + +from .. import operations, backends +from ..models import Route, Server +from ..utils import get_backend_choices + + +class RouterTests(BaseTestCase): + def setUp(self): + self.host = Server.objects.create(name='web.example.com') + self.host1 = Server.objects.create(name='web1.example.com') + self.host2 = Server.objects.create(name='web2.example.com') + + def test_list_backends(self): + # TODO count actual, register and compare + choices = list(Route._meta.get_field_by_name('backend')[0]._choices) + self.assertLess(1, len(choices)) + + def test_get_instances(self): + + class TestBackend(backends.ServiceBackend): + verbose_name = 'Route' + models = ['routes.Route',] + + choices = get_backend_choices(backends.ServiceBackend.get_backends()) + Route._meta.get_field_by_name('backend')[0]._choices = choices + backend = TestBackend.get_name() + + route = Route.objects.create(backend=backend, host=self.host, + match='True') + operation = operations.Operation(TestBackend, route, 'commit') + self.assertEqual(1, len(Route.get_servers(operation))) + + route = Route.objects.create(backend=backend, host=self.host1, + match='instance.backend == "TestBackend"') + operation = operations.Operation(TestBackend, route, 'commit') + self.assertEqual(2, len(Route.get_servers(operation))) + + route = Route.objects.create(backend=backend, host=self.host2, + match='instance.backend == "something else"') + operation = operations.Operation(TestBackend, route, 'commit') + self.assertEqual(2, len(Route.get_servers(operation))) diff --git a/orchestra/apps/orders/README.md b/orchestra/apps/orders/README.md new file mode 100644 index 00000000..023e0337 --- /dev/null +++ b/orchestra/apps/orders/README.md @@ -0,0 +1,8 @@ +Orders +====== + +Build an asyclic graph with every `model.save()` and `model.delete()` looking for Service.content_type matches. + +`ORDERS_GRAPH_MAX_DEPTH` + +autodiscover contacts by looking for `contact` atribute on related objects with reverse relationship `null=False` diff --git a/orchestra/apps/orders/__init__.py b/orchestra/apps/orders/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/orchestra/apps/orders/collector.py b/orchestra/apps/orders/collector.py new file mode 100644 index 00000000..900215a6 --- /dev/null +++ b/orchestra/apps/orders/collector.py @@ -0,0 +1,29 @@ +from . import settings + + +class Node(object): + def __init__(self, content): + self.content = content + self.parents = [] + self.path = [] + + def __repr__(self): + return "%s:%s" % (type(self.content).__name__, self.content) + + +class Collector(object): + def __init__(self, obj, cascade_only=False): + self.obj = obj + self.cascade_only = cascade_only + + def collect(self): + depth = settings.ORDERS_COLLECTOR_MAX_DEPTH + return self._rec_collect(self.obj, [self.obj], depth) + + def _rec_collect(self, obj, path, depth): + node = Node(content=obj) + # FK lookups + for field in obj._meta.fields: + if hasattr(field, 'related') and (self.cascade_only or not field.null): + related_object = getattr(obj, field.name) + diff --git a/orchestra/apps/orders/models.py b/orchestra/apps/orders/models.py new file mode 100644 index 00000000..128a89b6 --- /dev/null +++ b/orchestra/apps/orders/models.py @@ -0,0 +1,36 @@ +from django.contrib.contenttypes import generic +from django.contrib.contenttypes.models import ContentType +from django.db import models +from django.utils.translation import ugettext as _ + +from . import settings + + +class Service(models.Model): + name = models.CharField(_("name"), max_length=256) + content_type = models.ForeignKey(ContentType, verbose_name=_("content_type")) + match = models.CharField(_("expression"), max_length=256) + + def __unicode__(self): + return self.name + + +class Order(models.Model): + contact = models.ForeignKey(settings.ORDERS_CONTACT_MODEL, + verbose_name=_("contact"), related_name='orders') + content_type = models.ForeignKey(ContentType) + object_id = models.PositiveIntegerField(null=True) + service = models.ForeignKey(Service, verbose_name=_("service"), + related_name='orders')) + registered_on = models.DateTimeField(_("registered on"), auto_now_add=True) + canceled_on = models.DateTimeField(_("canceled on"), null=True, blank=True) + last_billed_on = models.DateTimeField(_("last billed on"), null=True, blank=True) + billed_until = models.DateTimeField(_("billed until"), null=True, blank=True) + ignore = models.BooleanField(_("ignore"), default=False) + description = models.CharField(_("description"), max_length=256, blank=True) + + content_object = generic.GenericForeignKey() + + def __unicode__(self): + return "%s@%s" (self.service, self.contact) + diff --git a/orchestra/apps/orders/settings.py b/orchestra/apps/orders/settings.py new file mode 100644 index 00000000..7a7c8a0d --- /dev/null +++ b/orchestra/apps/orders/settings.py @@ -0,0 +1,7 @@ +from django.conf import settings + + +ORDERS_CONTACT_MODEL = getattr(settings, 'ORDERS_CONTACT_MODEL', 'contacts.Contact') + + +ORDERS_COLLECTOR_MAX_DEPTH = getattr(settings, 'ORDERS_COLLECTOR_MAX_DEPTH', 3) diff --git a/orchestra/apps/orders/tests/__init__.py b/orchestra/apps/orders/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/orchestra/apps/orders/tests/models.py b/orchestra/apps/orders/tests/models.py new file mode 100644 index 00000000..b9c91273 --- /dev/null +++ b/orchestra/apps/orders/tests/models.py @@ -0,0 +1,21 @@ +from django.db import models + + +class Root(models.Model): + name = models.CharField(max_length=256, default='randomname') + + +class Related(models.Model): + root = models.ForeignKey(Root) + + +class TwoRelated(models.Model): + related = models.ForeignKey(Related) + + +class ThreeRelated(models.Model): + twolated = models.ForeignKey(TwoRelated) + + +class FourRelated(models.Model): + threerelated = models.ForeignKey(ThreeRelated) diff --git a/orchestra/apps/orders/tests/test_collector.py b/orchestra/apps/orders/tests/test_collector.py new file mode 100644 index 00000000..a6382b3d --- /dev/null +++ b/orchestra/apps/orders/tests/test_collector.py @@ -0,0 +1,28 @@ +from django.conf import settings +from django.core.management import call_command +from django.db.models import loading +from django.test import TestCase + +from .models import Root, Related, TwoRelated, ThreeRelated, FourRelated + + +#class CollectorTests(TestCase): +# def setUp(self): +# self.root = Root.objects.create(name='randomname') +# self.related = Related.objects.create(top=self.root) +# +# def _pre_setup(self): +# # Add the models to the db. +# self._original_installed_apps = list(settings.INSTALLED_APPS) +# settings.INSTALLED_APPS += ('orchestra.apps.orders.tests',) +# loading.cache.loaded = False +# call_command('syncdb', interactive=False, verbosity=0) +# super(CollectorTests, self)._pre_setup() +# +# def _post_teardown(self): +# super(CollectorTests, self)._post_teardown() +# settings.INSTALLED_APPS = self._original_installed_apps +# loading.cache.loaded = False + +# def test_models(self): +# self.assertEqual('randomname', self.root.name) diff --git a/orchestra/apps/users/__init__.py b/orchestra/apps/users/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/orchestra/apps/users/admin.py b/orchestra/apps/users/admin.py new file mode 100644 index 00000000..2ab9ed6e --- /dev/null +++ b/orchestra/apps/users/admin.py @@ -0,0 +1,110 @@ +from django.conf.urls import patterns, url +from django.core.urlresolvers import reverse +from django.contrib import admin +from django.contrib.admin.util import unquote +from django.contrib.auth import admin as auth +from django.utils.translation import ugettext, ugettext_lazy as _ + +from orchestra.admin import ExtendedModelAdmin +from orchestra.admin.utils import wrap_admin_view +from orchestra.apps.accounts.admin import AccountAdminMixin + +from .forms import UserCreationForm, UserChangeForm +from .models import User +from .roles.filters import role_list_filter_factory + + +class UserAdmin(AccountAdminMixin, auth.UserAdmin, ExtendedModelAdmin): + list_display = ('username', 'is_main') + list_filter = ('is_staff', 'is_superuser', 'is_active') + fieldsets = ( + (None, { + 'fields': ('account', 'username', 'password') + }), + (_("Personal info"), { + 'fields': ('first_name', 'last_name', 'email') + }), + (_("Permissions"), { + 'fields': ('is_active', 'is_staff', 'is_superuser', 'is_admin', 'is_main') + }), + (_("Important dates"), { + 'fields': ('last_login', 'date_joined') + }), + ) + add_fieldsets = ( + (None, { + 'classes': ('wide',), + 'fields': ('username', 'password1', 'password2', 'account'), + }), + ) + search_fields = ['username', 'account__user__username'] + readonly_fields = ('is_main', 'account_link') + change_readonly_fields = ('username',) + filter_horizontal = () + add_form = UserCreationForm + form = UserChangeForm + roles = [] + ordering = ('-id',) + + + def is_main(self, user): + return user.account.user == user + is_main.boolean = True + + def get_urls(self): + """ Returns the additional urls for the change view links """ + urls = super(UserAdmin, self).get_urls() + admin_site = self.admin_site + opts = self.model._meta + new_urls = patterns("") + for role in self.roles: + new_urls += patterns("", + url('^(\d+)/%s/$' % role.url_name, + wrap_admin_view(self, role().change_view), + name='%s_%s_%s_change' % (opts.app_label, opts.module_name, role.name)), + url('^(\d+)/%s/delete/$' % role.url_name, + wrap_admin_view(self, role().delete_view), + name='%s_%s_%s_delete' % (opts.app_label, opts.module_name, role.name)) + ) + return new_urls + urls + + def get_fieldsets(self, request, obj=None): + fieldsets = super(UserAdmin, self).get_fieldsets(request, obj=obj) + if obj and obj.account: + fieldsets[0][1]['fields'] = ('account_link',) + fieldsets[0][1]['fields'][1:] + return fieldsets + + def get_list_display(self, request): + roles = [] + for role in self.roles: + def has_role(user, role_class=role): + role = role_class(user=user) + if role.exists: + return 'True' + url = reverse('admin:users_user_%s_change' % role.name, args=(user.pk,)) + false = 'False' + return '%s' % (url, false) + has_role.short_description = _("Has %s") % role.name + has_role.admin_order_field = role.name + has_role.allow_tags = True + roles.append(has_role) + return list(self.list_display) + roles + ['account_link'] + + def get_list_filter(self, request): + roles = [ role_list_filter_factory(role) for role in self.roles ] + return list(self.list_filter) + roles + + def change_view(self, request, object_id, **kwargs): + user = self.get_object(User, unquote(object_id)) + extra_context = kwargs.get('extra_context', {}) + extra_context['roles'] = [ role(user=user) for role in self.roles ] + kwargs['extra_context'] = extra_context + return super(UserAdmin, self).change_view(request, object_id, **kwargs) + + def queryset(self, request): + """ Select related for performance """ + related = ['account__user'] + [ role.name for role in self.roles ] + return super(UserAdmin, self).queryset(request).select_related(*related) + + +admin.site.register(User, UserAdmin) diff --git a/orchestra/apps/users/api.py b/orchestra/apps/users/api.py new file mode 100644 index 00000000..80cc841f --- /dev/null +++ b/orchestra/apps/users/api.py @@ -0,0 +1,23 @@ +from django.contrib.auth import get_user_model +from rest_framework import viewsets +from rest_framework import status +from rest_framework.decorators import action +from rest_framework.response import Response + +from orchestra.api import router, SetPasswordApiMixin +from orchestra.apps.accounts.api import AccountApiMixin + +from .serializers import UserSerializer + + +class UserViewSet(AccountApiMixin, SetPasswordApiMixin, viewsets.ModelViewSet): + model = get_user_model() + serializer_class = UserSerializer + + def get_queryset(self): + """ select related roles """ + qs = super(UserViewSet, self).get_queryset() + return qs.select_related(*self.inserted) + + +router.register(r'users', UserViewSet) diff --git a/orchestra/apps/users/backends.py b/orchestra/apps/users/backends.py new file mode 100644 index 00000000..41895094 --- /dev/null +++ b/orchestra/apps/users/backends.py @@ -0,0 +1,41 @@ +from django.utils.translation import ugettext_lazy as _ + +from orchestra.apps.orchestration import ServiceBackend + +from . import settings + + +class SystemUserBackend(ServiceBackend): + verbose_name = _("System User") + model = 'users.User' + ignore_fields = ['last_login'] + + def save(self, user): + context = self.get_context(user) + if user.is_main: + self.append( + "if [[ $( id %(username)s ) ]]; then \n" + " usermod --password '%(password)s' %(username)s \n" + "else \n" + " useradd %(username)s --password '%(password)s' \\\n" + " --shell /bin/false \n" + "fi" % context + ) + self.append("mkdir -p %(home)s" % context) + self.append("chown %(username)s.%(username)s %(home)s" % context) + else: + self.delete(user) + + def delete(self, user): + context = self.get_context(user) + self.append("{ sleep 2 && killall -u %(username)s -s KILL; } &" % context) + self.append("killall -u %(username)s" % context) + self.append("userdel %(username)s" % context) + + def get_context(self, user): + context = { + 'username': user.username, + 'password': user.password if user.is_active else '*%s' % user.password, + } + context['home'] = settings.USERS_SYSTEMUSER_HOME % context + return context diff --git a/orchestra/apps/users/forms.py b/orchestra/apps/users/forms.py new file mode 100644 index 00000000..a4d3c9c2 --- /dev/null +++ b/orchestra/apps/users/forms.py @@ -0,0 +1,50 @@ +from django import forms +from django.contrib import auth +from django.utils.translation import ugettext, ugettext_lazy as _ + +from orchestra.core.validators import validate_password + +from .models import User + + +class UserCreationForm(auth.forms.UserCreationForm): + class Meta(auth.forms.UserCreationForm.Meta): + model = User + + def __init__(self, *args, **kwargs): + super(UserCreationForm, self).__init__(*args, **kwargs) + self.fields['password1'].validators.append(validate_password) + + def clean_username(self): + # Since User.username is unique, this check is redundant, + # but it sets a nicer error message than the ORM. See #13147. + username = self.cleaned_data["username"] + try: + User._default_manager.get(username=username) + except User.DoesNotExist: + return username + raise forms.ValidationError(self.error_messages['duplicate_username']) + + +class UserChangeForm(forms.ModelForm): + password = auth.forms.ReadOnlyPasswordHashField(label=_("Password"), + help_text=_("Raw passwords are not stored, so there is no way to see " + "this user's password, but you can change the password " + "using this form.")) + + class Meta: + model = User + fields = '__all__' + + def __init__(self, *args, **kwargs): + super(UserChangeForm, self).__init__(*args, **kwargs) + f = self.fields.get('user_permissions', None) + if f is not None: + f.queryset = f.queryset.select_related('content_type') + + def clean_password(self): + # Regardless of what the user provides, return the initial value. + # This is done here, rather than on the field, because the + # field does not have access to the initial value + return self.initial["password"] + diff --git a/orchestra/apps/users/models.py b/orchestra/apps/users/models.py new file mode 100644 index 00000000..e1ba1d66 --- /dev/null +++ b/orchestra/apps/users/models.py @@ -0,0 +1,88 @@ +from django.contrib.auth import models as auth +from django.core import validators +from django.db import models +from django.utils import timezone +from django.utils.translation import ugettext_lazy as _ + +from orchestra.core import services + + +class User(auth.AbstractBaseUser): + username = models.CharField(_("username"), max_length=64, unique=True, + help_text=_("Required. 30 characters or fewer. Letters, digits and " + "@/./+/-/_ only."), + validators=[validators.RegexValidator(r'^[\w.@+-]+$', + _("Enter a valid username."), 'invalid')]) + account = models.ForeignKey('accounts.Account', verbose_name=_("Account"), + related_name='users', null=True) + first_name = models.CharField(_("first name"), max_length=30, blank=True) + last_name = models.CharField(_("last name"), max_length=30, blank=True) + email = models.EmailField(_('email address'), blank=True) + is_superuser = models.BooleanField(_("superuser status"), default=False, + help_text=_("Designates that this user has all permissions without " + "explicitly assigning them.")) + is_staff = models.BooleanField(_("staff status"), default=False, + help_text=_("Designates whether the user can log into this admin " + "site.")) + is_admin = models.BooleanField(_("admin status"), default=False, + help_text=_("Designates whether the user can administrate its account.")) + is_active = models.BooleanField(_("active"), default=True, + help_text=_("Designates whether this user should be treated as " + "active. Unselect this instead of deleting accounts.")) + date_joined = models.DateTimeField(_("date joined"), default=timezone.now) + + objects = auth.UserManager() + + USERNAME_FIELD = 'username' + REQUIRED_FIELDS = ['email'] + + def get_full_name(self): + """ Returns the first_name plus the last_name, with a space in between """ + full_name = '%s %s' % (self.first_name, self.last_name) + return full_name.strip() + + def get_short_name(self): + """ Returns the short name for the user """ + return self.first_name + + def email_user(self, subject, message, from_email=None, **kwargs): + """ Sends an email to this User """ + send_mail(subject, message, from_email, [self.email], **kwargs) + + def has_perm(self, perm, obj=None): + """ + Returns True if the user has the specified permission. This method + queries all available auth backends, but returns immediately if any + backend returns True. Thus, a user who has permission from a single + auth backend is assumed to have permission in general. If an object is + provided, permissions for this specific object are checked. + """ + # Active superusers have all permissions. + if self.is_active and self.is_superuser: + return True + # Otherwise we need to check the backends. + return auth._user_has_perm(self, perm, obj) + + def has_perms(self, perm_list, obj=None): + """ + Returns True if the user has each of the specified permissions. If + object is passed, it checks if the user has all required perms for this + object. + """ + for perm in perm_list: + if not self.has_perm(perm, obj): + return False + return True + + def has_module_perms(self, app_label): + """ + Returns True if the user has any permissions in the given app label. + Uses pretty much the same logic as has_perm, above. + """ + # Active superusers have all permissions. + if self.is_active and self.is_superuser: + return True + return auth._user_has_module_perms(self, app_label) + + +services.register(User, menu=False) diff --git a/orchestra/apps/users/roles/__init__.py b/orchestra/apps/users/roles/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/orchestra/apps/users/roles/admin.py b/orchestra/apps/users/roles/admin.py new file mode 100644 index 00000000..1e442cf3 --- /dev/null +++ b/orchestra/apps/users/roles/admin.py @@ -0,0 +1,147 @@ +from django.contrib import messages +from django.contrib.admin.util import unquote, get_deleted_objects +from django.contrib.admin.templatetags.admin_urls import add_preserved_filters +from django.core.urlresolvers import reverse +from django.db import router +from django.http import Http404, HttpResponseRedirect +from django.template.response import TemplateResponse +from django.shortcuts import redirect +from django.utils.encoding import force_text +from django.utils.html import escape +from django.utils.translation import ugettext, ugettext_lazy as _ + +from orchestra.admin.utils import get_modeladmin + +from .forms import role_form_factory +from ..models import User + + +class RoleAdmin(object): + model = None + name = '' + url_name = '' + form = None + + def __init__(self, user=None): + self.user = user + + @property + def exists(self): + try: + return getattr(self.user, self.name) + except self.model.DoesNotExist: + return False + + def get_user(self, request, object_id): + modeladmin = get_modeladmin(User) + user = modeladmin.get_object(request, unquote(object_id)) + opts = self.model._meta + if user is None: + raise Http404( + _('%(name)s object with primary key %(key)r does not exist.') % + {'name': force_text(opts.verbose_name), 'key': escape(object_id)} + ) + return user + + def change_view(self, request, object_id): + modeladmin = get_modeladmin(User) + user = self.get_user(request, object_id) + self.user = user + obj = None + exists = self.exists + if exists: + obj = getattr(user, self.name) + form_class = self.form if self.form else role_form_factory(self) + form = form_class(instance=obj) + opts = modeladmin.model._meta + app_label = opts.app_label + title = _("Add %s for user %s" % (self.name, user)) + action = _("Create") + # User has submitted the form + if request.method == 'POST': + form = form_class(request.POST, instance=obj) + form.user = user + if form.is_valid(): + obj = form.save() + context = { + 'name': obj._meta.verbose_name, + 'obj': obj, + 'action': _("saved" if exists else "created") + } + modeladmin.log_change(request, request.user, "%s saved" % self.name.capitalize()) + msg = _('The role %(name)s for user "%(obj)s" was %(action)s successfully.') % context + modeladmin.message_user(request, msg, messages.SUCCESS) + url = 'admin:%s_%s_change' % (opts.app_label, opts.module_name) + if not "_continue" in request.POST: + return redirect(url, object_id) + exists = True + + if exists: + title = _("Change %s %s settings" % (user, self.name)) + action = _("Save") + form = form_class(instance=obj) + + context = { + 'title': title, + 'opts': opts, + 'app_label': app_label, + 'form': form, + 'action': action, + 'role': self, + 'roles': [ role(user=user) for role in modeladmin.roles ], + 'media': modeladmin.media + } + + template = 'admin/users/user/role.html' + app = modeladmin.admin_site.name + return TemplateResponse(request, template, context, current_app=app) + + def delete_view(self, request, object_id): + "The 'delete' admin view for this model." + opts = self.model._meta + app_label = opts.app_label + modeladmin = get_modeladmin(User) + user = self.get_user(request, object_id) + obj = getattr(user, self.name) + + using = router.db_for_write(self.model) + + # Populate deleted_objects, a data structure of all related objects that + # will also be deleted. + (deleted_objects, perms_needed, protected) = get_deleted_objects( + [obj], opts, request.user, modeladmin.admin_site, using) + + if request.POST: # The user has already confirmed the deletion. + if perms_needed: + raise PermissionDenied + obj_display = force_text(obj) + modeladmin.log_deletion(request, obj, obj_display) + modeladmin.delete_model(request, obj) + post_url = reverse('admin:users_user_change', args=(user.pk,)) + preserved_filters = modeladmin.get_preserved_filters(request) + post_url = add_preserved_filters( + {'preserved_filters': preserved_filters, 'opts': opts}, post_url + ) + return HttpResponseRedirect(post_url) + + object_name = force_text(opts.verbose_name) + + if perms_needed or protected: + title = _("Cannot delete %(name)s") % {"name": object_name} + else: + title = _("Are you sure?") + + context = { + "title": title, + "object_name": object_name, + "object": obj, + "deleted_objects": deleted_objects, + "perms_lacking": perms_needed, + "protected": protected, + "opts": opts, + "app_label": app_label, + 'preserved_filters': modeladmin.get_preserved_filters(request), + 'role': self, + } + return TemplateResponse(request, 'admin/users/user/delete_role.html', + context, current_app=modeladmin.admin_site.name) diff --git a/orchestra/apps/users/roles/filters.py b/orchestra/apps/users/roles/filters.py new file mode 100644 index 00000000..7bac9a3b --- /dev/null +++ b/orchestra/apps/users/roles/filters.py @@ -0,0 +1,23 @@ +from django.contrib.admin import SimpleListFilter +from django.utils.translation import ugettext_lazy as _ + + +def role_list_filter_factory(role): + class RoleListFilter(SimpleListFilter): + """ Filter Nodes by group according to request.user """ + title = _("has %s" % role.name) + parameter_name = role.url_name + + def lookups(self, request, model_admin): + return ( + ('True', _("Yes")), + ('False', _("No")), + ) + + def queryset(self, request, queryset): + if self.value() == 'True': + return queryset.filter(**{ '%s__isnull' % role.name: False }) + if self.value() == 'False': + return queryset.filter(**{ '%s__isnull' % role.name: True }) + + return RoleListFilter diff --git a/orchestra/apps/users/roles/forms.py b/orchestra/apps/users/roles/forms.py new file mode 100644 index 00000000..decc6610 --- /dev/null +++ b/orchestra/apps/users/roles/forms.py @@ -0,0 +1,17 @@ +from django import forms + + +class RoleAdminBaseForm(forms.ModelForm): + class Meta: + exclude = ('user', ) + + def save(self, *args, **kwargs): + self.instance.user = self.user + return super(RoleAdminBaseForm, self).save(*args, **kwargs) + + +def role_form_factory(role): + class RoleAdminForm(RoleAdminBaseForm): + class Meta(RoleAdminBaseForm.Meta): + model = role.model + return RoleAdminForm diff --git a/orchestra/apps/users/roles/jabber/__init__.py b/orchestra/apps/users/roles/jabber/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/orchestra/apps/users/roles/jabber/admin.py b/orchestra/apps/users/roles/jabber/admin.py new file mode 100644 index 00000000..8d5590f5 --- /dev/null +++ b/orchestra/apps/users/roles/jabber/admin.py @@ -0,0 +1,17 @@ +from django.contrib import admin +from django.contrib.auth import get_user_model +from django.utils.translation import ugettext_lazy as _ + +from orchestra.admin.utils import insertattr +from orchestra.apps.users.roles.admin import RoleAdmin + +from .models import Jabber + + +class JabberRoleAdmin(RoleAdmin): + model = Jabber + name = 'jabber' + url_name = 'jabber' + + +insertattr(get_user_model(), 'roles', JabberRoleAdmin) diff --git a/orchestra/apps/users/roles/jabber/models.py b/orchestra/apps/users/roles/jabber/models.py new file mode 100644 index 00000000..a82bc429 --- /dev/null +++ b/orchestra/apps/users/roles/jabber/models.py @@ -0,0 +1,10 @@ +from django.db import models +from django.utils.translation import ugettext_lazy as _ + + +class Jabber(models.Model): + user = models.OneToOneField('users.User', verbose_name=_("user"), + related_name='jabber') + + def __unicode__(self): + return str(self.user) diff --git a/orchestra/apps/users/roles/mail/__init__.py b/orchestra/apps/users/roles/mail/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/orchestra/apps/users/roles/mail/admin.py b/orchestra/apps/users/roles/mail/admin.py new file mode 100644 index 00000000..e36cde21 --- /dev/null +++ b/orchestra/apps/users/roles/mail/admin.py @@ -0,0 +1,129 @@ +from django import forms +from django.contrib import admin +from django.contrib.auth import get_user_model +from django.contrib.auth.admin import UserAdmin +from django.core.urlresolvers import reverse +from django.utils.safestring import mark_safe +from django.utils.translation import ugettext_lazy as _ + +from orchestra.admin import ExtendedModelAdmin +from orchestra.admin.utils import insertattr, link +from orchestra.apps.accounts.admin import SelectAccountAdminMixin +from orchestra.apps.domains.forms import DomainIterator +from orchestra.apps.users.roles.admin import RoleAdmin + +from .forms import MailRoleAdminForm +from .models import Mailbox, Address, Autoresponse + + +class AutoresponseInline(admin.StackedInline): + model = Autoresponse + verbose_name_plural = _("autoresponse") + + def formfield_for_dbfield(self, db_field, **kwargs): + if db_field.name == 'subject': + kwargs['widget'] = forms.TextInput(attrs={'size':'118'}) + return super(AutoresponseInline, self).formfield_for_dbfield(db_field, **kwargs) + + +#class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): +# list_display = ('email', 'domain_link', 'mailboxes', 'forwards', 'account_link') +# fields = ('account_link', ('name', 'domain'), 'destination') +# inlines = [AutoresponseInline] +# search_fields = ('name', 'domain__name',) +# readonly_fields = ('account_link', 'domain_link', 'email_link') +# filter_by_account_fields = ['domain'] +# +# domain_link = link('domain', order='domain__name') +# +# def email_link(self, address): +# link = self.domain_link(address) +# return "%s@%s" % (address.name, link) +# email_link.short_description = _("Email") +# email_link.allow_tags = True +# +# def mailboxes(self, address): +# boxes = [] +# for mailbox in address.get_mailboxes(): +# user = mailbox.user +# url = reverse('admin:users_user_mailbox_change', args=(user.pk,)) +# boxes.append('%s' % (url, user.username)) +# return '
    '.join(boxes) +# mailboxes.allow_tags = True +# +# def forwards(self, address): +# values = [ dest for dest in address.destination.split() if '@' in dest ] +# return '
    '.join(values) +# forwards.allow_tags = True +# +# def formfield_for_dbfield(self, db_field, **kwargs): +# if db_field.name == 'destination': +# kwargs['widget'] = forms.TextInput(attrs={'size':'118'}) +# return super(AddressAdmin, self).formfield_for_dbfield(db_field, **kwargs) +# +# def queryset(self, request): +# """ Select related for performance """ +# qs = super(AddressAdmin, self).queryset(request) +# # TODO django 1.7 account__user is not needed +# return qs.select_related('domain', 'account__user') + + +class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): + list_display = ( + 'email', 'domain_link', 'display_mailboxes', 'display_forward', 'account_link' + ) + fields = ('account_link', ('name', 'domain'), 'mailboxes', 'forward') + inlines = [AutoresponseInline] + search_fields = ('name', 'domain__name',) + readonly_fields = ('account_link', 'domain_link', 'email_link') + filter_by_account_fields = ['domain'] + filter_horizontal = ['mailboxes'] + + domain_link = link('domain', order='domain__name') + + def email_link(self, address): + link = self.domain_link(address) + return "%s@%s" % (address.name, link) + email_link.short_description = _("Email") + email_link.allow_tags = True + + def display_mailboxes(self, address): + boxes = [] + for mailbox in address.mailboxes(): + user = mailbox.user + url = reverse('admin:users_user_mailbox_change', args=(user.pk,)) + boxes.append('%s' % (url, user.username)) + return '
    '.join(boxes) + display_mailboxes.short_description = _("Mailboxes") + display_mailboxes.allow_tags = True + + def display_forward(self, address): + values = [ dest for dest in address.forward.split() ] + return '
    '.join(values) + display_forward.short_description = _("Forward") + display_forward.allow_tags = True + + def formfield_for_dbfield(self, db_field, **kwargs): + if db_field.name == 'forward': + kwargs['widget'] = forms.TextInput(attrs={'size':'118'}) + if db_field.name == 'mailboxes': + mailboxes = db_field.rel.to.objects.select_related('user') + kwargs['queryset'] = mailboxes.filter(user__account=self.account) + return super(AddressAdmin, self).formfield_for_dbfield(db_field, **kwargs) + + def queryset(self, request): + """ Select related for performance """ + qs = super(AddressAdmin, self).queryset(request) + # TODO django 1.7 account__user is not needed + return qs.select_related('domain', 'account__user') + + +class MailRoleAdmin(RoleAdmin): + model = Mailbox + name = 'mailbox' + url_name = 'mailbox' + form = MailRoleAdminForm + + +admin.site.register(Address, AddressAdmin) +insertattr(get_user_model(), 'roles', MailRoleAdmin) diff --git a/orchestra/apps/users/roles/mail/api.py b/orchestra/apps/users/roles/mail/api.py new file mode 100644 index 00000000..410d8a1c --- /dev/null +++ b/orchestra/apps/users/roles/mail/api.py @@ -0,0 +1,27 @@ +from rest_framework import viewsets + +from orchestra.api import router +from orchestra.apps.accounts.api import AccountApiMixin + +from .models import Address, Mailbox +from .serializers import AddressSerializer, MailboxSerializer + + +class AddressViewSet(AccountApiMixin, viewsets.ModelViewSet): + model = Address + serializer_class = AddressSerializer + + + +class MailboxViewSet(viewsets.ModelViewSet): + model = Mailbox + serializer_class = MailboxSerializer + + def get_queryset(self): + qs = super(MailboxViewSet, self).get_queryset() + qs = qs.select_related('user') + return qs.filter(user__account=self.request.user.account_id) + + +router.register(r'mailboxes', MailboxViewSet) +router.register(r'addresses', AddressViewSet) diff --git a/orchestra/apps/users/roles/mail/backends.py b/orchestra/apps/users/roles/mail/backends.py new file mode 100644 index 00000000..422eb6ab --- /dev/null +++ b/orchestra/apps/users/roles/mail/backends.py @@ -0,0 +1,143 @@ +import os + +from django.utils import timezone +from django.utils.translation import ugettext_lazy as _ + +from orchestra.apps.orchestration import ServiceBackend + +from . import settings + + +class MailSystemUserBackend(ServiceBackend): + verbose_name = _("Mail system user") + model = 'mail.Mailbox' + + DEFAULT_GROUP = 'postfix' + + def create_user(self, context): + self.append( + "if [[ $( id %(username)s ) ]]; then \n" + " usermod -p '%(password)s' %(username)s \n" + "else \n" + " useradd %(username)s --password '%(password)s' \\\n" + " --shell /dev/null \n" + "fi" % context + ) + self.append("mkdir -p %(home)s" % context) + self.append("chown %(username)s.%(group)s %(home)s" % context) + + def generate_filter(self, mailbox, context): + datetime = timezone.now().strftime("%B %d, %Y, %H:%M") + context['filtering'] = ( + "# Sieve Filter\n" + "# Generated by Orchestra %s\n\n" % datetime + ) + if mailbox.use_custom_filtering: + context['filtering'] += mailbox.custom_filtering + else: + context['filtering'] += settings.EMAILS_DEFAUL_FILTERING + context['filter_path'] = os.path.join(context['home'], '.orchestra.sieve') + self.append("echo '%(filtering)s' > %(filter_path)s" % context) + + def save(self, mailbox): + context = self.get_context(mailbox) + self.create_user(context) + self.generate_filter(mailbox, context) + + def delete(self, mailbox): + context = self.get_context(mailbox) + self.append("{ sleep 2 && killall -u %(username)s -s KILL; } &" % context) + self.append("killall -u %(username)s" % context) + self.append("userdel %(username)s" % context) + self.append("rm -fr %(home)s" % context) + + def get_context(self, mailbox): + user = mailbox.user + context = { + 'username': user.username, + 'password': user.password if user.is_active else '*%s' % user.password, + 'group': self.DEFAULT_GROUP + } + context['home'] = settings.EMAILS_HOME % context + return context + + +class PostfixAddressBackend(ServiceBackend): + verbose_name = _("Postfix address") + model = 'mail.Address' + + def include_virtdomain(self, context): + self.append( + '[[ $(grep "^\s*%(domain)s\s*$" %(virtdomains)s) ]]' + ' || { echo "%(domain)s" >> %(virtdomains)s; UPDATED=1; }' % context + ) + + def exclude_virtdomain(self, context): + domain = context['domain'] + if not Address.objects.filter(domain=domain).exists(): + self.append('sed -i "s/^%(domain)s//" %(virtdomains)s' % context) + + def update_virtusertable(self, context): + self.append( + 'LINE="%(email)s\t%(destination)s"\n' + 'if [[ ! $(grep "^%(email)s\s" %(virtusertable)s) ]]; then\n' + ' echo "$LINE" >> %(virtusertable)s\n' + ' UPDATED=1\n' + 'else\n' + ' if [[ ! $(grep "^${LINE}$" %(virtusertable)s) ]]; then\n' + ' sed -i "s/^%(email)s\s.*$/${LINE}/" %(virtusertable)s\n' + ' UPDATED=1\n' + ' fi\n' + 'fi' % context + ) + + def exclude_virtusertable(self, context): + self.append( + 'if [[ $(grep "^%(email)s\s") ]]; then\n' + ' sed -i "s/^%(email)s\s.*$//" %(virtusertable)s\n' + ' UPDATED=1\n' + 'fi' + ) + + def save(self, address): + context = self.get_context(address) + self.include_virtdomain(context) + self.update_virtusertable(context) + + def delete(self, address): + context = self.get_context(address) + self.exclude_virtdomain(context) + self.exclude_virtusertable(context) + + def commit(self): + context = self.get_context_files() + self.append('[[ $UPDATED == 1 ]] && { ' + 'postmap %(virtdomains)s;' + 'postmap %(virtusertable)s;' + '}' % context) + + def get_context_files(self): + return { + 'virtdomains': settings.EMAILS_VIRTDOMAINS_PATH, + 'virtusertable': settings.EMAILS_VIRTUSERTABLE_PATH, + } + + def get_context(self, address): + context = self.get_context_files() + context.update({ + 'domain': address.domain, + 'email': address.email, + 'destination': address.destination, + }) + return context + + +class AutoresponseBackend(ServiceBackend): + verbose_name = _("Mail autoresponse") + model = 'mail.Autoresponse' + + def save(self, autoresponse): + pass + + def delete(self, autoresponse): + pass diff --git a/orchestra/apps/users/roles/mail/forms.py b/orchestra/apps/users/roles/mail/forms.py new file mode 100644 index 00000000..7f1023ff --- /dev/null +++ b/orchestra/apps/users/roles/mail/forms.py @@ -0,0 +1,53 @@ +from django import forms +from django.core.urlresolvers import reverse +from django.utils.safestring import mark_safe +from django.utils.translation import ugettext_lazy as _ + +from orchestra.forms.widgets import ReadOnlyWidget + +from .models import Mailbox +from ..forms import RoleAdminBaseForm + + +class MailRoleAdminForm(RoleAdminBaseForm): + class Meta(RoleAdminBaseForm.Meta): + model = Mailbox + + def __init__(self, *args, **kwargs): + super(MailRoleAdminForm, self).__init__(*args, **kwargs) + instance = kwargs.get('instance') + if instance: + widget = ReadOnlyWidget(self.addresses(instance)) + self.fields['addresses'] = forms.CharField(widget=widget, + label=_("Addresses")) + +# def addresses(self, mailbox): +# account = mailbox.user.account +# addresses = account.addresses.filter(destination__contains=mailbox.user.username) +# add_url = reverse('admin:mail_address_add') +# add_url += '?account=%d&destination=%s' % (account.pk, mailbox.user.username) +# img = 'Add Another' +# onclick = 'onclick="return showAddAnotherPopup(this);"' +# add_link = '%s Add address' % (add_url, onclick, img) +# value = '%s

    ' % add_link +# for pk, name, domain in addresses.values_list('pk', 'name', 'domain__name'): +# url = reverse('admin:mail_address_change', args=(pk,)) +# name = '%s@%s' % (name, domain) +# value += '
  • %s
  • ' % (url, name) +# value = '
      %s
    ' % value +# return mark_safe('
    %s
    ' % value) + + def addresses(self, mailbox): + account = mailbox.user.account + add_url = reverse('admin:mail_address_add') + add_url += '?account=%d&mailboxes=%s' % (account.pk, mailbox.pk) + img = 'Add Another' + onclick = 'onclick="return showAddAnotherPopup(this);"' + add_link = '%s Add address' % (add_url, onclick, img) + value = '%s

    ' % add_link + for pk, name, domain in mailbox.addresses.values_list('pk', 'name', 'domain__name'): + url = reverse('admin:mail_address_change', args=(pk,)) + name = '%s@%s' % (name, domain) + value += '
  • %s
  • ' % (url, name) + value = '
      %s
    ' % value + return mark_safe('
    %s
    ' % value) diff --git a/orchestra/apps/users/roles/mail/models.py b/orchestra/apps/users/roles/mail/models.py new file mode 100644 index 00000000..858325f9 --- /dev/null +++ b/orchestra/apps/users/roles/mail/models.py @@ -0,0 +1,110 @@ +import re + +from django.contrib.auth.hashers import check_password, make_password +from django.core.validators import RegexValidator +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from orchestra.core import services + +from . import validators, settings + + +class Mailbox(models.Model): + user = models.OneToOneField('users.User', verbose_name=_("User"), + related_name='mailbox') + use_custom_filtering = models.BooleanField(_("Use custom filtering"), + default=False) + custom_filtering = models.TextField(_("filtering"), blank=True, + validators=[validators.validate_sieve], + help_text=_("Arbitrary email filtering in sieve language.")) + + class Meta: + verbose_name_plural = _("mailboxes") + + def __unicode__(self): + return self.user.username + +# def get_addresses(self): +# regex = r'(^|\s)+%s(\s|$)+' % self.user.username +# return Address.objects.filter(destination__regex=regex) +# +# def delete(self, *args, **kwargs): +# """ Update related addresses """ +# regex = re.compile(r'(^|\s)+(\s*%s)(\s|$)+' % self.user.username) +# super(Mailbox, self).delete(*args, **kwargs) +# for address in self.get_addresses(): +# address.destination = regex.sub(r'\3', address.destination).strip() +# if not address.destination: +# address.delete() +# else: +# address.save() + + +#class Address(models.Model): +# name = models.CharField(_("name"), max_length=64, +# validators=[validators.validate_emailname]) +# domain = models.ForeignKey(settings.EMAILS_DOMAIN_MODEL, +# verbose_name=_("domain"), +# related_name='addresses') +# destination = models.CharField(_("destination"), max_length=256, +# validators=[validators.validate_destination], +# help_text=_("Space separated mailbox names or email addresses")) +# account = models.ForeignKey('accounts.Account', verbose_name=_("Account"), +# related_name='addresses') +# +# class Meta: +# verbose_name_plural = _("addresses") +# unique_together = ('name', 'domain') +# +# def __unicode__(self): +# return self.email +# +# @property +# def email(self): +# return "%s@%s" % (self.name, self.domain) +# +# def get_mailboxes(self): +# for dest in self.destination.split(): +# if '@' not in dest: +# yield Mailbox.objects.select_related('user').get(user__username=dest) + + +class Address(models.Model): + name = models.CharField(_("name"), max_length=64, + validators=[validators.validate_emailname]) + domain = models.ForeignKey(settings.EMAILS_DOMAIN_MODEL, + verbose_name=_("domain"), + related_name='addresses') + mailboxes = models.ManyToManyField('mail.Mailbox', verbose_name=_("mailboxes"), + related_name='addresses', blank=True) + forward = models.CharField(_("forward"), max_length=256, blank=True, + validators=[validators.validate_forward]) + account = models.ForeignKey('accounts.Account', verbose_name=_("Account"), + related_name='addresses') + + class Meta: + verbose_name_plural = _("addresses") + unique_together = ('name', 'domain') + + def __unicode__(self): + return self.email + + @property + def email(self): + return "%s@%s" % (self.name, self.domain) + + +class Autoresponse(models.Model): + address = models.OneToOneField(Address, verbose_name=_("address"), + related_name='autoresponse') + # TODO initial_date + subject = models.CharField(_("subject"), max_length=256) + message = models.TextField(_("message")) + enabled = models.BooleanField(_("enabled"), default=False) + + def __unicode__(self): + return self.address + + +services.register(Address) diff --git a/orchestra/apps/users/roles/mail/serializers.py b/orchestra/apps/users/roles/mail/serializers.py new file mode 100644 index 00000000..24bf1a70 --- /dev/null +++ b/orchestra/apps/users/roles/mail/serializers.py @@ -0,0 +1,43 @@ +from rest_framework import serializers + +from orchestra.api import router +from orchestra.apps.accounts.serializers import AccountSerializerMixin + +from .models import Address, Mailbox + + +#class AddressSerializer(serializers.HyperlinkedModelSerializer): +# class Meta: +# model = Address +# fields = ('url', 'name', 'domain', 'destination') + + +class NestedMailboxSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = Mailbox + fields = ('url', 'use_custom_filtering', 'custom_filtering') + + +class MailboxSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = Mailbox + fields = ('url', 'user', 'use_custom_filtering', 'custom_filtering') + + +class AddressSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer): + class Meta: + model = Address + fields = ('url', 'name', 'domain', 'mailboxes', 'forward') + + def get_fields(self, *args, **kwargs): + fields = super(AddressSerializer, self).get_fields(*args, **kwargs) + account = self.context['view'].request.user.account_id + mailboxes = fields['mailboxes'].queryset.select_related('user') + fields['mailboxes'].queryset = mailboxes.filter(user__account=account) + # TODO do it on permissions or in self.filter_by_account_field ? + domain = fields['domain'].queryset + fields['domain'].queryset = domain .filter(account=account) + return fields + + +router.insert('users', 'mailbox', NestedMailboxSerializer, required=False) diff --git a/orchestra/apps/users/roles/mail/settings.py b/orchestra/apps/users/roles/mail/settings.py new file mode 100644 index 00000000..fcca1e32 --- /dev/null +++ b/orchestra/apps/users/roles/mail/settings.py @@ -0,0 +1,29 @@ +from django.conf import settings + + +EMAILS_DOMAIN_MODEL = getattr(settings, 'EMAILS_DOMAIN_MODEL', 'domains.Domain') + +EMAILS_HOME = getattr(settings, 'EMAILS_HOME', '/home/%(username)s/') + +EMAILS_SIEVETEST_PATH = getattr(settings, 'EMAILS_SIEVETEST_PATH', '/dev/shm') + +EMAILS_SIEVETEST_BIN_PATH = getattr(settings, 'EMAILS_SIEVETEST_BIN_PATH', + '%(orchestra_root)s/bin/sieve-test') + + +EMAILS_VIRTUSERTABLE_PATH = getattr(settings, 'EMAILS_VIRTUSERTABLE_PATH', + '/etc/postfix/virtusertable') + + +EMAILS_VIRTDOMAINS_PATH = getattr(settings, 'EMAILS_VIRTDOMAINS_PATH', + '/etc/postfix/virtdomains') + + +EMAILS_DEFAUL_FILTERING = getattr(settings, 'EMAILS_DEFAULT_FILTERING', + 'require ["fileinto","regex","envelope","vacation","reject","relational","comparator-i;ascii-numeric"];\n' + '\n' + 'if header :value "ge" :comparator "i;ascii-numeric" "X-Spam-Score" "5" {\n' + ' fileinto "Junk";\n' + ' discard;\n' + '}' +) diff --git a/orchestra/apps/users/roles/mail/validators.py b/orchestra/apps/users/roles/mail/validators.py new file mode 100644 index 00000000..55d241a4 --- /dev/null +++ b/orchestra/apps/users/roles/mail/validators.py @@ -0,0 +1,63 @@ +import hashlib +import os +import re + +from django.core.validators import ValidationError, EmailValidator +from django.utils.translation import ugettext_lazy as _ + +from orchestra.utils import paths +from orchestra.utils.system import run + +from . import settings + + +def validate_emailname(value): + msg = _("'%s' is not a correct email name" % value) + if '@' in value: + raise ValidationError(msg) + value += '@localhost' + try: + EmailValidator(value) + except ValidationError: + raise ValidationError(msg) + + +#def validate_destination(value): +# """ space separated mailboxes or emails """ +# for destination in value.split(): +# msg = _("'%s' is not an existent mailbox" % destination) +# if '@' in destination: +# if not destination[-1].isalpha(): +# raise ValidationError(msg) +# EmailValidator(destination) +# else: +# from .models import Mailbox +# if not Mailbox.objects.filter(user__username=destination).exists(): +# raise ValidationError(msg) +# validate_emailname(destination) + + +def validate_forward(value): + """ space separated mailboxes or emails """ + for destination in value.split(): + EmailValidator(destination) + + +def validate_sieve(value): + from .models import Mailbox + sieve_name = '%s.sieve' % hashlib.md5(value).hexdigest() + path = os.path.join(settings.EMAILS_SIEVETEST_PATH, sieve_name) + with open(path, 'wb') as f: + f.write(value) + context = { + 'orchestra_root': paths.get_orchestra_root() + } + sievetest = settings.EMAILS_SIEVETEST_BIN_PATH % context + test = run(' '.join([sievetest, path, '/dev/null']), display=False) + if test.return_code: + 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)) diff --git a/orchestra/apps/users/roles/owncloud/__init__.py b/orchestra/apps/users/roles/owncloud/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/orchestra/apps/users/roles/posix/__init__.py b/orchestra/apps/users/roles/posix/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/orchestra/apps/users/roles/posix/admin.py b/orchestra/apps/users/roles/posix/admin.py new file mode 100644 index 00000000..45bbae6c --- /dev/null +++ b/orchestra/apps/users/roles/posix/admin.py @@ -0,0 +1,17 @@ +from django.contrib import admin +from django.contrib.auth import get_user_model +from django.utils.translation import ugettext_lazy as _ + +from orchestra.admin.utils import insertattr +from orchestra.apps.users.roles.admin import RoleAdmin + +from .models import POSIX + + +class POSIXRoleAdmin(RoleAdmin): + model = POSIX + name = 'posix' + url_name = 'posix' + + +insertattr(get_user_model(), 'roles', POSIXRoleAdmin) diff --git a/orchestra/apps/users/roles/posix/models.py b/orchestra/apps/users/roles/posix/models.py new file mode 100644 index 00000000..2164d4cd --- /dev/null +++ b/orchestra/apps/users/roles/posix/models.py @@ -0,0 +1,16 @@ +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from . import settings + + +class POSIX(models.Model): + user = models.OneToOneField('users.User', verbose_name=_("user"), + related_name='posix') + home = models.CharField(_("home"), max_length=256, blank=True, + help_text=_("Home directory relative to account's ~primary_user")) + shell = models.CharField(_("shell"), max_length=32, + choices=settings.POSIX_SHELLS, default=settings.POSIX_DEFAULT_SHELL) + + def __unicode__(self): + return str(self.user) diff --git a/orchestra/apps/users/roles/posix/serializers.py b/orchestra/apps/users/roles/posix/serializers.py new file mode 100644 index 00000000..3dc341c5 --- /dev/null +++ b/orchestra/apps/users/roles/posix/serializers.py @@ -0,0 +1,14 @@ +from rest_framework import serializers + +from orchestra.api import router + +from .models import POSIX + + +class POSIXSerializer(serializers.ModelSerializer): + class Meta: + model = POSIX + fields = ('home', 'shell') + + +router.insert('users', 'posix', POSIXSerializer, required=False) diff --git a/orchestra/apps/users/roles/posix/settings.py b/orchestra/apps/users/roles/posix/settings.py new file mode 100644 index 00000000..36860863 --- /dev/null +++ b/orchestra/apps/users/roles/posix/settings.py @@ -0,0 +1,11 @@ +from django.conf import settings +from django.utils.translation import ugettext, ugettext_lazy as _ + + +POSIX_SHELLS = getattr(settings, 'POSIX_SHELLS', ( + ('/bin/false', _("FTP/sFTP only")), + ('/bin/rsync', _("rsync shell")), + ('/bin/bash', "Bash"), +)) + +POSIX_DEFAULT_SHELL = getattr(settings, 'POSIX_DEFAULT_SHELL', '/bin/false') diff --git a/orchestra/apps/users/serializers.py b/orchestra/apps/users/serializers.py new file mode 100644 index 00000000..321e8b02 --- /dev/null +++ b/orchestra/apps/users/serializers.py @@ -0,0 +1,35 @@ +from django.contrib.auth import get_user_model +from django.forms import widgets +from django.utils.translation import ugettext, ugettext_lazy as _ +from rest_framework import serializers + +from orchestra.apps.accounts.serializers import AccountSerializerMixin +from orchestra.core.validators import validate_password + + +class UserSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer): + password = serializers.CharField(max_length=128, label=_('Password'), + validators=[validate_password], write_only=True, required=False, + widget=widgets.PasswordInput) + + class Meta: + model = get_user_model() + fields = ( + 'url', 'username', 'password', 'first_name', 'last_name', 'email', + 'is_admin', 'is_active', + ) + + def validate_password(self, attrs, source): + """ POST only password """ + if self.object.pk: + if 'password' in attrs: + raise serializers.ValidationError(_("Can not set password")) + elif 'password' not in attrs: + raise serializers.ValidationError(_("Password required")) + return attrs + + def save_object(self, obj, **kwargs): + # FIXME this method will be called when saving nested serializers :( + if not obj.pk: + obj.set_password(obj.password) + super(UserSerializer, self).save_object(obj, **kwargs) diff --git a/orchestra/apps/users/settings.py b/orchestra/apps/users/settings.py new file mode 100644 index 00000000..9521bf19 --- /dev/null +++ b/orchestra/apps/users/settings.py @@ -0,0 +1,4 @@ +from django.conf import settings + + +USERS_SYSTEMUSER_HOME = getattr(settings, 'USERES_SYSTEMUSER_HOME', '/home/%(username)s') diff --git a/orchestra/apps/users/templates/admin/users/user/change_form.html b/orchestra/apps/users/templates/admin/users/user/change_form.html new file mode 100644 index 00000000..82814f98 --- /dev/null +++ b/orchestra/apps/users/templates/admin/users/user/change_form.html @@ -0,0 +1,15 @@ +{% extends "admin/change_form.html" %} +{% load i18n admin_urls %} + +{% block object-tools-items %} +
  • {% trans "User" %}
  • +{% for item in roles %} +
  • {% if item.exists %}{{ item.name.capitalize }}{% else %}Add {{ item.name }}{% endif %}
  • +{% endfor %} +
  • + {% url opts|admin_urlname:'history' original.pk|admin_urlquote as history_url %} + {% trans "History" %} +
  • +{% if has_absolute_url %}
  • {% trans "View on site" %}
  • {% endif%} +{% endblock %} + diff --git a/orchestra/apps/users/templates/admin/users/user/delete_role.html b/orchestra/apps/users/templates/admin/users/user/delete_role.html new file mode 100644 index 00000000..ae94df22 --- /dev/null +++ b/orchestra/apps/users/templates/admin/users/user/delete_role.html @@ -0,0 +1,15 @@ +{% extends "admin/delete_confirmation.html" %} +{% load i18n admin_urls %} + + +{% block breadcrumbs %} + +{% endblock %} + diff --git a/orchestra/apps/users/templates/admin/users/user/role.html b/orchestra/apps/users/templates/admin/users/user/role.html new file mode 100644 index 00000000..75927310 --- /dev/null +++ b/orchestra/apps/users/templates/admin/users/user/role.html @@ -0,0 +1,70 @@ +{% extends "admin/base_site.html" %} +{% load i18n admin_urls admin_static admin_modify utils %} + + +{% block extrastyle %} +{{ block.super }} + +{{ media }} +{% endblock %} + +{% block coltype %}colM{% endblock %} +{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} change-form{% endblock %} + + +{% block breadcrumbs %} + +{% endblock %} + + + +{% block content %}
    +{% block object-tools %} + +{% endblock %} + +
    {% csrf_token %} +
    + {% for field in form %} +
    + + {% if not line.fields|length_is:'1' and not field.is_readonly %}{{ field.errors }}{% endif %} + {% if field|is_checkbox %} + {{ field }} + {% else %} + {{ field.label_tag }} {{ field }} + {% endif %} + {% if field.help_text %} +

    {{ field.help_text|safe }}

    + {% endif %} +
    +
    + {% endfor %} + + +
    + + {% if role.exists %}{% endif %} + +
    + + +{% endblock %} diff --git a/orchestra/apps/vps/__init__.py b/orchestra/apps/vps/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/orchestra/apps/vps/admin.py b/orchestra/apps/vps/admin.py new file mode 100644 index 00000000..236b82c8 --- /dev/null +++ b/orchestra/apps/vps/admin.py @@ -0,0 +1,50 @@ +from django.conf.urls import patterns +from django.contrib import admin +from django.contrib.auth.admin import UserAdmin +from django.core.urlresolvers import reverse +from django.utils.translation import ugettext_lazy as _ + +from orchestra.admin import ExtendedModelAdmin +from orchestra.apps.accounts.admin import AccountAdminMixin + +from .forms import VPSChangeForm, VPSCreationForm +from .models import VPS + + +class VPSAdmin(AccountAdminMixin, ExtendedModelAdmin): + list_display = ('hostname', 'type', 'template', 'account_link') + list_filter = ('type', 'template') + form = VPSChangeForm + add_form = VPSCreationForm + readonly_fields = ('account_link',) + change_readonly_fields = ('account', 'name', 'type', 'template') + fieldsets = ( + (None, { + 'classes': ('wide',), + 'fields': ('account_link', 'hostname', 'type', 'template') + }), + (_("Login"), { + 'classes': ('wide',), + 'fields': ('password',) + }) + ) + add_fieldsets = ( + (None, { + 'classes': ('wide',), + 'fields': ('account', 'hostname', 'type', 'template') + }), + (_("Login"), { + 'classes': ('wide',), + 'fields': ('password1', 'password2',) + }), + ) + + def get_urls(self): + useradmin = UserAdmin(VPS, self.admin_site) + return patterns('', + (r'^(\d+)/password/$', + self.admin_site.admin_view(useradmin.user_change_password)) + ) + super(VPSAdmin, self).get_urls() + + +admin.site.register(VPS, VPSAdmin) diff --git a/orchestra/apps/vps/forms.py b/orchestra/apps/vps/forms.py new file mode 100644 index 00000000..22ed7574 --- /dev/null +++ b/orchestra/apps/vps/forms.py @@ -0,0 +1,39 @@ +from django import forms +from django.contrib.auth.forms import UserCreationForm, ReadOnlyPasswordHashField +from django.utils.translation import ugettext_lazy as _ + + +class VPSCreationForm(forms.ModelForm): + password1 = forms.CharField(label=_("Password"), + widget=forms.PasswordInput) + password2 = forms.CharField(label=_("Password confirmation"), + widget=forms.PasswordInput, + help_text=_("Enter the same password as above, for verification.")) + + class Meta: + fields = ('username', 'account', 'type', 'template') + + def clean_password2(self): + password1 = self.cleaned_data.get("password1") + password2 = self.cleaned_data.get("password2") + if password1 and password2 and password1 != password2: + msg = _("The two password fields didn't match.") + raise forms.ValidationError(msg) + return password2 + + def save(self, commit=True): + vps = super(VPSCreationForm, self).save(commit=False) + vps.set_password(self.cleaned_data["password1"]) + if commit: + vps.save() + return vps + + +class VPSChangeForm(forms.ModelForm): + password = ReadOnlyPasswordHashField(label=_("Password"), + help_text=_("Raw passwords are not stored, so there is no way to see " + "this user's password, but you can change the password " + "using this form.")) + + def clean_password(self): + return self.initial["password"] diff --git a/orchestra/apps/vps/models.py b/orchestra/apps/vps/models.py new file mode 100644 index 00000000..476acad9 --- /dev/null +++ b/orchestra/apps/vps/models.py @@ -0,0 +1,37 @@ +from django.db import models +from django.utils.translation import ugettext_lazy as _ +from django.contrib.auth.hashers import make_password + +from orchestra.core import services +from orchestra.core.validators import validate_hostname + +from . import settings + + +class VPS(models.Model): + hostname = models.CharField(_("hostname"), max_length=256, unique=True, + validators=[validate_hostname]) + type = models.CharField(_("type"), max_length=64, choices=settings.VPS_TYPES, + default=settings.VPS_DEFAULT_TYPE) + template = models.CharField(_("template"), max_length=64, + choices=settings.VPS_TEMPLATES, default=settings.VPS_DEFAULT_TEMPLATE) + password = models.CharField(_('password'), max_length=128, + help_text=_("root password of this virtual machine")) + account = models.ForeignKey('accounts.Account', verbose_name=_("Account"), + related_name='vpss') + + class Meta: + verbose_name = "VPS" + verbose_name_plural = "VPSs" + + def __unicode__(self): + return self.hostname + + def set_password(self, raw_password): + self.password = make_password(raw_password) + + def get_username(self): + return self.hostname + + +services.register(VPS) diff --git a/orchestra/apps/vps/settings.py b/orchestra/apps/vps/settings.py new file mode 100644 index 00000000..60179aad --- /dev/null +++ b/orchestra/apps/vps/settings.py @@ -0,0 +1,17 @@ +from django.conf import settings + + +VPS_TYPES = getattr(settings, 'VPS_TYPES', ( + ('openvz', 'OpenVZ container'), +)) + + +VPS_DEFAULT_TYPE = getattr(settings, 'VPS_DEFAULT_TYPE', 'openvz') + + +VPS_TEMPLATES = getattr(settings, 'VPS_TEMPLATES', ( + ('debian7', 'Debian 7 - Wheezy'), +)) + + +VPS_DEFAULT_TEMPLATE = getattr(settings, 'VPS_DEFAULT_TEMPLATE', 'debian7') diff --git a/orchestra/apps/webapps/__init__.py b/orchestra/apps/webapps/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/orchestra/apps/webapps/admin.py b/orchestra/apps/webapps/admin.py new file mode 100644 index 00000000..00aca017 --- /dev/null +++ b/orchestra/apps/webapps/admin.py @@ -0,0 +1,48 @@ +from django import forms +from django.contrib import admin +from django.core.urlresolvers import reverse +from django.utils.translation import ugettext_lazy as _ + +from orchestra.admin import ExtendedModelAdmin +from orchestra.apps.accounts.admin import SelectAccountAdminMixin + +from .models import WebApp, WebAppOption + + +class WebAppOptionInline(admin.TabularInline): + model = WebAppOption + extra = 1 + + class Media: + css = { + 'all': ('orchestra/css/hide-inline-id.css',) + } + + def formfield_for_dbfield(self, db_field, **kwargs): + """ Make value input widget bigger """ + if db_field.name == 'value': + kwargs['widget'] = forms.TextInput(attrs={'size':'100'}) + return super(WebAppOptionInline, self).formfield_for_dbfield(db_field, **kwargs) + + +class WebAppAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): + fields = ('account_link', 'name', 'type') + list_display = ('name', 'type', 'display_websites', 'account_link') + list_filter = ('type',) + inlines = [WebAppOptionInline] + readonly_fields = ('account_link',) + change_readonly_fields = ('name', 'type') + + def display_websites(self, webapp): + websites = [] + for content in webapp.content_set.all().select_related('website'): + website = content.website + url = reverse('admin:websites_website_change', args=(website.pk,)) + name = "%s on %s" % (website.name, content.path) + websites.append('%s' % (url, name)) + return '
    '.join(websites) + display_websites.short_description = _("web sites") + display_websites.allow_tags = True + + +admin.site.register(WebApp, WebAppAdmin) diff --git a/orchestra/apps/webapps/api.py b/orchestra/apps/webapps/api.py new file mode 100644 index 00000000..3e8eac7d --- /dev/null +++ b/orchestra/apps/webapps/api.py @@ -0,0 +1,28 @@ +from rest_framework import viewsets +from rest_framework.response import Response + +from orchestra.api import router, collectionlink +from orchestra.apps.accounts.api import AccountApiMixin + +from . import settings +from .models import WebApp +from .serializers import WebAppSerializer + + +class WebAppViewSet(AccountApiMixin, viewsets.ModelViewSet): + model = WebApp + serializer_class = WebAppSerializer + filter_fields = ('name',) + + @collectionlink() + def configuration(self, request): + names = [ + 'WEBAPPS_BASE_ROOT', 'WEBAPPS_TYPES', 'WEBAPPS_WEBAPP_OPTIONS', + 'WEBAPPS_PHP_DISABLED_FUNCTIONS', 'WEBAPPS_DEFAULT_TYPE' + ] + return Response({ + name: getattr(settings, name, None) for name in names + }) + + +router.register(r'webapps', WebAppViewSet) diff --git a/orchestra/apps/webapps/backends/__init__.py b/orchestra/apps/webapps/backends/__init__.py new file mode 100644 index 00000000..07763320 --- /dev/null +++ b/orchestra/apps/webapps/backends/__init__.py @@ -0,0 +1,26 @@ +import pkgutil + + +class WebAppServiceMixin(object): + model = 'webapps.WebApp' + + def create_webapp_dir(self, context): + self.append("mkdir -p '%(app_path)s'" % context) + self.append("chown %(user)s.%(group)s '%(app_path)s'" % context) + + def delete_webapp_dir(self, context): + self.append("rm -fr %(app_path)s" % context) + + def get_context(self, webapp): + return { + 'user': webapp.account.user.username, + 'group': webapp.account.user.username, + 'app_name': webapp.name, + 'type': webapp.type, + 'app_path': webapp.get_path(), + 'banner': self.get_banner(), + } + + +for __, module_name, __ in pkgutil.walk_packages(__path__): + exec('from . import %s' % module_name) diff --git a/orchestra/apps/webapps/backends/awstats.py b/orchestra/apps/webapps/backends/awstats.py new file mode 100644 index 00000000..578c8a6d --- /dev/null +++ b/orchestra/apps/webapps/backends/awstats.py @@ -0,0 +1,12 @@ +from django.utils.translation import ugettext_lazy as _ + +from orchestra.apps.orchestration import ServiceBackend + +from . import WebAppServiceMixin + + +class AwstatsBackend(WebAppServiceMixin, ServiceBackend): + verbose_name = _("Awstats") + + def save(self, webapp): + pass diff --git a/orchestra/apps/webapps/backends/dokuwikimu.py b/orchestra/apps/webapps/backends/dokuwikimu.py new file mode 100644 index 00000000..0356af23 --- /dev/null +++ b/orchestra/apps/webapps/backends/dokuwikimu.py @@ -0,0 +1,28 @@ +from django.utils.translation import ugettext_lazy as _ + +from orchestra.apps.orchestration import ServiceBackend + +from . import WebAppServiceMixin +from .. import settings + +class DokuWikiMuBackend(WebAppServiceMixin, ServiceBackend): + verbose_name = _("DokuWiki multisite") + + def save(self, webapp): + context = self.get_context(webapp) + self.append("mkdir %(app_path)" % context) + self.append("tar xfz %(template)s -C %(app_path)s" % context) + self.append("chown -R www-data %(app_path)s" % context) + # TODO move dokuwiki to user directory + + def delete(self, webapp): + context = self.get_context(webapp) + self.append("rm -fr %(app_path)s" % context) + + def get_context(self, webapp): + context = super(DokuwikiMuBackend, self).get_context(webapp) + context.update({ + 'template': settings.WEBAPPS_DOKUWIKIMU_TEMPLATE_PATH, + 'app_path': os.path.join(settings.WEBAPPS_DOKUWIKIMU_FARM_PATH, webapp.name) + }) + return context diff --git a/orchestra/apps/webapps/backends/drupalmu.py b/orchestra/apps/webapps/backends/drupalmu.py new file mode 100644 index 00000000..4ca40a0d --- /dev/null +++ b/orchestra/apps/webapps/backends/drupalmu.py @@ -0,0 +1,35 @@ +import os + +from django.utils.translation import ugettext_lazy as _ + +from orchestra.apps.orchestration import ServiceBackend + +from . import WebAppServiceMixin +from .. import settings + + +class DrupalMuBackend(WebAppServiceMixin, ServiceBackend): + verbose_name = _("Drupal multisite") + + def save(self, webapp): + context = self.get_context(webapp) + self.append("mkdir %(drupal_path)s" % context) + self.append("chown -R www-data %(drupal_path)s" % context) + self.append( + "# the following assumes settings.php to be previously configured\n" + "REGEX='^\s*$databases\[.default.\]\[.default.\]\[.prefix.\]'\n" + "CONFIG='$databases[\'default\'][\'default\'][\'prefix\'] = \'%(app_name)s_\';'\n" + "if [[ ! $(grep $REGEX %(drupal_settings)s) ]]; then\n" + " echo $CONFIG >> %(drupal_settings)s\n" + "fi" % context + ) + + def selete(self, webapp): + context = self.get_context(webapp) + self.append("rm -fr %(app_path)s" % context) + + def get_context(self, webapp): + context = super(DrupalMuBackend, self).get_context(webapp) + context['drupal_path'] = settings.WEBAPPS_DRUPAL_SITES_PATH % context + context['drupal_settings'] = os.path.join(context['drupal_path'], 'settings.php') + return context diff --git a/orchestra/apps/webapps/backends/phpfcgid.py b/orchestra/apps/webapps/backends/phpfcgid.py new file mode 100644 index 00000000..ec808519 --- /dev/null +++ b/orchestra/apps/webapps/backends/phpfcgid.py @@ -0,0 +1,48 @@ +import os + +from django.utils.translation import ugettext_lazy as _ + +from orchestra.apps.orchestration import ServiceBackend + +from . import WebAppServiceMixin +from .. import settings + + +class PHPFcgidBackend(WebAppServiceMixin, ServiceBackend): + verbose_name = _("PHP-Fcgid") + + def save(self, webapp): + context = self.get_context(webapp) + self.create_webapp_dir(context) + self.append("mkdir -p %(wrapper_dir)s" % context) + self.append( + "{ echo -e '%(wrapper_content)s' | diff -N -I'^\s*#' %(wrapper_path)s - ; } ||" + " { echo -e '%(wrapper_content)s' > %(wrapper_path)s; UPDATED=1; }" % context) + self.append("chmod +x %(wrapper_path)s" % context) + self.append("chown -R %(user)s.%(group)s %(wrapper_dir)s" % context) + + def delete(self, webapp): + context = self.get_context(webapp) + self.delete_webapp_dir(context) + + def get_context(self, webapp): + context = super(PHPFcgidBackend, self).get_context(webapp) + init_vars = webapp.get_php_init_vars() + if init_vars: + init_vars = [ '%s="%s"' % (k,v) for v,k in init_vars.iteritems() ] + init_vars = ', -d '.join(init_vars) + context['init_vars'] = '-d %s' % init_vars + else: + context['init_vars'] = '' + wrapper_path = settings.WEBAPPS_FCGID_PATH % context + context.update({ + 'wrapper_content': ( + "#!/bin/sh\n" + "# %(banner)s\n" + "export PHPRC=/etc/%(type)s/cgi/\n" + "exec /usr/bin/%(type)s-cgi %(init_vars)s\n" + ) % context, + 'wrapper_path': wrapper_path, + 'wrapper_dir': os.path.dirname(wrapper_path), + }) + return context diff --git a/orchestra/apps/webapps/backends/phpfpm.py b/orchestra/apps/webapps/backends/phpfpm.py new file mode 100644 index 00000000..edcc9963 --- /dev/null +++ b/orchestra/apps/webapps/backends/phpfpm.py @@ -0,0 +1,58 @@ +import os + +from django.template import Template, Context +from django.utils.translation import ugettext_lazy as _ + +from orchestra.apps.orchestration import ServiceBackend + +from . import WebAppServiceMixin +from .. import settings + + +class PHPFPMBackend(WebAppServiceMixin, ServiceBackend): + verbose_name = _("PHP-FPM") + + def save(self, webapp): + context = self.get_context(webapp) + self.create_webapp_dir(context) + self.append( + "{ echo -e '%(fpm_config)s' | diff -N -I'^\s*;;' %(fpm_path)s - ; } ||" + " { echo -e '%(fpm_config)s' > %(fpm_path)s; UPDATEDFPM=1; }" % context + ) + + def delete(self, webapp): + context = self.get_context(webapp) + self.delete_webapp_dir(context) + + def commit(self): + super(PHPFPMBackend, self).commit() + self.append('[[ $UPDATEDFPM == 1 ]] && service php5-fpm reload') + + def get_context(self, webapp): + context = super(PHPFPMBackend, self).get_context(webapp) + context.update({ + 'init_vars': webapp.get_php_init_vars(), + 'fpm_port': webapp.get_fpm_port(), + }) + context['fpm_listen'] = settings.WEBAPPS_FPM_LISTEN % context + fpm_config = Template( + "[{{ user }}]\n" + ";; {{ banner }}\n" + "user = {{ user }}\n" + "group = {{ group }}\n\n" + "listen = {{ fpm_listen | safe }}\n" + "listen.owner = {{ user }}\n" + "listen.group = {{ group }}\n" + "pm = ondemand\n" + "pm.max_children = 4\n" + "{% for name,value in init_vars.iteritems %}" + "php_admin_value[{{ name | safe }}] = {{ value | safe }}\n" + "{% endfor %}" + ) + fpm_file = '%(user)s.conf' % context + context.update({ + 'fpm_config': fpm_config.render(Context(context)), + 'fpm_path': os.path.join(settings.WEBAPPS_PHPFPM_POOL_PATH, fpm_file), + }) + return context + diff --git a/orchestra/apps/webapps/backends/static.py b/orchestra/apps/webapps/backends/static.py new file mode 100644 index 00000000..54ca7c5b --- /dev/null +++ b/orchestra/apps/webapps/backends/static.py @@ -0,0 +1,17 @@ +from django.utils.translation import ugettext_lazy as _ + +from orchestra.apps.orchestration import ServiceBackend + +from . import WebAppServiceMixin + + +class StaticBackend(WebAppServiceMixin, ServiceBackend): + verbose_name = _("Static") + + def save(self, webapp): + context = self.get_context(webapp) + self.create_webapp_dir(context) + + def delete(self, webapp): + context = self.get_context(webapp) + self.delete_webapp_dir(context) diff --git a/orchestra/apps/webapps/backends/wordpressmu.py b/orchestra/apps/webapps/backends/wordpressmu.py new file mode 100644 index 00000000..de682b4b --- /dev/null +++ b/orchestra/apps/webapps/backends/wordpressmu.py @@ -0,0 +1,101 @@ +import re +import sys + +import requests +from django.utils.translation import ugettext_lazy as _ + +from orchestra.apps.orchestration import ServiceBackend + +from . import WebAppServiceMixin +from .. import settings + + +class WordpressMuBackend(WebAppServiceMixin, ServiceBackend): + verbose_name = _("Wordpress multisite") + + @property + def script(self): + return self.cmds + + def login(self, session): + base_url = self.get_base_url() + login_url = base_url + '/wp-login.php' + login_data = { + 'log': 'admin', + 'pwd': settings.WEBAPPS_WORDPRESSMU_ADMIN_PASSWORD, + 'redirect_to': '/wp-admin/' + } + response = session.post(login_url, data=login_data) + if response.url != base_url + '/wp-admin/': + raise IOError("Failure login to remote application") + + def get_base_url(self): + base_url = settings.WEBAPPS_WORDPRESSMU_BASE_URL + if base_url.endswith('/'): + base_url = base_url[:-1] + return base_url + + def create_blog(self, webapp, server): + emails = webapp.account.contacts.filter(email_usage__contains='') + email = emails.values_list('email', flat=True).first() + + base_url = self.get_base_url() + session = requests.Session() + self.login(session) + + url = base_url + '/wp-admin/network/site-new.php' + content = session.get(url).content + wpnonce = re.compile('name="_wpnonce_add-blog"\s+value="([^"]*)"') + wpnonce = wpnonce.search(content).groups()[0] + + url += '?action=add-site' + data = { + 'blog[domain]': webapp.name, + 'blog[title]': webapp.name, + 'blog[email]': email, + '_wpnonce_add-blog': wpnonce, + } + response = session.post(url, data=data) + + def delete_blog(self, webapp, server): + # OH, I've enjoied so much coding this methods that I want to thanks + # the wordpress team for the excellent software they are producing + context = self.get_context(webapp) + session = requests.Session() + self.login(session) + + base_url = self.get_base_url() + search = base_url + '/wp-admin/network/sites.php?s=%(name)s&action=blogs' % context + regex = re.compile( + '%(name)s' % context + ) + content = session.get(search).content + ids = regex.search(content).groups() + if len(ids) > 1: + raise ValueError("Multiple matches") + + delete = re.compile('(.*)') + content = delete.search(content).groups()[0] + wpnonce = re.compile('_wpnonce=([^"]*)"') + wpnonce = wpnonce.search(content).groups()[0] + delete = '/wp-admin/network/sites.php?action=confirm&action2=deleteblog' + delete += '&id=%d&_wpnonce=%d' % (ids[0], wpnonce) + + content = session.get(delete).content + wpnonce = re.compile('name="_wpnonce"\s+value="([^"]*)"') + wpnonce = wpnonce.search(content).groups()[0] + data = { + 'action': 'deleteblog', + 'id': ids[0], + '_wpnonce': wpnonce, + '_wp_http_referer': '/wp-admin/network/sites.php', + } + delete = base_url + '/wp-admin/network/sites.php?action=deleteblog' + session.post(delete, data=data) + + def save(self, webapp): + self.append(self.create_blog, webapp) + + def delete(self, webapp): + self.append(self.delete_blog, webapp) diff --git a/orchestra/apps/webapps/models.py b/orchestra/apps/webapps/models.py new file mode 100644 index 00000000..573f5817 --- /dev/null +++ b/orchestra/apps/webapps/models.py @@ -0,0 +1,98 @@ +import re + +from django.core.exceptions import ValidationError +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from orchestra.core import validators, services +from orchestra.utils.functional import cached + +from . import settings + + +def settings_to_choices(choices): + return sorted( + [ (name, opt[0]) for name,opt in choices.iteritems() ], + key=lambda e: e[1] + ) + + +class WebApp(models.Model): + """ Represents a web application """ + name = models.CharField(_("name"), max_length=128, + validators=[validators.validate_name]) + type = models.CharField(_("type"), max_length=32, + choices=settings_to_choices(settings.WEBAPPS_TYPES), + default=settings.WEBAPPS_DEFAULT_TYPE) + account = models.ForeignKey('accounts.Account', verbose_name=_("Account"), + related_name='webapps') + + class Meta: + unique_together = ('name', 'account') + verbose_name = _("Web App") + verbose_name_plural = _("Web Apps") + + def __unicode__(self): + return self.name + + @cached + def get_options(self): + return { opt.name: opt.value for opt in self.options.all() } + + def get_php_init_vars(self): + init_vars = [] + options = WebAppOption.objects.filter(webapp__type=self.type) + for opt in options.filter(webapp__account=self.account): + name = opt.name.replace('PHP-', '') + value = "%s" % opt.value + init_vars.append((name, value)) + enabled_functions = self.options.filter(name='enabled_functions') + if enabled_functions: + enabled_functions = enabled_functions.get().value.split(',') + disabled_functions = [] + for function in settings.WEBAPPS_PHP_DISABLED_FUNCTIONS: + if function not in enabled_functions: + disabled_functions.append(function) + init_vars.append(('dissabled_functions', ','.join(disabled_functions))) + return init_vars + + def get_fpm_port(self): + return settings.WEBAPPS_FPM_START_PORT + self.account.user.pk + + def get_method(self): + method = settings.WEBAPPS_TYPES[self.type] + args = method[2] if len(method) == 4 else () + return method[1], args + + def get_path(self): + context = { + 'user': self.account.user, + 'app_name': self.name, + } + return settings.WEBAPPS_BASE_ROOT % context + + +class WebAppOption(models.Model): + webapp = models.ForeignKey(WebApp, verbose_name=_("Web application"), + related_name='options') + name = models.CharField(_("name"), max_length=128, + choices=settings_to_choices(settings.WEBAPPS_OPTIONS)) + value = models.CharField(_("value"), max_length=256) + + class Meta: + unique_together = ('webapp', 'name') + verbose_name = _("option") + verbose_name_plural = _("options") + + def __unicode__(self): + return self.name + + def clean(self): + """ validates name and value according to WEBAPPS_OPTIONS """ + __, regex = settings.WEBAPPS_OPTIONS[self.name] + if not re.match(regex, self.value): + msg = _("'%s' does not match %s") + raise ValidationError(msg % (self.value, regex)) + + +services.register(WebApp) diff --git a/orchestra/apps/webapps/serializers.py b/orchestra/apps/webapps/serializers.py new file mode 100644 index 00000000..111abcae --- /dev/null +++ b/orchestra/apps/webapps/serializers.py @@ -0,0 +1,14 @@ +from rest_framework import serializers + +from orchestra.api.fields import OptionField +from orchestra.apps.accounts.serializers import AccountSerializerMixin + +from .models import WebApp + + +class WebAppSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer): + options = OptionField(required=False) + + class Meta: + model = WebApp + fields = ('url', 'name', 'type', 'options') diff --git a/orchestra/apps/webapps/settings.py b/orchestra/apps/webapps/settings.py new file mode 100644 index 00000000..28254331 --- /dev/null +++ b/orchestra/apps/webapps/settings.py @@ -0,0 +1,200 @@ +from django.conf import settings +from django.utils.translation import ugettext_lazy as _ + + +WEBAPPS_BASE_ROOT = getattr(settings, 'WEBAPPS_BASE_ROOT', '/home/%(user)s/webapps/%(app_name)s/') + + +WEBAPPS_FPM_LISTEN = getattr(settings, 'WEBAPPS_FPM_LISTEN', +# '/var/run/%(user)s-%(app_name)s.sock') + '127.0.0.1:%(fpm_port)s') + +WEBAPPS_FPM_START_PORT = getattr(settings, 'WEBAPPS_FPM_START_PORT', 10000) + +WEBAPPS_FCGID_PATH = getattr(settings, 'WEBAPPS_FCGID_PATH', + '/home/httpd/fcgid/%(user)s/%(type)s-wrapper') + + +WEBAPPS_TYPES = getattr(settings, 'WEBAPPS_TYPES', { + # { name: ( verbose_name, method_name, method_args, description) } + 'php5.5': ( + _("PHP 5.5"), +# 'fpm', ('unix:/var/run/%(user)s-%(app_name)s.sock|fcgi://127.0.0.1%(app_path)s',), + 'fpm', ('fcgi://127.0.0.1:%(fpm_port)s%(app_path)s',), + _("This creates a PHP5.5 application under ~/webapps/\n" + "PHP-FPM will be used to execute PHP files.") + ), + 'php5': ( + _("PHP 5"), + 'fcgid', (WEBAPPS_FCGID_PATH,), + _("This creates a PHP5.2 application under ~/webapps/\n" + "Apache-mod-fcgid will be used to execute PHP files.") + ), + 'php4': ( + _("PHP 4"), + 'fcgid', (WEBAPPS_FCGID_PATH,), + _("This creates a PHP4 application under ~/webapps/\n" + "Apache-mod-fcgid will be used to execute PHP files.") + ), + 'static': ( + _("Static"), + 'alias', (), + _("This creates a Static application under ~/webapps/\n" + "Apache2 will be used to serve static content and execute CGI files.") + ), + 'wordpressmu': ( + _("Wordpress (shared)"), + 'fpm', ('fcgi://127.0.0.1:8990/home/httpd/wordpress-mu/',), + _("This creates a Wordpress site into a shared Wordpress server\n" + "By default this blog will be accessible via http://.blogs.example.com") + + ), + 'dokuwikimu': ( + _("DokuWiki (shared)"), + 'alias', ('/home/httpd/wikifarm/farm/',), + _("This create a Dokuwiki wiki into a shared Dokuwiki server\n") + ), + 'drupalmu': ( + _("Drupdal (shared)"), + 'fpm', ('fcgi://127.0.0.1:8991/home/httpd/drupal-mu/',), + _("This creates a Drupal site into a shared Drupal server\n" + "The installation will be completed after visiting " + "http://.drupal.example.com/install.php?profile=standard&locale=ca\n" + "By default this site will be accessible via http://.drupal.example.com") + ), + 'webalizer': ( + _("Webalizer"), + 'alias', ('%(app_path)s%(site_name)s',), + _("This creates a Webalizer application under ~/webapps/-\n") + ), +}) + + +WEBAPPS_DEFAULT_TYPE = getattr(settings, 'WEBAPPS_DEFAULT_TYPE', 'php5.5') + + +WEBAPPS_DEFAULT_HTTPS_CERT = getattr(settings, 'WEBAPPS_DEFAULT_HTTPS_CERT', + ('/etc/apache2/cert', '/etc/apache2/cert.key') +) + + +WEBAPPS_OPTIONS = getattr(settings, 'WEBAPPS_OPTIONS', { + # { name: ( verbose_name, validation_regex ) } + # PHP + 'enabled_functions': ( + _("PHP - Enabled functions"), + r'^[\w.,-]+$' + ), + 'PHP-register_globals': ( + _("PHP - Register globals"), + r'^(On|Off|on|off)$' + ), + 'PHP-allow_url_include': ( + _("PHP - Allow URL include"), + r'^(On|Off|on|off)$' + ), + 'PHP-auto_append_file': ( + _("PHP - Auto append file"), + r'^none$' + ), + 'PHP-default_socket_timeout': ( + _("PHP - Default socket timeout"), + r'P^[0-9][0-9]?[0-9]?$' + ), + 'PHP-display_errors': ( + _("PHP - Display errors"), + r'^(On|Off|on|off)$' + ), + 'PHP-magic_quotes_gpc': ( + _("PHP - Magic quotes GPC"), + r'^(On|Off|on|off)$' + ), + 'PHP-max_execution_time': ( + _("PHP - Max execution time"), + r'^[0-9][0-9]?[0-9]?$' + ), + 'PHP-max_input_time': ( + _("PHP - Max input time"), + r'^[0-9][0-9]?[0-9]?$' + ), + 'PHP-memory_limit': ( + _("PHP - Memory limit"), + r'^[0-9][0-9]?[0-9]?M$' + ), + 'PHP-mysql.connect_timeout': ( + _("PHP - Mysql connect timeout"), + r'^[0-9][0-9]?[0-9]?$' + ), + 'PHP-post_max_size': ( + _("PHP - Post max size"), + r'^[0-9][0-9]?M$' + ), + 'PHP-safe_mode': ( + _("PHP - Safe mode"), + r'^(On|Off|on|off)$' + ), + 'PHP-suhosin.post.max_vars': ( + _("PHP - Suhosin post max vars"), + r'^[0-9][0-9]?[0-9]?[0-9]?$' + ), + 'PHP-suhosin.request.max_vars': ( + _("PHP - Suhosin request max vars"), + r'^[0-9][0-9]?[0-9]?[0-9]?$' + ), + 'PHP-suhosin.simulation': ( + _("PHP - Suhosin simulation"), + r'^(On|Off|on|off)$' + ), + # FCGID + 'FcgidIdleTimeout': ( + _("FCGI - Idle timeout"), + r'^[0-9][0-9]?[0-9]?$' + ), + 'FcgidBusyTimeout': ( + _("FCGI - Busy timeout"), + r'^[0-9][0-9]?[0-9]?$' + ), + 'FcgidConnectTimeout': ( + _("FCGI - Connection timeout"), + r'^[0-9][0-9]?[0-9]?$' + ), + 'FcgidIOTimeout': ( + _("FCGI - IO timeout"), + r'^[0-9][0-9]?[0-9]?$' + ), +}) + + +WEBAPPS_PHP_DISABLED_FUNCTIONS = getattr(settings, 'WEBAPPS_PHP_DISABLED_FUNCTION', [ + 'exec', 'passthru', 'shell_exec', 'system', 'proc_open', 'popen', 'curl_exec', + 'curl_multi_exec', 'show_source', 'pcntl_exec', 'proc_close', + 'proc_get_status', 'proc_nice', 'proc_terminate', 'ini_alter', 'virtual', + 'openlog', 'escapeshellcmd', 'escapeshellarg', 'dl' +]) + + +WEBAPPS_WORDPRESSMU_BASE_URL = getattr(settings, 'WEBAPPS_WORDPRESSMU_BASE_URL', + 'http://blogs.example.com') + + +WEBAPPS_WORDPRESSMU_ADMIN_PASSWORD = getattr(settings, 'WEBAPPS_WORDPRESSMU_ADMIN_PASSWORD', + 'secret') + + + + + +WEBAPPS_DOKUWIKIMU_TEMPLATE_PATH = setattr(settings, 'WEBAPPS_DOKUWIKIMU_TEMPLATE_PATH', + '/home/httpd/htdocs/wikifarm/template.tar.gz') + + +WEBAPPS_DOKUWIKIMU_FARM_PATH = getattr(settings, 'WEBAPPS_DOKUWIKIMU_FARM_PATH', + '/home/httpd/htdocs/wikifarm/farm') + + +WEBAPPS_DRUPAL_SITES_PATH = getattr(settings, 'WEBAPPS_DRUPAL_SITES_PATH', + '/home/httpd/htdocs/drupal-mu/sites/%(app_name)s') + + +WEBAPPS_PHPFPM_POOL_PATH = getattr(settings, 'WEBAPPS_PHPFPM_POOL_PATH', + '/etc/php5/fpm/pool.d') diff --git a/orchestra/apps/websites/__init__.py b/orchestra/apps/websites/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/orchestra/apps/websites/admin.py b/orchestra/apps/websites/admin.py new file mode 100644 index 00000000..1e04f7e7 --- /dev/null +++ b/orchestra/apps/websites/admin.py @@ -0,0 +1,93 @@ +from django import forms +from django.contrib import admin +from django.core.urlresolvers import reverse +from django.utils.safestring import mark_safe +from django.utils.translation import ugettext_lazy as _ + +from orchestra.admin import ExtendedModelAdmin +from orchestra.admin.utils import link +from orchestra.apps.accounts.admin import AccountAdminMixin, SelectAccountAdminMixin +from orchestra.apps.accounts.widgets import account_related_field_widget_factory + +from .models import Content, Website, WebsiteOption + + +class WebsiteOptionInline(admin.TabularInline): + model = WebsiteOption + extra = 1 + + class Media: + css = { + 'all': ('orchestra/css/hide-inline-id.css',) + } + + def formfield_for_dbfield(self, db_field, **kwargs): + """ Make value input widget bigger """ + if db_field.name == 'value': + kwargs['widget'] = forms.TextInput(attrs={'size':'100'}) + return super(WebsiteOptionInline, self).formfield_for_dbfield(db_field, **kwargs) + + +class ContentInline(AccountAdminMixin, admin.TabularInline): + model = Content + extra = 1 + fields = ('webapp', 'webapp_link', 'webapp_type', 'path') + readonly_fields = ('webapp_link', 'webapp_type') + filter_by_account_fields = ['webapp'] + + webapp_link = link('webapp', popup=True) + webapp_link.short_description = _("Web App") + + def webapp_type(self, content): + if not content.pk: + return '' + return content.webapp.get_type_display() + webapp_type.short_description = _("Web App type") + + +class WebsiteAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): + list_display = ('name', 'display_domains', 'display_webapps', 'account_link') + list_filter = ('port', 'is_active') + change_readonly_fields = ('name',) + inlines = [ContentInline, WebsiteOptionInline] + filter_horizontal = ['domains'] + fieldsets = ( + (None, { + 'classes': ('extrapretty',), + 'fields': ('account_link', 'name', 'port', 'domains', 'is_active'), + }), + ) + filter_by_account_fields = ['domains'] + + def display_domains(self, website): + domains = [] + for domain in website.domains.all(): + url = '%s://%s' % (website.protocol, domain) + domains.append('%s' % (url, url)) + return '
    '.join(domains) + display_domains.short_description = _("domains") + display_domains.allow_tags = True + + def display_webapps(self, website): + webapps = [] + for content in website.content_set.all().select_related('webapp'): + webapp = content.webapp + url = reverse('admin:webapps_webapp_change', args=(webapp.pk,)) + name = "%s on %s" % (webapp.get_type_display(), content.path) + webapps.append('%s' % (url, name)) + return '
    '.join(webapps) + display_webapps.allow_tags = True + display_webapps.short_description = _("Web apps") + + def formfield_for_dbfield(self, db_field, **kwargs): + if db_field.name == 'root': + kwargs['widget'] = forms.TextInput(attrs={'size':'100'}) + return super(WebsiteAdmin, self).formfield_for_dbfield(db_field, **kwargs) + + def queryset(self, request): + """ Select related for performance """ + qs = super(WebsiteAdmin, self).queryset(request) + return qs.prefetch_related('domains') + + +admin.site.register(Website, WebsiteAdmin) diff --git a/orchestra/apps/websites/api.py b/orchestra/apps/websites/api.py new file mode 100644 index 00000000..7936cf2c --- /dev/null +++ b/orchestra/apps/websites/api.py @@ -0,0 +1,25 @@ +from rest_framework import viewsets +from rest_framework.response import Response + +from orchestra.api import router, collectionlink +from orchestra.apps.accounts.api import AccountApiMixin + +from . import settings +from .models import Website +from .serializers import WebsiteSerializer + + +class WebsiteViewSet(AccountApiMixin, viewsets.ModelViewSet): + model = Website + serializer_class = WebsiteSerializer + filter_fields = ('name',) + + @collectionlink() + def configuration(self, request): + names = ['WEBSITES_OPTIONS', 'WEBSITES_PORT_CHOICES'] + return Response({ + name: getattr(settings, name, None) for name in names + }) + + +router.register(r'websites', WebsiteViewSet) diff --git a/orchestra/apps/websites/backends/__init__.py b/orchestra/apps/websites/backends/__init__.py new file mode 100644 index 00000000..6e57f3a5 --- /dev/null +++ b/orchestra/apps/websites/backends/__init__.py @@ -0,0 +1,5 @@ +import pkgutil + + +for __, module_name, __ in pkgutil.walk_packages(__path__): + exec('from . import %s' % module_name) diff --git a/orchestra/apps/websites/backends/apache.py b/orchestra/apps/websites/backends/apache.py new file mode 100644 index 00000000..6fcea8e5 --- /dev/null +++ b/orchestra/apps/websites/backends/apache.py @@ -0,0 +1,175 @@ +import os + +from django.template import Template, Context +from django.utils.translation import ugettext_lazy as _ + +from orchestra.apps.orchestration import ServiceBackend + +from .. import settings + + +class Apache2Backend(ServiceBackend): + model = 'websites.Website' + related_models = (('websites.Content', 'website'),) + verbose_name = _("Apache 2") + + def save(self, site): + context = self.get_context(site) + extra_conf = self.get_content_directives(site) + if site.protocol is 'https': + extra_conf += self.get_ssl(site) + extra_conf += self.get_security(site) + context['extra_conf'] = extra_conf + + apache_conf = Template( + "# {{ banner }}\n" + "\n" + " ServerName {{ site.domains.all|first }}\n" + "{% if site.domains.all|slice:\"1:\" %}" + " ServerAlias {{ site.domains.all|slice:\"1:\"|join:' ' }}\n" + "{% endif %}" + " CustomLog {{ logs }} common\n" + " SuexecUserGroup {{ user }} {{ group }}\n" + "{% for line in extra_conf.splitlines %}" + " {{ line | safe }}\n" + "{% endfor %}" + "\n" + ) + apache_conf = apache_conf.render(Context(context)) + apache_conf += self.get_protections(site) + context['apache_conf'] = apache_conf + + self.append( + "{ echo -e '%(apache_conf)s' | diff -N -I'^\s*#' %(sites_available)s - ; } ||" + " { echo -e '%(apache_conf)s' > %(sites_available)s; UPDATED=1; }" % context + ) + self.enable_or_disable(site) + + def delete(self, site): + context = self.get_context(site) + self.append("a2dissite %(site_unique_name)s.conf && UPDATED=1" % context) + self.append("rm -fr %(sites_available)s" % context) + + def commit(self): + """ reload Apache2 if necessary """ + self.append('[[ $UPDATED == 1 ]] && service apache2 reload') + + def get_content_directives(self, site): + directives = '' + for content in site.content_set.all().order_by('-path'): + method, args = content.webapp.get_method() + method = getattr(self, 'get_%s_directives' % method) + directives += method(content, *args) + return directives + + def get_alias_directives(self, content, *args): + context = self.get_content_context(content) + context['path'] = args[0] % context if args else content.webapp.get_path() + return "Alias %(location)s %(path)s\n" % context + + def get_fpm_directives(self, content, *args): + context = self.get_content_context(content) + context['fcgi_path'] = args[0] % context + directive = "ProxyPassMatch ^%(location)s(.*\.php(/.*)?)$ %(fcgi_path)s$1\n" + return directive % context + + def get_fcgid_directives(self, content, fcgid_path): + context = self.get_content_context(content) + context['fcgid_path'] = fcgid_path % context + fcgid = self.get_alias_directives(content) + fcgid += ( + "ProxyPass %(location)s !\n" + "\n" + " Options +ExecCGI\n" + " AddHandler fcgid-script .php\n" + " FcgidWrapper %(fcgid_path)s\n" + ) % context + for option in content.webapp.options.filter(name__startswith='Fcgid'): + fcgid += " %s %s\n" % (option.name, option.value) + fcgid += "\n" + return fcgid + + def get_ssl(self, site): + cert = settings.WEBSITES_DEFAULT_HTTPS_CERT + custom_cert = site.options.filter(name='ssl') + if custom_cert: + cert = tuple(custom_cert[0].value.split()) + directives = ( + "SSLEngine on\n" + "SSLCertificateFile %s\n" + "SSLCertificateKeyFile %s\n" + ) % cert + return directives + + def get_security(self, site): + directives = '' + for rules in site.options.filter(name='sec_rule_remove'): + for rule in rules.split(): + directives += "SecRuleRemoveById %d" % rule + + for modsecurity in site.options.filter(name='sec_rule_off'): + directives += ( + "\n" + " SecRuleEngine Off\n" + "\n" % modsecurity.value + ) + return directives + + def get_protections(self, site): + protections = "" + __, regex = settings.WEBSITES_OPTIONS['directory_protection'] + for protection in site.options.filter(name='directory_protection'): + path, name, passwd = re.match(regex, protection.value).groups() + path = os.path.join(context['root'], path) + passwd = os.path.join(self.USER_HOME % context, passwd) + protections += ("\n" + "\n" + " AllowOverride All\n" +# " AuthPAM_Enabled off\n" + " AuthType Basic\n" + " AuthName %s\n" + " AuthUserFile %s\n" + " \n" + " require valid-user\n" + " \n" + "\n" % (path, name, passwd) + ) + return protections + + def enable_or_disable(self, site): + context = self.get_context(site) + self.append("ls -l %(sites_enabled)s; DISABLED=$?" % context) + if site.is_active: + self.append("if [[ $DISABLED ]]; then a2ensite %(site_unique_name)s.conf;\n" + "else UPDATED=0; fi" % context) + else: + self.append("if [[ ! $DISABLED ]]; then a2dissite %(site_unique_name)s.conf;\n" + "else UPDATED=0; fi" % context) + + def get_context(self, site): + base_apache_conf = settings.WEBSITES_BASE_APACHE_CONF + sites_available = os.path.join(base_apache_conf, 'sites-available') + sites_enabled = os.path.join(base_apache_conf, 'sites-enabled') + context = { + 'site': site, + 'site_name': site.name, + 'site_unique_name': site.unique_name, + 'user': site.account.user.username, + 'group': site.account.user.username, + 'sites_enabled': sites_enabled, + 'sites_available': "%s.conf" % os.path.join(sites_available, site.unique_name), + 'logs': os.path.join(settings.WEBSITES_BASE_APACHE_LOGS, site.unique_name), + 'banner': self.get_banner(), + } + return context + + def get_content_context(self, content): + context = self.get_context(content.website) + context.update({ + 'type': content.webapp.type, + 'location': content.path, + 'app_name': content.webapp.name, + 'app_path': content.webapp.get_path(), + 'fpm_port': content.webapp.get_fpm_port(), + }) + return context diff --git a/orchestra/apps/websites/backends/webalizer.py b/orchestra/apps/websites/backends/webalizer.py new file mode 100644 index 00000000..3b95f531 --- /dev/null +++ b/orchestra/apps/websites/backends/webalizer.py @@ -0,0 +1,84 @@ +import os +from functools import partial + +from django.utils.translation import ugettext_lazy as _ + +from orchestra.apps.orchestration import ServiceBackend + +from .. import settings + + +class WebalizerBackend(ServiceBackend): + verbose_name = _("Webalizer") + model = 'websites.Content' + + def save(self, content): + context = self.get_context(content) + self.append("mkdir -p %(webalizer_path)s" % context) + self.append("[[ ! -e %(webalizer_path)s/index.html ]] && " + "echo 'Webstats are coming soon' > %(webalizer_path)s/index.html" % context) + self.append("echo '%(webalizer_conf)s' > %(webalizer_conf_path)s" % context) + self.append("chown %(user)s.www-data %(webalizer_path)s" % context) + + def delete(self, content): + context = self.get_context(content) + self.append("rm -fr %(webalizer_path)s" % context) + self.append("rm %(webalizer_conf_path)s" % context) + + def get_context(self, content): + conf_file = "%s.conf" % content.website.name + context = { + 'site_logs': os.path.join(settings.WEBSITES_BASE_APACHE_LOGS, content.website.unique_name), + 'site_name': content.website.name, + 'webalizer_path': os.path.join(content.webapp.get_path(), content.website.name), + 'webalizer_conf_path': os.path.join(settings.WEBSITES_WEBALIZER_PATH, conf_file), + 'user': content.webapp.account.user, + 'banner': self.get_banner(), + } + context['webalizer_conf'] = ( + "# %(banner)s\n" + "LogFile %(site_logs)s\n" + "LogType clf\n" + "OutputDir %(webalizer_path)s\n" + "HistoryName webalizer.hist\n" + "Incremental yes\n" + "IncrementalName webalizer.current\n" + "ReportTitle Stats of\n" + "HostName %(site_name)s\n" + "\n" + "PageType htm*\n" + "PageType php*\n" + "PageType shtml\n" + "PageType cgi\n" + "PageType pl\n" + "\n" + "DNSCache /var/lib/dns_cache.db\n" + "DNSChildren 15\n" + "\n" + "HideURL *.gif\n" + "HideURL *.GIF\n" + "HideURL *.jpg\n" + "HideURL *.JPG\n" + "HideURL *.png\n" + "HideURL *.PNG\n" + "HideURL *.ra\n" + "\n" + "IncludeURL *\n" + "\n" + "SearchEngine yahoo.com p=\n" + "SearchEngine altavista.com q=\n" + "SearchEngine google.com q=\n" + "SearchEngine eureka.com q=\n" + "SearchEngine lycos.com query=\n" + "SearchEngine hotbot.com MT=\n" + "SearchEngine msn.com MT=\n" + "SearchEngine infoseek.com qt=\n" + "SearchEngine webcrawler searchText=\n" + "SearchEngine excite search=\n" + "SearchEngine netscape.com search=\n" + "SearchEngine mamma.com query=\n" + "SearchEngine alltheweb.com query=\n" + "\n" + "DumpSites yes\n" + ) % context + return context diff --git a/orchestra/apps/websites/models.py b/orchestra/apps/websites/models.py new file mode 100644 index 00000000..19ecdf2d --- /dev/null +++ b/orchestra/apps/websites/models.py @@ -0,0 +1,92 @@ +import re + +from django.core.exceptions import ValidationError +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from orchestra.core import validators, services +from orchestra.utils.functional import cached + +from . import settings + + +def settings_to_choices(choices): + return sorted( + [ (name, opt[0]) for name,opt in choices.iteritems() ], + key=lambda e: e[1] + ) + + +class Website(models.Model): + name = models.CharField(_("name"), max_length=128, unique=True, + validators=[validators.validate_name]) + account = models.ForeignKey('accounts.Account', verbose_name=_("Account"), + related_name='websites') + port = models.PositiveIntegerField(_("port"), + choices=settings.WEBSITES_PORT_CHOICES, + default=settings.WEBSITES_DEFAULT_PORT) + domains = models.ManyToManyField(settings.WEBSITES_DOMAIN_MODEL, + related_name='websites', verbose_name=_("domains")) + contents = models.ManyToManyField('webapps.WebApp', through='websites.Content') + is_active = models.BooleanField(_("is active"), default=True) + + def __unicode__(self): + return self.name + + @property + def unique_name(self): + return "%s-%s" % (self.account, self.name) + + @cached + def get_options(self): + return { opt.name: opt.value for opt in self.options.all() } + + @property + def protocol(self): + if self.port == 80: + return 'http' + if self.port == 443: + return 'https' + raise TypeError('No protocol for port "%s"' % self.port) + + +class WebsiteOption(models.Model): + website = models.ForeignKey(Website, verbose_name=_("web site"), + related_name='options') + name = models.CharField(_("name"), max_length=128, + choices=settings_to_choices(settings.WEBSITES_OPTIONS)) + value = models.CharField(_("value"), max_length=256) + + class Meta: + unique_together = ('website', 'name') + verbose_name = _("option") + verbose_name_plural = _("options") + + def __unicode__(self): + return self.name + + def clean(self): + """ validates name and value according to WEBSITES_WEBSITEOPTIONS """ + __, regex = settings.WEBSITES_OPTIONS[self.name] + if not re.match(regex, self.value): + msg = _("'%s' does not match %s") + raise ValidationError(msg % (self.value, regex)) + + +class Content(models.Model): + webapp = models.ForeignKey('webapps.WebApp', verbose_name=_("web application")) + website = models.ForeignKey('websites.Website', verbose_name=_("web site")) + path = models.CharField(_("path"), max_length=256, blank=True) + + class Meta: + unique_together = ('website', 'path') + + def clean(self): + if not self.path.startswith('/'): + self.path = '/' + self.path + + def __unicode__(self): + return self.website.name + self.path + + +services.register(Website) diff --git a/orchestra/apps/websites/serializers.py b/orchestra/apps/websites/serializers.py new file mode 100644 index 00000000..58f8c4b6 --- /dev/null +++ b/orchestra/apps/websites/serializers.py @@ -0,0 +1,22 @@ +from rest_framework import serializers + +from orchestra.api.fields import OptionField +from orchestra.apps.accounts.serializers import AccountSerializerMixin + +from .models import Website, Content + + +class ContentSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = Content + fields = ('webapp', 'path') + + +class WebsiteSerializer(AccountSerializerMixin, serializers.HyperlinkedModelSerializer): + contents = ContentSerializer(required=False, many=True, allow_add_remove=True, + source='content_set') + options = OptionField(required=False) + + class Meta: + model = Website + fields = ('url', 'name', 'port', 'domains', 'is_active', 'contents', 'options') diff --git a/orchestra/apps/websites/settings.py b/orchestra/apps/websites/settings.py new file mode 100644 index 00000000..f5f33c14 --- /dev/null +++ b/orchestra/apps/websites/settings.py @@ -0,0 +1,50 @@ +from django.conf import settings +from django.utils.translation import ugettext_lazy as _ + + +WEBSITES_PORT_CHOICES = getattr(settings, 'WEBSITES_PORT_CHOICES', ( + (80, 'HTTP'), + (443, 'HTTPS'), +)) + + +WEBSITES_DEFAULT_PORT = getattr(settings, 'WEBSITES_DEFAULT_PORT', 80) + + +WEBSITES_DOMAIN_MODEL = getattr(settings, 'WEBSITES_DOMAIN_MODEL', 'domains.Domain') + + +WEBSITES_OPTIONS = getattr(settings, 'WEBSITES_OPTIONS', { + # { name: ( verbose_name, validation_regex ) } + 'directory_protection': ( + _("HTTPD - Directory protection"), + r'^([\w/_]+)\s+(\".*\")\s+([\w/_\.]+)$' + ), + 'redirection': ( + _("HTTPD - Redirection"), + r'^.*\s+.*$' + ), + 'ssl': ( + _("HTTPD - SSL"), + r'^.*\s+.*$' + ), + 'sec_rule_remove': ( + _("HTTPD - SecRuleRemoveById"), + r'^[0-9,\s]+$' + ), + 'sec_rule_off': ( + _("HTTPD - Disable Modsecurity"), + r'^[\w/_]+$' + ), +}) + + +WEBSITES_BASE_APACHE_CONF = getattr(settings, 'WEBSITES_BASE_APACHE_CONF', + '/etc/apache2/') + +WEBSITES_WEBALIZER_PATH = getattr(settings, 'WEBSITES_WEBALIZER_PATH', + '/home/httpd/webalizer/') + + +WEBSITES_BASE_APACHE_LOGS = getattr(settings, 'WEBSITES_BASE_APACHE_LOGS', + '/var/log/apache2/virtual/') diff --git a/orchestra/bin/celerybeat b/orchestra/bin/celerybeat new file mode 100755 index 00000000..e43c300b --- /dev/null +++ b/orchestra/bin/celerybeat @@ -0,0 +1,212 @@ +#!/bin/bash +# ========================================================= +# celerybeat - Starts the Celery periodic task scheduler. +# ========================================================= +# +# :Usage: /etc/init.d/celerybeat {start|stop|force-reload|restart|try-restart|status} +# :Configuration file: /etc/default/celerybeat or /etc/default/celeryd +# +# See http://docs.celeryproject.org/en/latest/tutorials/daemonizing.html#generic-init-scripts + +### BEGIN INIT INFO +# Provides: celerybeat +# Required-Start: $network $local_fs $remote_fs postgresql celeryd +# Required-Stop: $network $local_fs $remote_fs postgresql celeryd +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: celery periodic task scheduler +### END INIT INFO + +# Cannot use set -e/bash -e since the kill -0 command will abort +# abnormally in the absence of a valid process ID. +#set -e + +DEFAULT_PID_FILE="/var/run/celery/beat.pid" +DEFAULT_LOG_FILE="/var/log/celery/beat.log" +DEFAULT_LOG_LEVEL="INFO" +DEFAULT_CELERYBEAT="celerybeat" + +# /etc/init.d/celerybeat: start and stop the celery periodic task scheduler daemon. + +if test -f /etc/default/celeryd; then + . /etc/default/celeryd +fi + +if test -f /etc/default/celerybeat; then + . /etc/default/celerybeat +fi + +CELERYBEAT=${CELERYBEAT:-$DEFAULT_CELERYBEAT} +CELERYBEAT_LOG_LEVEL=${CELERYBEAT_LOG_LEVEL:-${CELERYBEAT_LOGLEVEL:-$DEFAULT_LOG_LEVEL}} + +# Set CELERY_CREATE_DIRS to always create log/pid dirs. +CELERY_CREATE_DIRS=${CELERY_CREATE_DIRS:-0} +CELERY_CREATE_RUNDIR=$CELERY_CREATE_DIRS +CELERY_CREATE_LOGDIR=$CELERY_CREATE_DIRS +if [ -z "$CELERYBEAT_PID_FILE" ]; then + CELERYBEAT_PID_FILE="$DEFAULT_PID_FILE" + CELERY_CREATE_RUNDIR=1 +fi +if [ -z "$CELERYBEAT_LOG_FILE" ]; then + CELERYBEAT_LOG_FILE="$DEFAULT_LOG_FILE" + CELERY_CREATE_LOGDIR=1 +fi + +export CELERY_LOADER + +CELERYBEAT_OPTS="$CELERYBEAT_OPTS -f $CELERYBEAT_LOG_FILE -l $CELERYBEAT_LOG_LEVEL" + +if [ -n "$2" ]; then + CELERYBEAT_OPTS="$CELERYBEAT_OPTS $2" +fi + +CELERYBEAT_LOG_DIR=`dirname $CELERYBEAT_LOG_FILE` +CELERYBEAT_PID_DIR=`dirname $CELERYBEAT_PID_FILE` + +# Extra start-stop-daemon options, like user/group. +if [ -n "$CELERYBEAT_USER" ]; then + DAEMON_OPTS="$DAEMON_OPTS --uid $CELERYBEAT_USER" +fi +if [ -n "$CELERYBEAT_GROUP" ]; then + DAEMON_OPTS="$DAEMON_OPTS --gid $CELERYBEAT_GROUP" +fi + +CELERYBEAT_CHDIR=${CELERYBEAT_CHDIR:-$CELERYD_CHDIR} +if [ -n "$CELERYBEAT_CHDIR" ]; then + DAEMON_OPTS="$DAEMON_OPTS --workdir $CELERYBEAT_CHDIR" +fi + + +export PATH="${PATH:+$PATH:}/usr/sbin:/sbin" + +check_dev_null() { + if [ ! -c /dev/null ]; then + echo "/dev/null is not a character device!" + exit 75 # EX_TEMPFAIL + fi +} + +maybe_die() { + if [ $? -ne 0 ]; then + echo "Exiting: $*" + exit 77 # EX_NOPERM + fi +} + +create_default_dir() { + if [ ! -d "$1" ]; then + echo "- Creating default directory: '$1'" + mkdir -p "$1" + maybe_die "Couldn't create directory $1" + echo "- Changing permissions of '$1' to 02755" + chmod 02755 "$1" + maybe_die "Couldn't change permissions for $1" + if [ -n "$CELERYBEAT_USER" ]; then + echo "- Changing owner of '$1' to '$CELERYBEAT_USER'" + chown "$CELERYBEAT_USER" "$1" + maybe_die "Couldn't change owner of $1" + fi + if [ -n "$CELERYBEAT_GROUP" ]; then + echo "- Changing group of '$1' to '$CELERYBEAT_GROUP'" + chgrp "$CELERYBEAT_GROUP" "$1" + maybe_die "Couldn't change group of $1" + fi + fi +} + +check_paths() { + if [ $CELERY_CREATE_LOGDIR -eq 1 ]; then + create_default_dir "$CELERYBEAT_LOG_DIR" + fi + if [ $CELERY_CREATE_RUNDIR -eq 1 ]; then + create_default_dir "$CELERYBEAT_PID_DIR" + fi +} + + +create_paths () { + create_default_dir "$CELERYBEAT_LOG_DIR" + create_default_dir "$CELERYBEAT_PID_DIR" +} + + +wait_pid () { + pid=$1 + forever=1 + i=0 + while [ $forever -gt 0 ]; do + kill -0 $pid 1>/dev/null 2>&1 + if [ $? -eq 1 ]; then + echo "OK" + forever=0 + else + kill -TERM "$pid" + i=$((i + 1)) + if [ $i -gt 60 ]; then + echo "ERROR" + echo "Timed out while stopping (30s)" + forever=0 + else + sleep 0.5 + fi + fi + done +} + + +stop_beat () { + echo -n "Stopping celerybeat... " + if [ -f "$CELERYBEAT_PID_FILE" ]; then + wait_pid $(cat "$CELERYBEAT_PID_FILE") + else + echo "NOT RUNNING" + fi +} + +start_beat () { + echo "Starting celerybeat..." + if [ -n "$VIRTUALENV" ]; then + source $VIRTUALENV/bin/activate + fi + $CELERYBEAT $CELERYBEAT_OPTS $DAEMON_OPTS --detach \ + --pidfile="$CELERYBEAT_PID_FILE" +} + + + +case "$1" in + start) + check_dev_null + check_paths + start_beat + ;; + stop) + check_paths + stop_beat + ;; + reload|force-reload) + echo "Use start+stop" + ;; + restart) + echo "Restarting celery periodic task scheduler" + check_paths + stop_beat + check_dev_null + start_beat + ;; + create-paths) + check_dev_null + create_paths + ;; + check-paths) + check_dev_null + check_paths + ;; + *) + echo "Usage: /etc/init.d/celerybeat {start|stop|restart|create-paths}" + exit 64 # EX_USAGE + ;; +esac + +exit 0 + diff --git a/orchestra/bin/celeryd b/orchestra/bin/celeryd new file mode 100755 index 00000000..0d34dce6 --- /dev/null +++ b/orchestra/bin/celeryd @@ -0,0 +1,234 @@ +#!/bin/sh -e +# ============================================ +# celeryd - Starts the Celery worker daemon. +# ============================================ +# +# :Usage: /etc/init.d/celeryd {start|stop|force-reload|restart|try-restart|status} +# :Configuration file: /etc/default/celeryd +# +# See http://docs.celeryproject.org/en/latest/tutorials/daemonizing.html#generic-init-scripts + + +### BEGIN INIT INFO +# Provides: celeryd +# Required-Start: $network $local_fs $remote_fs postgresql celeryev rabbitmq-server +# Required-Stop: $network $local_fs $remote_fs postgresql celeryev rabbitmq-server +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: celery task worker daemon +### END INIT INFO + +# some commands work asyncronously, so we'll wait this many seconds +SLEEP_SECONDS=5 + +DEFAULT_PID_FILE="/var/run/celery/%n.pid" +DEFAULT_LOG_FILE="/var/log/celery/%n.log" +DEFAULT_LOG_LEVEL="INFO" +DEFAULT_NODES="celery" +DEFAULT_CELERYD="-m celery.bin.celeryd_detach" + +CELERY_DEFAULTS=${CELERY_DEFAULTS:-"/etc/default/celeryd"} + +test -f "$CELERY_DEFAULTS" && . "$CELERY_DEFAULTS" + +# Set CELERY_CREATE_DIRS to always create log/pid dirs. +CELERY_CREATE_DIRS=${CELERY_CREATE_DIRS:-0} +CELERY_CREATE_RUNDIR=$CELERY_CREATE_DIRS +CELERY_CREATE_LOGDIR=$CELERY_CREATE_DIRS +if [ -z "$CELERYD_PID_FILE" ]; then + CELERYD_PID_FILE="$DEFAULT_PID_FILE" + CELERY_CREATE_RUNDIR=1 +fi +if [ -z "$CELERYD_LOG_FILE" ]; then + CELERYD_LOG_FILE="$DEFAULT_LOG_FILE" + CELERY_CREATE_LOGDIR=1 +fi + +CELERYD_LOG_LEVEL=${CELERYD_LOG_LEVEL:-${CELERYD_LOGLEVEL:-$DEFAULT_LOG_LEVEL}} +CELERYD_MULTI=${CELERYD_MULTI:-"celeryd-multi"} +CELERYD=${CELERYD:-$DEFAULT_CELERYD} +CELERYD_NODES=${CELERYD_NODES:-$DEFAULT_NODES} + +export CELERY_LOADER + +if [ -n "$2" ]; then + CELERYD_OPTS="$CELERYD_OPTS $2" +fi + +CELERYD_LOG_DIR=`dirname $CELERYD_LOG_FILE` +CELERYD_PID_DIR=`dirname $CELERYD_PID_FILE` + +# Extra start-stop-daemon options, like user/group. +if [ -n "$CELERYD_USER" ]; then + DAEMON_OPTS="$DAEMON_OPTS --uid=$CELERYD_USER" +fi +if [ -n "$CELERYD_GROUP" ]; then + DAEMON_OPTS="$DAEMON_OPTS --gid=$CELERYD_GROUP" +fi + +if [ -n "$CELERYD_CHDIR" ]; then + DAEMON_OPTS="$DAEMON_OPTS --workdir=$CELERYD_CHDIR" +fi + + +check_dev_null() { + if [ ! -c /dev/null ]; then + echo "/dev/null is not a character device!" + exit 75 # EX_TEMPFAIL + fi +} + + +maybe_die() { + if [ $? -ne 0 ]; then + echo "Exiting: $* (errno $?)" + exit 77 # EX_NOPERM + fi +} + +create_default_dir() { + if [ ! -d "$1" ]; then + echo "- Creating default directory: '$1'" + mkdir -p "$1" + maybe_die "Couldn't create directory $1" + echo "- Changing permissions of '$1' to 02755" + chmod 02755 "$1" + maybe_die "Couldn't change permissions for $1" + if [ -n "$CELERYD_USER" ]; then + echo "- Changing owner of '$1' to '$CELERYD_USER'" + chown "$CELERYD_USER" "$1" + maybe_die "Couldn't change owner of $1" + fi + if [ -n "$CELERYD_GROUP" ]; then + echo "- Changing group of '$1' to '$CELERYD_GROUP'" + chgrp "$CELERYD_GROUP" "$1" + maybe_die "Couldn't change group of $1" + fi + fi +} + + +check_paths() { + if [ $CELERY_CREATE_LOGDIR -eq 1 ]; then + create_default_dir "$CELERYD_LOG_DIR" + fi + if [ $CELERY_CREATE_RUNDIR -eq 1 ]; then + create_default_dir "$CELERYD_PID_DIR" + fi +} + +create_paths() { + create_default_dir "$CELERYD_LOG_DIR" + create_default_dir "$CELERYD_PID_DIR" +} + +export PATH="${PATH:+$PATH:}/usr/sbin:/sbin" + + +_get_pid_files() { + [ ! -d "$CELERYD_PID_DIR" ] && return + echo `ls -1 "$CELERYD_PID_DIR"/*.pid 2> /dev/null` +} + +stop_workers () { + $CELERYD_MULTI stopwait $CELERYD_NODES --pidfile="$CELERYD_PID_FILE" + sleep $SLEEP_SECONDS +} + + +start_workers () { + $CELERYD_MULTI start $CELERYD_NODES $DAEMON_OPTS \ + --pidfile="$CELERYD_PID_FILE" \ + --logfile="$CELERYD_LOG_FILE" \ + --loglevel="$CELERYD_LOG_LEVEL" \ + --cmd="$CELERYD" \ + $CELERYD_OPTS + sleep $SLEEP_SECONDS +} + + +restart_workers () { + $CELERYD_MULTI restart $CELERYD_NODES $DAEMON_OPTS \ + --pidfile="$CELERYD_PID_FILE" \ + --logfile="$CELERYD_LOG_FILE" \ + --loglevel="$CELERYD_LOG_LEVEL" \ + --cmd="$CELERYD" \ + $CELERYD_OPTS + sleep $SLEEP_SECONDS +} + +check_status () { + local pid_files= + pid_files=`_get_pid_files` + [ -z "$pid_files" ] && echo "celeryd not running (no pidfile)" && exit 1 + + local one_failed= + for pid_file in $pid_files; do + local node=`basename "$pid_file" .pid` + local pid=`cat "$pid_file"` + local cleaned_pid=`echo "$pid" | sed -e 's/[^0-9]//g'` + if [ -z "$pid" ] || [ "$cleaned_pid" != "$pid" ]; then + echo "bad pid file ($pid_file)" + else + local failed= + kill -0 $pid 2> /dev/null || failed=true + if [ "$failed" ]; then + echo "celeryd (node $node) (pid $pid) is stopped, but pid file exists!" + one_failed=true + else + echo "celeryd (node $node) (pid $pid) is running..." + fi + fi + done + + [ "$one_failed" ] && exit 1 || exit 0 +} + + +case "$1" in + start) + check_dev_null + check_paths + start_workers + ;; + + stop) + check_dev_null + check_paths + stop_workers + ;; + + reload|force-reload) + echo "Use restart" + ;; + + status) + check_status + ;; + + restart) + check_dev_null + check_paths + restart_workers + ;; + try-restart) + check_dev_null + check_paths + restart_workers + ;; + create-paths) + check_dev_null + create_paths + ;; + check-paths) + check_dev_null + check_paths + ;; + *) + echo "Usage: /etc/init.d/celeryd {start|stop|restart|kill|create-paths}" + exit 64 # EX_USAGE + ;; +esac + +exit 0 + diff --git a/orchestra/bin/celeryevcam b/orchestra/bin/celeryevcam new file mode 100755 index 00000000..623e1adb --- /dev/null +++ b/orchestra/bin/celeryevcam @@ -0,0 +1,226 @@ +#!/bin/bash +# ============================================ +# celeryd - Starts the Celery worker daemon. +# ============================================ +# +# :Usage: /etc/init.d/celeryd {start|stop|force-reload|restart|try-restart|status} +# +# :Configuration file: /etc/default/celeryev | /etc/default/celeryd +# +# To configure celeryd you probably need to tell it where to chdir. +# +# EXAMPLE CONFIGURATION +# ===================== +# +# this is an example configuration for a Python project: +# +# /etc/default/celeryd: +# +# # Where to chdir at start. +# CELERYD_CHDIR="/opt/Myproject/" +# +# # Extra arguments to celeryev +# CELERYEV_OPTS="-x" +# +# # Name of the celery config module.# +# CELERY_CONFIG_MODULE="celeryconfig" +# +# # Camera class to use (required) +# CELERYEV_CAM = "myapp.Camera" +# +# EXAMPLE DJANGO CONFIGURATION +# ============================ +# +# # Where the Django project is. +# CELERYD_CHDIR="/opt/Project/" +# +# # Name of the projects settings module. +# export DJANGO_SETTINGS_MODULE="MyProject.settings" +# +# # Path to celeryd +# CELERYEV="/opt/Project/manage.py" +# +# # Extra arguments to manage.py +# CELERYEV_OPTS="celeryev" +# +# # Camera class to use (required) +# CELERYEV_CAM="djcelery.snapshot.Camera" +# +# AVAILABLE OPTIONS +# ================= +# +# * CELERYEV_OPTS +# Additional arguments to celeryd, see `celeryd --help` for a list. +# +# * CELERYD_CHDIR +# Path to chdir at start. Default is to stay in the current directory. +# +# * CELERYEV_PID_FILE +# Full path to the pidfile. Default is /var/run/celeryd.pid. +# +# * CELERYEV_LOG_FILE +# Full path to the celeryd logfile. Default is /var/log/celeryd.log +# +# * CELERYEV_LOG_LEVEL +# Log level to use for celeryd. Default is INFO. +# +# * CELERYEV +# Path to the celeryev program. Default is `celeryev`. +# You can point this to an virtualenv, or even use manage.py for django. +# +# * CELERYEV_USER +# User to run celeryev as. Default is current user. +# +# * CELERYEV_GROUP +# Group to run celeryev as. Default is current user. +# +# * VIRTUALENV +# Full path to the virtualenv environment to activate. Default is none. + +### BEGIN INIT INFO +# Provides: celeryev +# Required-Start: $network $local_fs $remote_fs postgresql rabbitmq-server +# Required-Stop: $network $local_fs $remote_fs postgresql rabbitmq-server +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: celery event snapshots +### END INIT INFO + +# Cannot use set -e/bash -e since the kill -0 command will abort +# abnormally in the absence of a valid process ID. +#set -e + +DEFAULT_PID_FILE="/var/run/celeryev.pid" +DEFAULT_LOG_FILE="/var/log/celeryev.log" +DEFAULT_LOG_LEVEL="INFO" +DEFAULT_CELERYEV="/usr/bin/celeryev" + +if test -f /etc/default/celeryd; then + . /etc/default/celeryd +fi + +if test -f /etc/default/celeryev; then + . /etc/default/celeryev +fi + +CELERYEV=${CELERYEV:-$DEFAULT_CELERYEV} +CELERYEV_PID_FILE=${CELERYEV_PID_FILE:-${CELERYEV_PIDFILE:-$DEFAULT_PID_FILE}} +CELERYEV_LOG_FILE=${CELERYEV_LOG_FILE:-${CELERYEV_LOGFILE:-$DEFAULT_LOG_FILE}} +CELERYEV_LOG_LEVEL=${CELERYEV_LOG_LEVEL:-${CELERYEV_LOG_LEVEL:-$DEFAULT_LOG_LEVEL}} + +export CELERY_LOADER + +if [ -z "$CELERYEV_CAM" ]; then + echo "Missing CELERYEV_CAM variable" 1>&2 + exit +fi + +CELERYEV_OPTS="$CELERYEV_OPTS -f $CELERYEV_LOG_FILE -l $CELERYEV_LOG_LEVEL -c $CELERYEV_CAM" + +if [ -n "$2" ]; then + CELERYEV_OPTS="$CELERYEV_OPTS $2" +fi + +CELERYEV_LOG_DIR=`dirname $CELERYEV_LOG_FILE` +CELERYEV_PID_DIR=`dirname $CELERYEV_PID_FILE` +if [ ! -d "$CELERYEV_LOG_DIR" ]; then + mkdir -p $CELERYEV_LOG_DIR +fi +if [ ! -d "$CELERYEV_PID_DIR" ]; then + mkdir -p $CELERYEV_PID_DIR +fi + +# Extra start-stop-daemon options, like user/group. +if [ -n "$CELERYEV_USER" ]; then + DAEMON_OPTS="$DAEMON_OPTS --uid $CELERYEV_USER" + chown "$CELERYEV_USER" $CELERYEV_LOG_DIR $CELERYEV_PID_DIR +fi +if [ -n "$CELERYEV_GROUP" ]; then + DAEMON_OPTS="$DAEMON_OPTS --gid $CELERYEV_GROUP" + chgrp "$CELERYEV_GROUP" $CELERYEV_LOG_DIR $CELERYEV_PID_DIR +fi + +CELERYEV_CHDIR=${CELERYEV_CHDIR:-$CELERYD_CHDIR} +if [ -n "$CELERYEV_CHDIR" ]; then + DAEMON_OPTS="$DAEMON_OPTS --workdir $CELERYEV_CHDIR" +fi + + +export PATH="${PATH:+$PATH:}/usr/sbin:/sbin" + +check_dev_null() { + if [ ! -c /dev/null ]; then + echo "/dev/null is not a character device!" + exit 1 + fi +} + +wait_pid () { + pid=$1 + forever=1 + i=0 + while [ $forever -gt 0 ]; do + kill -0 $pid 1>/dev/null 2>&1 + if [ $? -eq 1 ]; then + echo "OK" + forever=0 + else + kill -TERM "$pid" + i=$((i + 1)) + if [ $i -gt 60 ]; then + echo "ERROR" + echo "Timed out while stopping (30s)" + forever=0 + else + sleep 0.5 + fi + fi + done +} + + +stop_evcam () { + echo -n "Stopping celeryev..." + if [ -f "$CELERYEV_PID_FILE" ]; then + wait_pid $(cat "$CELERYEV_PID_FILE") + else + echo "NOT RUNNING" + fi +} + +start_evcam () { + echo "Starting celeryev..." + if [ -n "$VIRTUALENV" ]; then + source $VIRTUALENV/bin/activate + fi + $CELERYEV $CELERYEV_OPTS $DAEMON_OPTS --detach \ + --pidfile="$CELERYEV_PID_FILE" +} + + + +case "$1" in + start) + check_dev_null + start_evcam + ;; + stop) + stop_evcam + ;; + reload|force-reload) + echo "Use start+stop" + ;; + restart) + echo "Restarting celery event snapshots" "celeryev" + stop_evcam + check_dev_null + start_evcam + ;; + + *) + echo "Usage: /etc/init.d/celeryev {start|stop|restart}" + exit 1 +esac + +exit 0 + diff --git a/orchestra/bin/django_bash_completion.sh b/orchestra/bin/django_bash_completion.sh new file mode 100755 index 00000000..8f852117 --- /dev/null +++ b/orchestra/bin/django_bash_completion.sh @@ -0,0 +1,72 @@ +# ######################################################################### +# This bash script adds tab-completion feature to django-admin.py and +# manage.py. +# +# Testing it out without installing +# ================================= +# +# To test out the completion without "installing" this, just run this file +# directly, like so: +# +# . ~/path/to/django_bash_completion +# +# Note: There's a dot ('.') at the beginning of that command. +# +# After you do that, tab completion will immediately be made available in your +# current Bash shell. But it won't be available next time you log in. +# +# Installing +# ========== +# +# To install this, point to this file from your .bash_profile, like so: +# +# . ~/path/to/django_bash_completion +# +# Do the same in your .bashrc if .bashrc doesn't invoke .bash_profile. +# +# Settings will take effect the next time you log in. +# +# Uninstalling +# ============ +# +# To uninstall, just remove the line from your .bash_profile and .bashrc. + +_django_completion() +{ + COMPREPLY=( $( COMP_WORDS="${COMP_WORDS[*]}" \ + COMP_CWORD=$COMP_CWORD \ + DJANGO_AUTO_COMPLETE=1 $1 ) ) +} +complete -F _django_completion -o default django-admin.py manage.py django-admin + +_python_django_completion() +{ + if [[ ${COMP_CWORD} -ge 2 ]]; then + PYTHON_EXE=${COMP_WORDS[0]##*/} + echo $PYTHON_EXE | egrep "python([2-9]\.[0-9])?" >/dev/null 2>&1 + if [[ $? == 0 ]]; then + PYTHON_SCRIPT=${COMP_WORDS[1]##*/} + echo $PYTHON_SCRIPT | egrep "manage\.py|django-admin(\.py)?" >/dev/null 2>&1 + if [[ $? == 0 ]]; then + COMPREPLY=( $( COMP_WORDS="${COMP_WORDS[*]:1}" \ + COMP_CWORD=$(( COMP_CWORD-1 )) \ + DJANGO_AUTO_COMPLETE=1 ${COMP_WORDS[*]} ) ) + fi + fi + fi +} + +# Support for multiple interpreters. +unset pythons +if command -v whereis &>/dev/null; then + python_interpreters=$(whereis python | cut -d " " -f 2-) + for python in $python_interpreters; do + pythons="${pythons} ${python##*/}" + done + pythons=$(echo $pythons | tr " " "\n" | sort -u | tr "\n" " ") +else + pythons=python +fi + +complete -F _python_django_completion -o default $pythons + diff --git a/orchestra/bin/orchestra-admin b/orchestra/bin/orchestra-admin new file mode 100755 index 00000000..7149f705 --- /dev/null +++ b/orchestra/bin/orchestra-admin @@ -0,0 +1,422 @@ +#!/bin/bash + +set -u + +bold=$(tput bold) +normal=$(tput sgr0) + + +function help () { + if [[ $# -gt 1 ]]; then + CMD="print_${2}_help" + $CMD + else + print_help + fi +} + + +function print_help () { + cat <<- EOF + + ${bold}NAME${normal} + ${bold}orchestra-admin${normal} - Orchetsra administration script + + ${bold}OPTIONS${normal} + ${bold}install_requirements${normal} + Installs Orchestra requirements using apt-get and pip + + ${bold}install_postfix${normal} + Installs postfix packages including dovecot, amavis, spamassassin and clamav + + ${bold}uninstall_postfix${normal} + Uninstall postfix packages including dovecot, amavis, spamassassin and clamav + + ${bold}install_certificate${normal} + Installs a valid all-purpose self signed certificate that is valid for the next ten years + + ${bold}uninstall_certificate${normal} + Uninstall certificate + + ${bold}startproject${normal} + Creates a new Django-orchestra instance + + ${bold}help${normal} + Displays this help text or related help page as argument + for example: + ${bold}orchestra-admin help startproject${normal} + + EOF +} +# in + + +show () { + echo " ${bold}\$ ${@}${normal}" +} +export -f show + + +run () { + show "${@}" + "${@}" +} +export -f run + + +check_root () { + [ $(whoami) != 'root' ] && { echo -e "\nErr. This should be run as root\n" >&2; exit 1; } +} +export -f check_root + + +get_orchestra_dir () { + if ! $(echo "import orchestra"|python 2> /dev/null); then + echo -e "\nErr. orchestra not installed.\n" >&2 + exit 1 + fi + PATH=$(echo "import orchestra, os; print os.path.dirname(os.path.realpath(orchestra.__file__))" | python) + echo $PATH +} +export -f get_orchestra_dir + + +function print_install_requirements_help () { + cat <<- EOF + + ${bold}NAME${normal} + ${bold}orchetsra-admin install_requirements${normal} - Installs all Orchestra requirements using apt-get and pip + + ${bold}OPTIONS${normal} + ${bold}-t, --testing${normal} + Install Orchestra normal requirements plus those needed for running functional tests + + ${bold}-h, --help${normal} + Displays this help text + + EOF +} + + +function install_requirements () { + opts=$(getopt -o h,t -l help,testing -- "$@") || exit 1 + set -- $opts + testing=false + + while [ $# -gt 0 ]; do + case $1 in + -h|--help) print_deploy_help; exit 0 ;; + -t|--testing) testing=true; shift ;; + (--) shift; break;; + (-*) echo "$0: Err. - unrecognized option $1" 1>&2; exit 1;; + (*) break;; + esac + shift + done + unset OPTIND + unset opt + + check_root + ORCHESTRA_PATH=$(get_orchestra_dir) + + APT="python-pip \ + python-psycopg2 \ + postgresql \ + rabbitmq-server \ + python-dev \ + bind9utils \ + python-cracklib" + + PIP="django==1.6.1 \ + django-celery-email==1.0.3 \ + django-fluent-dashboard==0.3.5 \ + https://bitbucket.org/izi/django-admin-tools/get/a0abfffd76a0.zip \ + South==0.8.1 \ + IPy==0.81 \ + django-extensions==1.1.1 \ + django-transaction-signals==1.0.0 \ + django-celery==3.1.1 \ + celery==3.1.7 \ + kombu==3.0.8 \ + Markdown==2.4 \ + django-debug-toolbar==1.0.1 \ + djangorestframework==2.3.12 \ + paramiko==1.12.1 \ + Pygments==1.6 \ + django-filter==0.7 \ + passlib==1.6.2" + + if $testing; then + APT="${APT} \ + iceweasel \ + xvfb" + PIP="${PIP} \ + selenium \ + xvfbwrapper" + fi + + # Make sure locales are in place before installing postgres + if [[ $({ perl --help > /dev/null; } 2>&1|grep 'locale failed') ]]; then + run sed -i "s/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/" /etc/locale.gen + run locale-gen + update-locale LANG=en_US.UTF-8 + fi + + run apt-get update + run apt-get install -y $APT + run pip install $PIP + + # Some versions of rabbitmq-server will not start automatically by default unless ... + sed -i "s/# Default-Start:.*/# Default-Start: 2 3 4 5/" /etc/init.d/rabbitmq-server + sed -i "s/# Default-Stop:.*/# Default-Stop: 0 1 6/" /etc/init.d/rabbitmq-server + run update-rc.d rabbitmq-server defaults +} +export -f install_requirements + + +print_startproject_help () { + cat <<- EOF + + ${bold}NAME${normal} + ${bold}orchestra-admin startproject${normal} - Create a new Django-Orchestra instance + + ${bold}SYNOPSIS${normal} + Options: [ -h ] + + ${bold}OPTIONS${normal} + ${bold}-h, --help${normal} + This help message + + ${bold}EXAMPLES${normal} + orchestra-admin startproject controlpanel + + EOF +} + + +function startproject () { + local PROJECT_NAME="$2"; shift + + opts=$(getopt -o h -l help -- "$@") || exit 1 + set -- $opts + + set -- $opts + while [ $# -gt 0 ]; do + case $1 in + -h|--help) print_startproject_help; exit 0 ;; + (--) shift; break;; + (-*) echo "$0: Err. - unrecognized option $1" 1>&2; exit 1;; + (*) break;; + esac + shift + done + + unset OPTIND + unset opt + + [ $(whoami) == 'root' ] && { echo -e "\nYou don't want to run this as root\n" >&2; exit 1; } + ORCHESTRA_PATH=$(get_orchestra_dir) || { echo "Error getting orchestra dir"; exit 1; } + if [[ ! -e $PROJECT_NAME/manage.py ]]; then + run django-admin.py startproject $PROJECT_NAME --template="${ORCHESTRA_PATH}/conf/project_template" + # This is a workaround for this issue https://github.com/pypa/pip/issues/317 + run chmod +x $PROJECT_NAME/manage.py + # End of workaround ### + else + echo "Not cloning: $PROJECT_NAME already exists." + fi + # Install bash autocompletition for django commands + if [[ ! $(grep 'source $HOME/.django_bash_completion.sh' ~/.bashrc &> /dev/null) ]]; then + # run wget https://raw.github.com/django/django/master/extras/django_bash_completion \ + # --no-check-certificate -O ~/.django_bash_completion.sh + cp ${ORCHESTRA_PATH}/bin/django_bash_completion.sh ~/.django_bash_completion.sh + echo 'source $HOME/.django_bash_completion.sh' >> ~/.bashrc + fi +} +export -f startproject + + +function print_install_certificate_help () { + cat <<- EOF + + ${bold}NAME${normal} + ${bold}orchetsra-admin install_certificate${normal} - Installs a valid all-purpose self signed certificate that is valid for the next ten years + + ${bold}OPTIONS${normal} + ${bold}-h, --help${normal} + Displays this help text + + EOF +} + +function install_certificate () { + opts=$(getopt -o h -l help -- "$@") || exit 1 + set -- $opts + + while [ $# -gt 0 ]; do + case $1 in + -h|--help) print_deploy_help; exit 0 ;; + (--) shift; break;; + (-*) echo "$0: Err. - unrecognized option $1" 1>&2; exit 1;; + (*) break;; + esac + shift + done + unset OPTIND + unset opt + + check_root + run openssl req -new -x509 -days 3650 -nodes -newkey rsa:4096 -out /etc/ssl/certs/mailserver.pem -keyout /etc/ssl/private/mailserver.pem + run chmod go= /etc/ssl/private/mailserver.pem +} +export -f install_certificate + + +function print_uninstall_certificate_help () { + cat <<- EOF + + ${bold}NAME${normal} + ${bold}orchetsra-admin uninstall_certificate${normal} - Remove self signed certificate + + ${bold}OPTIONS${normal} + ${bold}-h, --help${normal} + Displays this help text + + EOF +} + +function uninstall_certificate () { + opts=$(getopt -o h -l help -- "$@") || exit 1 + set -- $opts + + while [ $# -gt 0 ]; do + case $1 in + -h|--help) print_deploy_help; exit 0 ;; + (--) shift; break;; + (-*) echo "$0: Err. - unrecognized option $1" 1>&2; exit 1;; + (*) break;; + esac + shift + done + unset OPTIND + unset opt + + check_root + run rm -f /etc/ssl/private/mailserver.pem +} +export -f uninstall_certificate + + + +function print_install_postfix_help () { + cat <<- EOF + + ${bold}NAME${normal} + ${bold}orchetsra-admin install_postfix${normal} - Installs postfix server and its dependencies (dovecot, amavis, spamassassin and clamav) using apt-get. Also it generates a valid all-purpose certificate self signed that is valid for the next ten years. + + ${bold}OPTIONS${normal} + ${bold}-h, --help${normal} + Displays this help text + + EOF +} + + +function install_postfix () { + opts=$(getopt -o h -l help -- "$@") || exit 1 + set -- $opts + + while [ $# -gt 0 ]; do + case $1 in + -h|--help) print_deploy_help; exit 0 ;; + (--) shift; break;; + (-*) echo "$0: Err. - unrecognized option $1" 1>&2; exit 1;; + (*) break;; + esac + shift + done + unset OPTIND + unset opt + + check_root + ORCHESTRA_PATH=$(get_orchestra_dir) + + APT="postfix postfix-pgsql \ + swaks \ + dovecot-core dovecot-pop3d dovecot-imapd dovecot-antispam \ + dovecot-pgsql dovecot-sieve dovecot-managesieved dovecot-solr \ + amavisd-new spamassassin \ + clamav-freshclam clamav-base clamav clamav-daemon clamav-testfiles \ + " + run apt-get update + export DEBIAN_FRONTEND=noninteractive + run apt-get install -y $APT + unset $DEBIAN_FRONTEND; + run /usr/bin/freshclam + run apt-get --purge remove 'exim4*' -y + if [ ! -f /etc/ssl/private/mailserver.pem ]; then + install_certificate + fi; +} +export -f install_postfix + +function print_uninstall_postfix_help () { + cat <<- EOF + + ${bold}NAME${normal} + ${bold}orchetsra-admin uninstall_postfix${normal} - Uninstalls postfix server and its dependencies (dovecot, amavis, spamassassin and clamav) using dpkg and remove self signed certificate + + ${bold}OPTIONS${normal} + ${bold}-h, --help${normal} + Displays this help text + + EOF +} + + +function uninstall_postfix () { + opts=$(getopt -o h -l help -- "$@") || exit 1 + set -- $opts + + while [ $# -gt 0 ]; do + case $1 in + -h|--help) print_deploy_help; exit 0 ;; + (--) shift; break;; + (-*) echo "$0: Err. - unrecognized option $1" 1>&2; exit 1;; + (*) break;; + esac + shift + done + unset OPTIND + unset opt + + check_root + ORCHESTRA_PATH=$(get_orchestra_dir) + + APT="postfix postfix-pgsql \ + swaks \ + dovecot-core dovecot-pop3d dovecot-imapd dovecot-antispam \ + dovecot-pgsql dovecot-sieve dovecot-managesieved dovecot-solr \ + amavisd-new spamassassin \ + clamav-freshclam clamav-base clamav clamav-daemon libclamav6 clamav-testfiles \ + " + run dpkg -P --force-depends $APT + run apt-get update + run apt-get -f install -y + + if [ -d /var/run/amavis ]; then + run rm -rf /var/run/amavis + fi; + + if [ -d /var/lib/clamav ]; then + run rm -rf /var/lib/clamav + fi; + + if [ -f /etc/ssl/private/mailserver.pem ]; then + uninstall_certificate + fi; +} +export -f uninstall_postfix + + +[ $# -lt 1 ] && print_help +$1 "${@}" diff --git a/orchestra/bin/sieve-test b/orchestra/bin/sieve-test new file mode 100755 index 0000000000000000000000000000000000000000..da75303891277cc4f823ecc2d0aa8c9124bb6bd9 GIT binary patch literal 1295912 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

    Vp&;Lrb#hZRe|Fw4~mq?Rg<@iNUC%uat^Pp>@ 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èJusya!>ekR-&rPS2sV03xQT=zz=RMR2%1^ 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

      $@f0s2+=KUApiRl@_XN9*o7ue^KAx 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

      Bqavk@I3i`Yznlv@dRH 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

      ^5d8g1M#Qmr70u}*u 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

      Zs{6_xh(aRmzN4u>*X6&(x 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 z z-{us-aV^&3n%CLpL)`5}*b?UPf7IRF$iT910Gp4gN1MxVukTH||PM??0D8qSM{XF}L(X6@}^Yc1V(4A({Oi0ohk(-yJ_S`#+vNq2T@v zD)2iMxOi@_UE%H92hhvOjKv1wp4Fb=D9+I*k7rsJS~v36tt*hgk<%1yES<&fswsuLNp#tup*CrdpU z;Yw+Y6tU6d*=#s2O>Y>a^I;Tqac2vywemmpd=a}7zs~_UvaBt-Mv|<_4T!N@*x@36 zrWRJB*u%;1Cx4V$==wChp@y4~y?pahl^%b#owAz+Q}PY5#@RwHi-UJSo`7}!O%qM@Jf7=A{kCqeXhqJMID616Zg`3*C{1-*Tf$ zXwWcSnHH8K4Ht*cJ*Y~%iVT(6nB2!~inhl2Gu7PnB#F|poUu-YUGZ2M)StAIKljm~ z2Wo#EsCc}`{~o?WqCj>vnDP8H>=E%hV%5dWSAQmQQ3zDF#OnvXgT>at9NA^RZ13vn zKh)GmUeh$vWW58Sm*H!iWjxyuubzY=@rO-_#QGQqq)oNdUS2GSrjX^O-IBKLaNfAB z%{GfiyJM$os}W0)ChYTO1AcnxqmYh{4(D-0{4_2o1hFFt-0H7@_9 z7d%}i&jU$6fnLG&ViFiRBMlE&2QAp11ys|EkZ-qj8-^SY$MTX$Lp_C-7e^XBHm;c= z@B$O(+A3NT1+MGxeDQt|)8|IgE7{CZ%EbgQ^PwcXFjsq3B!Nity1R@9mAo+D$Nr`k z+rMfJa=gTM-#-XR#a%$7-M7SBr%0%)&?3FdK+9X=*ILCBaDQK5kAVfgL4i>2=#%dq zGoMCac`S7a=Mioxt^cSbS&`mQ+GLO27EO6Xip{S4tLn(bBaMm}6$1QW+6`a}qI;!(+!(bXdd7yR-}!O* z`9W?4mdm_(Qu^*pQMh8rN6t|QIpBHYOjU5<#$xx^CNBfI;**6%0>ie5eO@w8G>lOW z&Clc=O!Re$9MD6NU2%^0FHCV@!}nhMs`tyocMMMW|LC@#TCAh*}iye?JSzSi1B zTH9)?(%=%p8nD$jpjceN{f?u6r~$N^|Mz#!ok@cB{r}&;pO(zM_pHx(&U2pqG`KTJ z!=}U^UV__2a}+N=L)GrGBXO@BC|5H7M6)PA#Jv3$^y~p>I`q z&>ImhXj7Z)aEnJ>(vdiwD+NcS++Lmw78m7ya*X@@iPa5%d@H))DWlsqBo60dyJlDd zw0-`~6W&_blHC5)!Z2l{M4c{s{1A6q@n4(0IOVg?+jGx{8Rj2zQh#=nIjL`ch~Zec zWRBKxkzcQv%Oxz#LFlv1d&Vy=>CG>G1jV5PDyPHHl&fK{u;l}BTb(z)RVeI zYh}1AyY+s~sp^`S;&q($%ik?ro`HqmogzEmYX!?+A6Bq#3trJr^s zgBBYKG7}6m*cbNwD1Yx~nuoqG%v6~d@Eryd{Bk9k`xz-(OFN5c`1Qa>-Lit0dz5>S zS3b&gEn@TKc&qnkz`I<(2g$^){UGMi4%i}toIQ5$1d<` z%%?`Lq7XNWpToHb95v5ap;zX0&d$q5>*uS_irN}tVaWzsMN21OcN9+Ii67`>0Rq_< z4E<1lUy$EQEWs_#E6a?TbF7<^jw^<04<$Ok=*c;ssSmMWiV(-sck26qHq3ieZaH*+ zITya<10}BhOBSz`LG)hp6|-ZzkNWgp@I;uCGoGm6;E6@Sry=?#uYryL45h4b*C-$B z0*O`G(U$*{P?|IR>I}*L@eBOWcF{-q>1aHS`SFDRkKe3i?*LVLIu(T^B^q}NDaG4 zJWSdsZM(491-u*BB&a2cXSq$yumD_45_irDglbo0SNr(0+1rJECi{|4-v+N|iS0OG zCcbPMf2gf9JTariaNdD=N3c-(pab3DrPg@RfV9a>bw>PL{1_wI<%MG_&*d6e5kJNX z*v@Sd@Unk0{6zXrHQyD~e6(v>jFd33hC%!OOl?;(&+Nkde#pzq1M)FR-uG0gS!S5X zkuC7H=lv_CwYk^nF~XIH_Z7ZN3|4OQr&`E(ik)6&E8Gi zeH*vDE*Au?_I~-6gewz+ild-qbuG#doc?<#=<@3@3sx}SmUkvC`A`lysIp}tkO*%; zC_id`N?$O`M-;~(K*ygdUkRgN+u~t*5{gOS1Ki+|MxrcvTve%F{d<~|6ZDv zolQLbu%vA`@R1X90w8KZyJRdjKebA%yE+fspT(JHSt6?+wVw;k7}>D~N&M=CR=~Gz z9GOR{0`l4Oz-JgjZFicX8Xg4Hmoq5FJUN;`0FQtgg|E&$ADfW13(#)*#XBXX{kf3 z{C{bC4OWWoz6Fjmw;rPJTYUNQ*)L_F@s0}MUVz5j?GUdSYL&P#=S01irg{epc}d6I zOY}S}kT_j`gMq}v$Btu9+m3yasD+OC8de*ts5R%iJVnK;MR{{0Z+LF8&&h5dK0Sr< z8|57MuGvRwy*QBY^fw$x{yh7tEyr`Q#*u7?rryf{Yu!Iu+v|Ns++_t@%Xy89!QgXh zVAC3w9e&VVbeD=@`gqRUjOmh!?T;~S6#x2=?aHki&i9DxlovQ^-CQ@x{~lsG_;)7; zk2Y%=)&)~&A4T;UFZ>|snzhw_O&Z$85GDP#V+ze_x6Q#TBY%u(QJ ztn)F66x$_Id?gQO$!gvqxQ7hzD{jYj(}z(SlcvSB-a=B>A_n}WU19)^HVRgd|3Mc( z40xArBAJQdF@eV*JIJwLI+?pOb~2R%ld;m%Ht%#kAv*oU_#$`#I}xppnTX;!B}o-| z2l2&s)7|Dpp!|Y*t!b|VP=0|%shIVd9Z`|_|g2DX1*yf*hb99!Hv+uQxE4V zb=yj&XK5-e>|W%pZ)4x)eYRCoo`+uFOeymZ=%D^$Ly8d%bhgpn2Dpv zbtV0H3lIq;_UmL4fe+zETlkCG=uVO0z7<8ux6v>g547UM7N@t@OKmUU{PXR0Wb!D+ zyM$27qW&$&IRw_gsr2xUpC(d#D80lU@^Q|K6@}vHT=?T41MVpBS%zGpV!+$yYG@qd zE9%{f4a=SbzQE*XPYEXW@NnMt)gnX)A3no6gpculixjVo#kL+2&uim>`jm$AGjher zou<;epThO2lVuSZ7izsvtLbd7c*&e|WZ6uu#FeWuko+*_ynK3kg3f9R^m`rG>i#G~ z6l_>$k9XzYclR&d@~hJW#vEpjqHW&ZoxA%_OHY^{U;a*^cRp3xUXr@AByldcy5K8; zbFP6~1>$SBnV>F_D3ZIa_m_2=r=EHyj@J6{*MNn;22A`lc5iqQF3Bp1rqiTeOin4V&Kxa^tXmqebO`#3ESF@J68INC4rUYG+0pip5= z2Kh^qC|yTf<4P#tMWO`n@|bfMKFBdgTrB`I@n4^tu<9u$wv^4emz?{t>=?iHVsF!v zfWz$)Wr%DFP2A+X%AtrkVgwdSf2zv@hHTW9hP{*6k2u4CTw|b8#$CALeP(aZ1$$@o zoWE$@n0&o^k0ChjsG~zm2>nTnWw|Hpoh;7$nB`&JCylcZHS&1(leyW5V9) z+5O2a`s5J5vujQ8_^|)=>k~#M{y8C>SgQdWK4=gge# z-M|!`VPAVl-&0}|B#2qEW9*Yf74LiF_0{rvhS4L}k*^_xj;YVCdmkfxUb;{OOhZ>U zbOxdD=B+Ti?YXE-)w=Q_u{lb-78UkolWqk-I}l3m6Ao{6Z*U14&*&LMrRjuvf z_D}w@DZN+B&y3O9TdCQIt8(WW+(72ye@TpiKKS~L4|6I76d5qO)_WqoL_^1U$=mDK zU=*pT_TNMLHTuIr{iLwyJO#cD@Lz-DH^8zFId5i!mfWbbHFaY<%}dF7l;OWVLZ*+q zHyU~&f9lU6P~OXn3OS40RDLmu*TDn^LjH$$_|aF>h$ZkQKEc;4=1;|jTK9MJXcV4? z?6L8kk3Pg(nQSipQIIf>_nHr>bKVRsxnnJ_xfwD{^b+y9TRz9Qszf6*2JI``_|2yA zDQtBqM4#;7(R<;6;S8y!5mQp@R9-ADNJ4Y$x6A(^G{56sQ|-yS^e6qOM&z`$@UQp& z46HqOP}=w^YAX|wg0!{5u;k`lkQj;axZME3`*fDk+Mh=oYTn)s zX6C9+*)L&l-i4g~MtMh}O!r*IWZtRKNV4U_&9>k83g_E1eay zM`f?>fR}JqL{>Im8H>CeTKK9U^FnCJqgoJL5%&eK`=?(2JhCJCJcH53u)akOx2r;q zWkP-NdTkET*Tlhn1-jOW4PCJge>c2{w|VY!aLF89D+^4YIJdPhdwgqNj*(c}SuqFI zClPac5TR*uUe%Bq_(7Jf&(Fcq<{nJjLwonT{-Nobuc5Z_8acHhMDc|dX4Hf8Oy+ZN z-}}Lpl1h&U8s`mgY;SfO#F!!QLfYKN#GZH;oNxKH^PDyziMRC%1dn4`dLN&(=x|WJ=o3;|~)Nfm>nrb$f$VF0aGpB;Z&LemiYZ+Bq zkSQ~L3UPhV&3(_fAC?FD30bTk0jif7@W0hU?0kLe@kyz1|zGt!9@lxkovvzL-D z4)_NO&Kc>o{B;oUOVA_%?1~=>PCOEeJBkdq4EmXi!Ld=L0;+%;S~!(%oM$*smcJd0 z7bZ%J-GgFP2Z!1w@V*u%rW8(vYw*^{$#1-981u#8Q46&Ti!PO954L=X^NeXCJ^py^ zr>Yu}tW0GJ+=)TcZG8D_K~Sfv_NzrpX&0+}-3MHOJIXsn!(klcQjbOcsS(ZnyBTq< zv)yRy+EFz#fLeANS6XQZ^jYckTq;9yuv9+9zgB~hL}}BUb2Ug<3M?=%(sD_Gq@1Tn zP{wJb;B2simbqI`!->g1EFB!-&I-DXMPqm1pnbge=jj;ds%UNY^STL-nsiR9rZrut zP!FIc7wlAXB{42y5lX~4LyEoG>#<`ng-pF!d*E)Bj)yQiGQf56}kqmqVjB5ER7}d!D zV+f;q`SmhwAFKk!Dj*N+Aq5&tfq@p%d$w>41qRy>IqFCDP2%#9!ZpO!JD*qdXXc@W z9?~O!i?Psw#5$er-&1?OMZ!CUkya-v^5Hjc|3^CMfsi9`w zO$75AX=XJk)RPVACpAwx)W9(Wk77Qw&JDvw&1XBC%--vgL`#5cbaHz7YDO`k)Z6_B zlc#CpP!g={HBDYMPEqj7OJmYcm@wO&wCGJ_q-u8dAB3Kxyx;%WEP)1DvB7#vr=(A3 zsBLC2c!;tobeD=b`yy+a_j9(3-9E`INaC2jw&z%i;^fFabL&mbV@w8zzoJX|- zyr-wq5nRt8#v!L4vm8mE_g&7EHKx^J85tUL->VCrIhxazv)TLRxf*Wu_NVoNF{6+X zQh6c0?`hM)ZjbOb9r^YOCAWGx#l81vogxqMp7E%r=4YK3-RgT(WSuS2&3h49iU=8V zL%P{)(Jk76joN`PQ)I{veCnFP`ZV2j{n1$!@B0!16l{^!KF8A!u|r2Y*(Eu}ZK(ZT z)49=P{~p~iT&CHEY*-BBA|UWn+TMB3h=cH&F5|s|<)$Af5anvX^{Jryu=+MM+i>;X z6+rhUACVhz z?dqJ1EO7QHGRSrP%8gR!yyM+}j}a*1FwQseQ&55djA5`rXi**MEB+TXGW*7`q${wVx|2C9&m#HC3;xo@I=fkt zF47#bAD6>N4<2LYZ2FINmI}XTNmnv%PhH8oa|h#hE?yH=(z5M>j>&@%YP(TCeTTD9 zo6^y>+#;MM5w3*a;h!7WsD?l_XBUs|lr7CaT!drw4rE7?0 zi@`6necZXTJ+zQ~`+=I43FA*nzE_|AK36gJCibssxpsU(xIR5`7$Jxaq1x@{Mg^Qd z>mz-kMPa5HN_|MZhJqawD##u_Ag^+*4sEQ7r{e{oh5tjDxU;t9B;rit{mD1@kSGo< zxu$@7eMTmpsAjk!XRiY9 z9Z_UyYI3t!bP`xIKMzHCf4gS2EH)V2c|hNfl70Zw{Q6I+fZ)9g+P z4my_nbDp7Q8tE*DexfHLFYvVlu+wF6Oaf1IKv-8CdL5`LIrz$nxexoOPik zx2@%Y6LUXjP3rB>BhQCge`y9_lsQ^xOp(*!aC=}xH52b|9|)K^{ZXZdQq5@aE7=iC z_=cKZS1{L^!Tm4Y&ptBNdAE?fL%ZN=Mloa;9GZYp`I7M^*Dc1Zt-M_E(1WfHLnf(% zFkThswy1IDMZg_b#2{Mb#xPa=SXL!(ttfj~!#+(m}FGOqkHX1(9QS zhgvx~A+Rl{oMbj)GL-#>zkV$fMw~=2HS46i$Bqch-KxJvg=(_VMApwa2}$8#WHGKn zYI}`#3`(bi2WQ7n9*<+gve7`SWj7@r%9Y@Prq@-oVncmoyAmWtu`REN!~Kul9&4G` zzjr)T+lyX(8ng6Pqh~UPGZwncNB3G1G40zF43G_qNR~MV;v67JCLJtvizT$R7PGW+ zK!fv|P-?Kv5rezW^NvQfCVku-FvPb)dJU+u<>--?)2i$pA z81M{4tdv)i-KQnTFZnZ&z(Cj8HvfGdUg-ZtK`g*X|1$tE!HK9bz{HL9l36|%)ABXw zi2$YiB?eG;^8=!3reydICpO^%;Qj z9lGHG^6%@4dFqbRtz>p>}fFdk5+y*T+*&Y8x0{(fsnJ?50NQ$z4#n0K!pf6={ym6WQUv z!?zs%^Fa|@IeLe|(b$sc=2MfHM9q48sWBTv19_D$i<9D9(rkCcrpBF5WHjw6{w!|o z6sR;7ymv3%#?v?1dn~xNMZOOD&-=7r<9forAib>6>x@LkM@`#1;x24c8`?<0SdS(V&ecGJ83^w6 zGXBYri4+WaCZ2b}s(Yz6M80rSEoQbacHR5$c;LsoJWhJ=(ofWxbs`#4CmI+q+%9-e za~^~yHRuL9EOxwZ$9Kb>Mv^{ITfK=ptBOgOK|bKv6B>QoRVL-+Gj*+RCJ?2!c^B$z zT(C;5TD}1x$U>Y%7u4_`GE4rrMTET+*cD}%FLq7)G#*&?kxKkUCCGTebp3>>sNqw) z_MTmt+BItKp?!;5XuKeCbk8E4ei}v2-k$P8+#xa-?#h(G!0wnbU$~Ddbwklt=bjnA z=!_!EZa%x^54ZQ9Qs~?7H7NBFSX`L+RxW+?0ogb5_-ALGz*@H1>;vxswjZ_@d=*qT zFopKX^89JSpPxIYJg)TCQ`?kS3;&vf2q;kx8evMauyDw|HNF7@9|Fe4j^&7~74F&P zkn6VZGHv(l8P3_|_<~V?RFXX7^Go3Js`yehWoA50L~>}+S9y{@2RUGYao}^D_>`jT zB|OH(5x+!L+W_+5UwAN$a1Q>I-$v(4<_JtTr(HY@7UiN66egqUp>buN)2gY2+C(3v zAUbbQ>BA^Z&IwZ*H--}LJX1JPX~yBsD3KND7||mTM*b@(WA^T8%dKYIy&Q`5e)6?E zyt(GqtdlckJTxLFTZ1?Lyn6fnc@^4uwaKN#(7T$VR84t-EIG{js7@v|r5xy>Y^PiN zq5f>v4|W|$X8y`Mx2C+RW+okle%776)yIP&VosRVRI;ij@2!jEKVOu3H<4*X(S+AGhOPrL}BXbFkg?{7}*#N-M%^+TJk=V z{a$YEa~Q!y!)BAa%~9m4wgXs6_GP0HvJT-vPV4?aZbA>iO;SyqWRYRf(9c!_-v@II z4nn4Uo8Bjqjoix`%@IHONLgsuZjw5;#Q6fhjfGq#iZnRuk_To{QVJ(nw5!OSusj&~Jn=M373rP3HM`1$vt{oZ1ch$^ zg^ZHX4I1vP&nUjthU5*B&)}GdaZ#Q^yv<=2#6H2NTrf*_oO4(n?#HupTw@3j?-B-f z@-lBRx_w1z*H=QfcaJ1ws=)j6A@H+YY5}u_ zdJ%W>S=nD1yzBh&8aTg&{6qfT74=SQX*-Q%Z?r?wAPtXa^C>cxla20pnF$MvV_l|E$O;#I90oKobT$&HM%VHVohLU-OQFY%jvXMb3# z&YHm)btQck!D84j**@2hmCB8E7WKd@KffA)OaK+T?glD3Pn4T35i1Y{b3A@@`i&SkKV+gmval0(5+MfAK4rV&H=24dVfxZ1cSDyhhYTXvxJIz_3JH z_Vz*e=n`}8?C5HQJK=qY*1uOao=qGgKL08n-zG=`bK&~^`@m}5OBwLL_YU3oFV=`( z5dD1(skp*s_+UolYe{Q*p|*!96IxQ&h!orQjw!{ZzitxLzYG;ge`1cfA)^&d-(K26 zVefmr^4?3F@7`|SFP)$k9$X*4XhXcv7NO655{H?dTgf0Km0n~c_>ny(k1xEq;n4Vk z)i;n~*B-c$cds8cFn`_5lG;(wXU@a|wt_wT2I{;2fA1Qz_!>*S7h-_J<}PV)FDY(0 zy@W`px$}&j;IB8Cg-4L7DQiJ&X;pv_M#$Py0jYHZPpP%xSUb=DZNO$->mUj<2!xtd z=7q*!4>hw|%02_-NLNc7(++R1r^y2<9akM&jN@nG%~FU;3o8Acw^B+*&OZks7;Y3w z9M(U@-WBEzg0LP-qy?F#Ju~6}c2N2WvkcTP`d%;)B^^$y!i~R#)@ORm-e_S0nn8PA zqwyiPcnQ6lp${Tl)65U}b$-vvy_}~X^{x~Z*D<{5r89IC$i4%j@{fMO?(&a*u}%0# zzswZE`ejC6reEdy%JESve<}Xauc&@S^((Glas6tP8>9S`^W;=ND(&$2?A0N~=IB@k z&9FDV5j9P#fe4&HY8TFQ!gWS;I^KTCFvf1@>Hd$+HoOkYhcc>4)+HXy+qGKb%(U0i9cWXq{tBc$gL z|H#tLlJ^36bKmyv_M2$&n@E0z6&cfIxBHs-UGV~})PgatcA_t{4^8dbC)6rN1g_j4 zPpI`cp2ctGu2R3zG&}EGXdw3uwKkYug+q~=%FwMkLf{t8$zgAv_2HO=EMb<`$y?6J z>&Jn{UFOS(i@O4WaKL-iHr~s*0tLeo?j2yrI0vt3Iq~~k%Y%5J?5{Pxfp&@l-sQHP zc6C|YrR4KkxjWlM84~kN>pcJ6(ooZOMheCO`W1KCD3O|Sdn#t+e&f^sDdqOtiqI{( zg*3ikg=ir9<VkQuf%T%xnhj^Yrzsif&(r50i1{T2LS!xwD%;&HDm;?2^8p zvMdkpc=s$ahe^9kL-F*(M)$|-zyB6OCM-Nz@2gMQw=i(SCCJ8VM=GZF@AG~~JaqI^ z`lokCsCf8!PwW-g5xQW-j?fR5^Stj=o-YY~dwD4JB9$mMZf(5!46ynJg)*CQrAJ4( z1wORU=w+Psu<(}%eyI7J+ssJdirF@|!RmwH1D)@-4G!vj^W#H1{}<2dycuPsI_^nz zQ%C>VBg=8k00zTjN9erearLDCT5Pq7<=Iv7#~%2j{ZGl4*T2h%qRuY%y#WzTiK_S}=gmd4C0i6${?8z#KtheOV}=`^Gnw2MB)OBFUe_ zV3~1Z_l`SK3`RE^0M!{}Iq>cR;UqBLTBwC#569i%i6zn$aRaV<+r1QzkL@LNaAfF~ ze;Fq}HuRHh4-U6QQBVgKVQm4^Y@b^C9k8^FSMBCQoWxH�$-pg~X}fN*JO34{yGx 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

      7FqwV`k5b&93jv<5~RgvO`p(=_n&^ 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

      oB+2X*={ICU0$0E~3j>p4~I5X*6Lw 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 255: + return False + if hostname[-1] == ".": + hostname = hostname[:-1] # strip exactly one dot from the right, if present + allowed = re.compile("(?!-)[A-Z\d-]{1,63}(? 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_root(), 'manage.py') + run("python %s collectstatic --noinput" % manage_path) + run("python %s syncdb --noinput" % 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') diff --git a/orchestra/management/commands/restartservices.py b/orchestra/management/commands/restartservices.py new file mode 100644 index 00000000..50c0c91c --- /dev/null +++ b/orchestra/management/commands/restartservices.py @@ -0,0 +1,11 @@ +from django.core.management.base import BaseCommand + +from orchestra.management.commands.startservices import ManageServiceCommand +from orchestra.settings import RESTART_SERVICES + + +class Command(ManageServiceCommand): + services = RESTART_SERVICES + action = 'restart' + option_list = BaseCommand.option_list + help = 'Restart all related services. Usefull for reload configuration and files.' diff --git a/orchestra/management/commands/setupcelery.py b/orchestra/management/commands/setupcelery.py new file mode 100644 index 00000000..a6bf9c3f --- /dev/null +++ b/orchestra/management/commands/setupcelery.py @@ -0,0 +1,103 @@ +from optparse import make_option +from os import path + +from django.core.management.base import BaseCommand + +from orchestra.utils.paths import get_site_root, get_orchestra_root +from orchestra.utils.system import run, check_root + + +class Command(BaseCommand): + def __init__(self, *args, **kwargs): + super(Command, self).__init__(*args, **kwargs) + self.option_list = BaseCommand.option_list + ( + make_option('--username', dest='username', default='orchestra', + help='Specifies the system user that would run celeryd.'), + make_option('--processes', dest='processes', default=5, + help='Number of celeryd processes.'), + make_option('--noinput', action='store_false', dest='interactive', default=True, + help='Tells Django to NOT prompt the user for input of any kind. ' + 'You must use --username with --noinput, and must contain the ' + 'cleleryd process owner, which is the user how will perform tincd updates'), + ) + + option_list = BaseCommand.option_list + help = 'Configure Celeryd to run with your orchestra instance.' + + @check_root + def handle(self, *args, **options): + context = { + 'site_root': get_site_root(), + 'username': options.get('username'), + 'bin_path': path.join(get_orchestra_root(), 'bin'), + 'processes': options.get('processes'), + } + + celery_config = ( + '# Name of nodes to start, here we have a single node\n' + 'CELERYD_NODES="w1"\n' + '\n' + '# Where to chdir at start.\n' + 'CELERYD_CHDIR="%(site_root)s"\n' + '\n' + '# How to call "manage.py celeryd_multi"\n' + 'CELERYD_MULTI="$CELERYD_CHDIR/manage.py celeryd_multi"\n' + '\n' + '# Extra arguments to celeryd\n' + 'CELERYD_OPTS="-P:w1 processes -c:w1 %(processes)s -Q:w1 celery"\n' + '\n' + '# Name of the celery config module.\n' + 'CELERY_CONFIG_MODULE="celeryconfig"\n' + '\n' + '# %%n will be replaced with the nodename.\n' + 'CELERYD_LOG_FILE="/var/log/celery/%%n.log"\n' + 'CELERYD_PID_FILE="/var/run/celery/%%n.pid"\n' + 'CELERY_CREATE_DIRS=1\n' + '\n' + '# Full path to the celeryd logfile.\n' + 'CELERYEV_LOG_FILE="/var/log/celery/celeryev.log"\n' + 'CELERYEV_PID_FILE="/var/run/celery/celeryev.pid"\n' + '\n' + '# Workers should run as an unprivileged user.\n' + 'CELERYD_USER="%(username)s"\n' + 'CELERYD_GROUP="$CELERYD_USER"\n' + '\n' + '# Persistent revokes\n' + 'CELERYD_STATE_DB="$CELERYD_CHDIR/persistent_revokes"\n' + '\n' + '# Celeryev\n' + 'CELERYEV="$CELERYD_CHDIR/manage.py"\n' + 'CELERYEV_CAM="djcelery.snapshot.Camera"\n' + 'CELERYEV_USER="$CELERYD_USER"\n' + 'CELERYEV_GROUP="$CELERYD_USER"\n' + 'CELERYEV_OPTS="celerycam"\n' + '\n' + '# Celerybeat\n' + 'CELERYBEAT="${CELERYD_CHDIR}/manage.py celerybeat"\n' + 'CELERYBEAT_USER="$CELERYD_USER"\n' + 'CELERYBEAT_GROUP="$CELERYD_USER"\n' + 'CELERYBEAT_CHDIR="$CELERYD_CHDIR"\n' + 'CELERYBEAT_OPTS="--schedule=/var/run/celerybeat-schedule"\n' % 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 = ( + '/var/log/celery/*.log {\n' + ' weekly\n' + ' missingok\n' + ' rotate 10\n' + ' compress\n' + ' delaycompress\n' + ' notifempty\n' + ' copytruncate\n' + '}' + ) + run("echo '%s' > /etc/logrotate.d/celeryd" % rotate) diff --git a/orchestra/management/commands/setupnginx.py b/orchestra/management/commands/setupnginx.py new file mode 100644 index 00000000..811b0b82 --- /dev/null +++ b/orchestra/management/commands/setupnginx.py @@ -0,0 +1,129 @@ +from optparse import make_option +from os.path import expanduser + +from django.conf import settings +from django.core.management.base import BaseCommand +from django.utils.six.moves import input + +from orchestra.utils.paths import get_project_root, get_site_root, get_project_name +from orchestra.utils.system import run, check_root, get_default_celeryd_username + + +class Command(BaseCommand): + def __init__(self, *args, **kwargs): + super(Command, self).__init__(*args, **kwargs) + self.option_list = BaseCommand.option_list + ( + make_option('--user', dest='user', default=get_default_celeryd_username(), + help='uWSGI daemon user.'), + make_option('--group', dest='group', default='', + help='uWSGI daemon group.'), + make_option('--processes', dest='processes', default=4, + help='uWSGI number of processes.'), + make_option('--noinput', action='store_false', dest='interactive', default=True, + help='Tells Django to NOT prompt the user for input of any kind. ' + 'You must use --username with --noinput, and must contain the ' + 'cleeryd process owner, which is the user how will perform tincd updates'), + ) + + option_list = BaseCommand.option_list + help = 'Configures nginx + uwsgi to run with your Orchestra instance.' + + @check_root + def handle(self, *args, **options): + interactive = options.get('interactive') + + context = { + 'project_name': get_project_name(), + 'project_root': get_project_root(), + 'site_root': get_site_root(), + 'static_root': settings.STATIC_ROOT, + 'user': options.get('user'), + 'group': options.get('group') or options.get('user'), + 'home': expanduser("~%s" % options.get('user')), + 'processes': int(options.get('processes')),} + + nginx_conf = ( + 'server {\n' + ' listen 80;\n' + ' listen [::]:80 ipv6only=on;\n' + ' rewrite ^/$ /admin;\n' + ' client_max_body_size 500m;\n' + ' location / {\n' + ' uwsgi_pass unix:///var/run/uwsgi/app/%(project_name)s/socket;\n' + ' include uwsgi_params;\n' + ' }\n' + ' location /static {\n' + ' alias %(static_root)s;\n' + ' expires 30d;\n' + ' }\n' + '}\n') % context + + uwsgi_conf = ( + '[uwsgi]\n' + 'plugins = python\n' + 'chdir = %(site_root)s\n' + 'module = %(project_name)s.wsgi\n' + 'master = true\n' + 'processes = %(processes)d\n' + 'chmod-socket = 664\n' + 'stats = /run/uwsgi/%%(deb-confnamespace)/%%(deb-confname)/statsocket\n' + 'vacuum = true\n' + 'uid = %(user)s\n' + 'gid = %(group)s\n' + 'env = HOME=%(home)s\n') % context + + nginx = { + 'file': '/etc/nginx/conf.d/%(project_name)s.conf' % context, + 'conf': nginx_conf } + uwsgi = { + 'file': '/etc/uwsgi/apps-available/%(project_name)s.ini' % context, + 'conf': uwsgi_conf } + + for extra_context in (nginx, uwsgi): + context.update(extra_context) + diff = run("echo '%(conf)s'|diff - %(file)s" % context, error_codes=[0,1,2]) + if diff.return_code == 2: + # File does not exist + run("echo '%(conf)s' > %(file)s" % context) + elif diff.return_code == 1: + # File is different, save the old one + if interactive: + msg = ("\n\nFile %(file)s be updated, do you like to overide " + "it? (yes/no): " % context) + confirm = input(msg) + while 1: + if confirm not in ('yes', 'no'): + confirm = input('Please enter either "yes" or "no": ') + continue + if confirm == 'no': + return + break + run("cp %(file)s %(file)s.save" % context) + run("echo '%(conf)s' > %(file)s" % context) + 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) + + run('ln -s /etc/uwsgi/apps-available/%(project_name)s.ini /etc/uwsgi/apps-enabled/' % context, error_codes=[0,1]) + + # nginx should start after tincd + current = "\$local_fs \$remote_fs \$network \$syslog" + run('sed -i "s/ %s$/ %s \$named/g" /etc/init.d/nginx' % (current, current)) + + rotate = ( + '/var/log/nginx/*.log {\n' + ' daily\n' + ' missingok\n' + ' rotate 30\n' + ' compress\n' + ' delaycompress\n' + ' notifempty\n' + ' create 640 root adm\n' + ' sharedscripts\n' + ' postrotate\n' + ' [ ! -f /var/run/nginx.pid ] || kill -USR1 `cat /var/run/nginx.pid`\n' + ' endscript\n' + '}\n') + run("echo '%s' > /etc/logrotate.d/nginx" % rotate) + + # Allow nginx to write to uwsgi socket + run('adduser www-data %(group)s' % context) diff --git a/orchestra/management/commands/setuppostfix.py b/orchestra/management/commands/setuppostfix.py new file mode 100644 index 00000000..78c15812 --- /dev/null +++ b/orchestra/management/commands/setuppostfix.py @@ -0,0 +1,321 @@ +import os +import sys + +from optparse import make_option + +from django.core.management.base import BaseCommand + +from orchestra.utils.system import run, check_root + +class Command(BaseCommand): + def __init__(self, *args, **kwargs): + super(Command, self).__init__(*args, **kwargs) + self.option_list = BaseCommand.option_list + ( + make_option('--db_name', dest='db_name', default='orchestra', + help='Specifies the database to create.'), + make_option('--db_user', dest='db_user', default='orchestra', + help='Specifies the database to create.'), + make_option('--db_password', dest='db_password', default='orchestra', + help='Specifies the database to create.'), + make_option('--db_host', dest='db_host', default='localhost', + help='Specifies the database to create.'), + + make_option('--vmail_username', dest='vmail_username', default='vmail', + help='Specifies username in the operating system (default=vmail).'), + make_option('--vmail_uid', dest='vmail_uid', default='5000', + help='UID of user (default=5000).'), + make_option('--vmail_groupname', dest='vmail_groupname', default='vmail', + help='Specifies the groupname in the operating system (default=vmail).'), + make_option('--vmail_gid', dest='vmail_gid', default='5000', + help='GID of user (default=5000).'), + make_option('--vmail_home', dest='vmail_home', default='/var/vmail', + help='$HOME of user (default=/var/vmail).'), + + make_option('--dovecot_dir', dest='dovecot_dir', default='/etc/dovecot', + help='Dovecot root directory (default=/etc/dovecot).'), + + make_option('--postfix_dir', dest='postfix_dir', default='/etc/postfix', + help='Postfix root directory (default=/etc/postfix).'), + + make_option('--amavis_dir', dest='amavis_dir', default='/etc/amavis', + help='Amavis root directory (default=/etc/amavis).'), + + make_option('--noinput', action='store_false', dest='interactive', default=True, + help='Tells Django to NOT prompt the user for input of any kind. ' + 'You must use --username with --noinput, and must contain the ' + 'cleeryd process owner, which is the user how will perform tincd updates'), + ) + + option_list = BaseCommand.option_list + help = 'Setup Postfix.' + + @check_root + 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 = > %(settings)s' % context) diff --git a/orchestra/management/commands/startservices.py b/orchestra/management/commands/startservices.py new file mode 100644 index 00000000..f06fb26f --- /dev/null +++ b/orchestra/management/commands/startservices.py @@ -0,0 +1,59 @@ +from optparse import make_option + +from django.core.management.base import BaseCommand + +from orchestra.settings import START_SERVICES +from orchestra.utils.system import run, check_root + + +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 + + +def flatten(nested, depth=0): + if hasattr(nested, '__iter__'): + for sublist in nested: + for element in flatten(sublist, depth+1): + yield element + else: + yield nested + + + +class ManageServiceCommand(BaseCommand): + def __init__(self, *args, **kwargs): + super(ManageServiceCommand, self).__init__(*args, **kwargs) + self.option_list = BaseCommand.option_list + tuple( + make_option('--no-%s' % service, action='store_false', dest=service, default=True, + help='Do not %s %s' % (self.action, service)) for service in flatten(self.services) + ) + + @check_root + def handle(self, *args, **options): + for service in self.services: + if isinstance(service, str): + run_tuple(service, self.action, options) + else: + failure = True + for opt_service in service: + if run_tuple(opt_service, self.action, options, optional=True): + failure = False + break + if failure: + str_services = [ str(s) for s in service ] + self.stderr.write('Error %sing %s' % (self.action, ' OR '.join(str_services))) + + +class Command(ManageServiceCommand): + services = START_SERVICES + action = 'start' + option_list = BaseCommand.option_list + help = 'Start all related services. Usefull for reload configuration and files.' diff --git a/orchestra/management/commands/stopservices.py b/orchestra/management/commands/stopservices.py new file mode 100644 index 00000000..14bf0edc --- /dev/null +++ b/orchestra/management/commands/stopservices.py @@ -0,0 +1,11 @@ +from django.core.management.base import BaseCommand + +from orchestra.management.commands.startservices import ManageServiceCommand +from orchestra.settings import STOP_SERVICES + + +class Command(ManageServiceCommand): + services = STOP_SERVICES + action = 'stop' + option_list = BaseCommand.option_list + help = 'Stop all related services. Usefull for reload configuration and files.' diff --git a/orchestra/management/commands/upgradeorchestra.py b/orchestra/management/commands/upgradeorchestra.py new file mode 100644 index 00000000..eafcfa49 --- /dev/null +++ b/orchestra/management/commands/upgradeorchestra.py @@ -0,0 +1,98 @@ +import functools +import os +import random +import string +from distutils.sysconfig import get_python_lib +from optparse import make_option + +from django.core.management import call_command +from django.core.management.base import BaseCommand, CommandError + +from orchestra import get_version +from orchestra.utils.system import run, check_root + + +r = functools.partial(run, silent=False) + + +def get_existing_pip_installation(): + """ returns current pip installation path """ + if run("pip freeze|grep django-orchestra", error_codes=[0,1]).return_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 + + +class Command(BaseCommand): + def __init__(self, *args, **kwargs): + super(Command, self).__init__(*args, **kwargs) + self.option_list = BaseCommand.option_list + ( + make_option('--pip_only', action='store_true', dest='pip_only', default=False, + help='Only run "pip install django-orchestra --upgrade"'), + make_option('--orchestra_version', dest='version', default=False, + help='Specifies what version of the Orchestra you want to install'), + ) + + option_list = BaseCommand.option_list + help = "Upgrading Orchestra's installation. Desired version is accepted as argument" + can_import_settings = False + leave_locale_alone = True + + @check_root + 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: + # (Fucking 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) diff --git a/orchestra/models/__init__.py b/orchestra/models/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/orchestra/models/fields.py b/orchestra/models/fields.py new file mode 100644 index 00000000..50a0e5e1 --- /dev/null +++ b/orchestra/models/fields.py @@ -0,0 +1,57 @@ +from django.db import models +from django.utils.text import capfirst + +from ..forms.fields import MultiSelectFormField +from ..utils.apps import isinstalled + + +class MultiSelectField(models.CharField): + __metaclass__ = models.SubfieldBase + + def formfield(self, **kwargs): + defaults = { + 'required': not self.blank, + 'label': capfirst(self.verbose_name), + 'help_text': self.help_text, + 'choices': self.choices + } + if self.has_default(): + defaults['initial'] = eval(self.get_default()) + defaults.update(kwargs) + return MultiSelectFormField(**defaults) + + def get_db_prep_value(self, value, connection=None, prepared=False): + if isinstance(value, basestring): + return value + elif isinstance(value, list): + return ','.join(value) + + def to_python(self, value): + if value is not None: + return value if isinstance(value, list) else value.split(',') + return '' + + def contribute_to_class(self, cls, name): + super(MultiSelectField, self).contribute_to_class(cls, name) + if self.choices: + def func(self, field=name, choices=dict(self.choices)): + ','.join([ choices.get(value, value) for value in getattr(self, field) ]) + setattr(cls, 'get_%s_display' % self.name, func) + + def validate(self, value, model_instance): + arr_choices = self.get_choices_selected(self.get_choices_default()) + for opt_select in value: + if (opt_select not in arr_choices): + msg = self.error_messages['invalid_choice'] % value + raise exceptions.ValidationError(msg) + return + + def get_choices_selected(self, arr_choices=''): + if not arr_choices: + return False + return [ value for value,__ in arr_choices ] + + +if isinstalled('south'): + from south.modelsinspector import add_introspection_rules + add_introspection_rules([], ["^controller\.models\.fields\.MultiSelectField"]) diff --git a/orchestra/models/utils.py b/orchestra/models/utils.py new file mode 100644 index 00000000..9b3710ab --- /dev/null +++ b/orchestra/models/utils.py @@ -0,0 +1,49 @@ +from django.conf import settings +from django.db.models import loading, Manager +from django.utils import importlib + + +def get_model(label, import_module=True): + """ returns the modeladmin registred for model """ + app_label, model_name = label.split('.') + model = loading.get_model(app_label, model_name) + if model is None: + # Sometimes the models module is not yet imported + for app in settings.INSTALLED_APPS: + if app.endswith(app_label): + app_label = app + importlib.import_module('%s.%s' % (app_label, 'admin')) + return loading.get_model(*label.split('.')) + return model + + +def queryset_as_manager(qs_class): + # Allow chained managers + # Based on http://djangosnippets.org/snippets/562/#c2486 + class ChainerManager(Manager): + def __init__(self): + super(ChainerManager,self).__init__() + self.queryset_class = qs_class + + def get_query_set(self): + return self.queryset_class(self.model) + + def __getattr__(self, attr, *args): + try: + return getattr(type(self), attr, *args) + except AttributeError: + return getattr(self.get_query_set(), attr, *args) + return ChainerManager() + + +def get_field_value(obj, field_name): + names = field_name.split('__') + rel = getattr(obj, names.pop(0)) + for name in names: + try: + rel = getattr(rel, name) + except AttributeError: + # maybe it is a query manager + rel = getattr(rel.get(), name) + return rel + diff --git a/orchestra/permissions/__init__.py b/orchestra/permissions/__init__.py new file mode 100644 index 00000000..921dfe14 --- /dev/null +++ b/orchestra/permissions/__init__.py @@ -0,0 +1 @@ +from .options import * diff --git a/orchestra/permissions/api.py b/orchestra/permissions/api.py new file mode 100644 index 00000000..5bd892cd --- /dev/null +++ b/orchestra/permissions/api.py @@ -0,0 +1,28 @@ +from django.core.urlresolvers import resolve +from rest_framework import exceptions +from rest_framework.permissions import DjangoModelPermissions + + +class OrchestraPermissionBackend(DjangoModelPermissions): + """ Permissions according to each user """ + + def has_permission(self, request, view): + model_cls = getattr(view, 'model', None) + if not model_cls: + name = resolve(request.path).url_name + return name == 'api-root' + + perms = self.get_required_permissions(request.method, model_cls) + if (request.user and + request.user.is_authenticated() and + request.user.has_perms(perms, model_cls)): + return True + return False + + def has_object_permission(self, request, view, obj): + perms = self.get_required_permissions(request.method, type(obj)) + if (request.user and + request.user.is_authenticated() and + request.user.has_perms(perms, obj)): + return True + return False diff --git a/orchestra/permissions/auth.py b/orchestra/permissions/auth.py new file mode 100644 index 00000000..2006ef6e --- /dev/null +++ b/orchestra/permissions/auth.py @@ -0,0 +1,48 @@ +from django.contrib.auth.backends import ModelBackend +from django.db.models.loading import get_model, get_app, get_models + + +class OrchestraPermissionBackend(ModelBackend): + supports_object_permissions = True + supports_anonymous_user = False + supports_inactive_user = False + + def has_perm(self, user, perm, obj=None): + """ perm 'app.action_model' """ + if not user.is_active: + return False + + perm_type = perm.split('.')[1].split('_')[0] + if obj is None: + app_label = perm.split('.')[0] + model_label = perm.split('_')[1] + model = get_model(app_label, model_label) + perm_manager = model + else: + perm_manager = obj + + try: + is_authorized = perm_manager.has_permission(user, perm_type) + except AttributeError: + is_authorized = False + + return is_authorized + + def has_module_perms(self, user, app_label): + """ + Returns True if user_obj has any permissions in the given app_label. + """ + if not user.is_active: + return False + app = get_app(app_label) + for model in get_models(app): + try: + has_perm = model.has_permission.view(user) + except AttributeError: + pass + else: + if has_perm: + return True + return False + + diff --git a/orchestra/permissions/options.py b/orchestra/permissions/options.py new file mode 100644 index 00000000..1838ecd5 --- /dev/null +++ b/orchestra/permissions/options.py @@ -0,0 +1,107 @@ +import functools +import inspect + + +# WARNING: *MAGIC MODULE* +# This is not a safe place, lot of magic is happening here + + +class Permission(object): + """ + Base class used for defining class and instance permissions. + Enabling an ''intuitive'' interface for checking permissions: + + # Define permissions + class NodePermission(Permission): + def change(self, obj, cls, user): + return obj.user == user + + # Provide permissions + Node.has_permission = NodePermission() + + # Check class permission by passing it as string + Node.has_permission(user, 'change') + + # Check class permission by calling it + Node.has_permission.change(user) + + # Check instance permissions + node = Node() + node.has_permission(user, 'change') + node.has_permission.change(user) + """ + def __get__(self, obj, cls): + """ Hacking object internals to provide means for the mentioned interface """ + # call interface: has_permission(user, 'perm') + def call(user, perm): + return getattr(self, perm)(obj, cls, user) + + # has_permission.perm(user) + for func in inspect.getmembers(type(self), predicate=inspect.ismethod): + if func[1].im_class is not type(self): + # aggregated methods + setattr(call, func[0], functools.partial(func[1], obj, cls)) + else: + # self methods + setattr(call, func[0], functools.partial(func[1], self, obj, cls)) + return call + + def _aggregate(self, obj, cls, perm): + """ Aggregates cls methods to self class""" + for method in inspect.getmembers(perm, predicate=inspect.ismethod): + if not method[0].startswith('_'): + setattr(type(self), method[0], method[1]) + + +class ReadOnlyPermission(Permission): + """ Read only permissions """ + def view(self, obj, cls, user): + return True + + +class AllowAllPermission(object): + """ All methods return True """ + def __get__(self, obj, cls): + return self.AllowAllWrapper() + + class AllowAllWrapper(object): + """ Fake object that always returns True """ + def __call__(self, *args): + return True + + def __getattr__(self, name): + return lambda n: True + + +class RelatedPermission(Permission): + """ + Inherit permissions of a related object + + The following example will inherit permissions from sliver_iface.sliver.slice + SliverIfaces.has_permission = RelatedPermission('sliver.slices') + """ + def __init__(self, relation): + self.relation = relation + + def __get__(self, obj, cls): + """ Hacking object internals to provide means for the mentioned interface """ + # Walk through FK relations + relations = self.relation.split('.') + if obj is None: + parent = cls + for relation in relations: + parent = getattr(parent, relation).field.rel.to + else: + parent = reduce(getattr, relations, obj) + + # call interface: has_permission(user, 'perm') + def call(user, perm): + return parent.has_permission(user, perm) + + # method interface: has_permission.perm(user) + for name, func in parent.has_permission.__dict__.iteritems(): + if not name.startswith('_'): + setattr(call, name, func) + + return call + diff --git a/orchestra/settings.py b/orchestra/settings.py new file mode 100644 index 00000000..75b8585b --- /dev/null +++ b/orchestra/settings.py @@ -0,0 +1,28 @@ +from django.conf import settings +from django.utils.translation import ugettext_lazy as _ + + +# Domain name used when it will not be possible to infere the domain from a request +# For example in periodic tasks +SITE_URL = getattr(settings, 'SITE_URL', 'http://localhost') + +SITE_NAME = getattr(settings, 'SITE_NAME', 'confine') + +SITE_VERBOSE_NAME = getattr(settings, 'SITE_VERBOSE_NAME', + _("%s Hosting Management" % SITE_NAME.capitalize())) + + +# Service management commands + +START_SERVICES = getattr(settings, 'START_SERVICES', + ['postgresql', 'celeryevcam', 'celeryd', 'celerybeat', ('uwsgi', 'nginx'),] +) + +RESTART_SERVICES = getattr(settings, 'RESTART_SERVICES', + ['celeryd', 'celerybeat', 'uwsgi'] +) + +STOP_SERVICES = getattr(settings, 'STOP_SERVICES', + [('uwsgi', 'nginx'), 'celerybeat', 'celeryd', 'celeryevcam', 'postgresql'] +) + diff --git a/orchestra/static/admin/css/login.css b/orchestra/static/admin/css/login.css new file mode 100644 index 00000000..99cc756e --- /dev/null +++ b/orchestra/static/admin/css/login.css @@ -0,0 +1,99 @@ +/* LOGIN FORM */ + + + +#header #branding h1 { + margin: 0; + padding: 5px 10px; + background: transparent url(/static/orchestra/images/orchestra-logo.png) 10px 5px no-repeat; + text-indent: 0; + height: 31px; + width: 420px; + font-size: 18px; + color: whitesmoke; + font-weight: bold; + padding-left: 50px; + line-height: 30px; +} + +.version { + font-size: 0%; +} + +#header #branding:hover a { + text-decoration: none; +} + +.login #container #content h1 { + text-align: center; +} + +.login #container #content p { + text-align: center; +} + +.login .register-links { + text-align: right +} + + +body.login { + background: #eee; +} + +.login #container { + background: white; + border: 1px solid #ccc; + width: 28em; + min-width: 460px; + margin-left: auto; + margin-right: auto; + margin-top: 100px; +} + +.login #content-main { +/*changed*/ + width: 90%; + margin-left: 20px; +} + +.login form { + margin-top: 1em; +} + +.login .form-row { + padding: 4px 0; + float: left; + width: 100%; +} + +.login .form-row label { + padding-right: 0.5em; + line-height: 2em; + font-size: 1em; + clear: both; + color: #333; +} + +.login .form-row #id_username, .login .form-row #id_password { + clear: both; + padding: 6px; + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.login span.help { + font-size: 10px; + display: block; +} + +.login .submit-row { + clear: both; + padding: 1em 0 0 9.4em; +} + +.login .password-reset-link { + text-align: center; +} diff --git a/orchestra/static/admin_tools/css/theming.css b/orchestra/static/admin_tools/css/theming.css new file mode 100644 index 00000000..2ab98cbe --- /dev/null +++ b/orchestra/static/admin_tools/css/theming.css @@ -0,0 +1,21 @@ +/** + * theming styles + * + */ + +#header { + background: url(../images/admin-tools.png) 0 0 repeat-x; +} + +div.breadcrumbs { + display: block; + padding: 10px 15px; + border: 0; + background-position: 0 -8px; + border-bottom: 1px solid #ededed; +} + +div.breadcrumbs a { + display: inline; +} + diff --git a/orchestra/static/orchestra/css/adminextraprettystyle.css b/orchestra/static/orchestra/css/adminextraprettystyle.css new file mode 100644 index 00000000..df03ecb4 --- /dev/null +++ b/orchestra/static/orchestra/css/adminextraprettystyle.css @@ -0,0 +1,95 @@ +body { + background:#FBFAF9 url(/static/orchestra/images/page-gradient.png)top left repeat-x; +} + +#header #branding h1 { + margin: 0; + padding: 5px 10px; + background: transparent url(/static/orchestra/images/orchestra-logo.png) 10px 5px no-repeat; + text-indent: 0; + height: 31px; + font-size: 18px; + color: whitesmoke; + font-weight: bold; + padding-left: 50px; + line-height: 30px; +} + +.version:before { + content: "v"; + opacity: 0.6; + padding-right: 0.25em; +} + +.version { + font-size: 60%; +} + +#header #branding:hover a { + text-decoration: none; + color: azure; +} + +#header-branding { + background-image: url(/static/admin_tools/images/admin-tools.png); + width: 100%; + z-index: -1; + height: 41px; + position: absolute; +} + +#header-menu { + background: url(/static/admin_tools/images/admin-tools.png) 0 295px; + width: 100%; + z-index: -1; + margin-top: 41px; + height: 41px; + position: absolute; +} + +#header-breadcrumb { + width: 100%; + z-index: -1; + margin-top: 76px; + height: 69px; + position: absolute; + background-attachment: scroll; background-clip: border-box; + background-color: rgb(255, 255, 255); + background-image: url(/static/admin/img/nav-bg-reverse.gif); + background-origin: padding-box; + background-position: 0px -8px; + background-size: auto; + border-bottom-color: rgb(237, 237, 237); + border-bottom-style: solid; + border-bottom-width: 1px; + border-left-color: rgb(153, 153, 153); + border-left-style: none; + border-left-width: 0px; + border-right-color: rgb(153, 153, 153); + border-right-style: none; + border-right-width: 0px; + border-top-color: rgb(153, 153, 153); + border-top-style: none; + border-top-width: 0px; + color: white; + height: 13px; + padding-bottom: 10px; + padding-top: 10px; + background-repeat: repeat-x; + padding-left: 0; + padding-right: 0; +} + +#container { + max-width:1150px; + margin:0 auto; +} + +#header { + background:none; +} + +.dashboard-module { + background-color: white; +} + diff --git a/orchestra/static/orchestra/css/hide-inline-id.css b/orchestra/static/orchestra/css/hide-inline-id.css new file mode 100644 index 00000000..1e38d6f1 --- /dev/null +++ b/orchestra/static/orchestra/css/hide-inline-id.css @@ -0,0 +1,7 @@ +.inline-group .tabular td.original p { + visibility: hidden; +} + +.inline-group .tabular tr.has_original td { + padding-top: 0.8em; +} diff --git a/orchestra/static/orchestra/icons/Applications-internet.png b/orchestra/static/orchestra/icons/Applications-internet.png new file mode 100644 index 0000000000000000000000000000000000000000..e7237941aad541254e463401015f7f7148f055ef GIT binary patch literal 4646 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 literal 0 HcmV?d00001 diff --git a/orchestra/static/orchestra/icons/Applications-internet.svg b/orchestra/static/orchestra/icons/Applications-internet.svg new file mode 100644 index 00000000..e7849607 --- /dev/null +++ b/orchestra/static/orchestra/icons/Applications-internet.svg @@ -0,0 +1,520 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Internet Category + + + Jakub Steiner + + + + + Tuomas Kuosmanen + + + + http://jimmac.musichall.cz + + + internet + tools + applications + category + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/Applications-other.png b/orchestra/static/orchestra/icons/Applications-other.png new file mode 100644 index 0000000000000000000000000000000000000000..4c38a91ca0b4b4c86d600ddc8f558d8d8a0a8a14 GIT binary patch literal 2863 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Other applications + + + Jakub Steiner + + + http://jimmac.musichall.cz/ + + + category + applications + other + unspecified + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/Emblem-important.png b/orchestra/static/orchestra/icons/Emblem-important.png new file mode 100644 index 0000000000000000000000000000000000000000..d041dc94a471828fbfb19cc7221b5efc29572bc2 GIT binary patch literal 906 zcmV;519kj~P)Yf6u({^9;j?2%j^A&+7g^U_K$$N;tNLbt(abQ#u~* zze?_>0@%zc8cHy+rK9y&tgW?A5akBHqJ%|8qEWW++tz_cc3zYk0!0=W% z{?+o{!RAPPhiDWSuYi0qa*mT3d;H|r)xT$t&Kq!LZ^Mth@%a9bQ>r7^so9w&t9J5f z^6J;WO{|?OPXK^FHnNVTy*bWR;!8%tqEHWfd>>3} zg|dAF%C;ehU#y!A@ks9O?)vG-TGmh%aCWP-v%NLeLsn_F>RMo-Ca`cDz_?d}h|d>Y z6>mCS70}*Pzd<37yANa){(G?UTmx1S5Du!HRRPiF#wBEx`D(=h+2%Bi4*&yvk`g)4 z$GAHs_If^su6?a2Rs{OWI$#HaHo#Vdo&EnJOPzv zEUsGy>CZv=37CPF+}=wx&IP1j7alCGD}PhbbC@}%0N~#K6D-sWP?ck(3Mzt-<%@uj z${HsMEMW4?0YFX+K;3f(ObKyyH@JY(lf`RQ0Xqh1bnJQIB84M-gX@RjT>2SQF3uK3 z2`RTOnNH>QRRsXR-PeWwl&wBvK6zGx_xBYzqldZ0y4F&>KlV@m=0Wo21bjb8rF#!w zZJ!V$;}xyy*1r#?sCXN0%Z`ob4sJQ)+?X@>!_W1R1oJOz)J}G{YwI<)7^s;40^W3U zjDe|sSgPi|$g>AGAEh_$K1a`LHpBaEIh9~5+oGXejSGWcDiT`+Kytydx^5+3P8QB@ g8nBPoe9h^22bP{j9T!X1_W%F@07*qoM6N<$f=w%$8UO$Q literal 0 HcmV?d00001 diff --git a/orchestra/static/orchestra/icons/Emblem-important.svg b/orchestra/static/orchestra/icons/Emblem-important.svg new file mode 100644 index 00000000..6333216f --- /dev/null +++ b/orchestra/static/orchestra/icons/Emblem-important.svg @@ -0,0 +1,108 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/Face-monkey.png b/orchestra/static/orchestra/icons/Face-monkey.png new file mode 100644 index 0000000000000000000000000000000000000000..d0aec90c46e9124f84a9c7317f0080e759647e2c GIT binary patch literal 3908 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 ShYg4T0000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Ulisse Perusin + + + emote-monkey + emoticons monkey + + 18/05/2006 + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/Koala.png b/orchestra/static/orchestra/icons/Koala.png new file mode 100644 index 0000000000000000000000000000000000000000..165593c0fff01e0ec00cf50c75dd5c49d031613e GIT binary patch literal 4728 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Koala / OpenDylan + Feb. 2008 + + + F.Bellaiche <frederic.bellaiche@gmail.com> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/Mr-potato.png b/orchestra/static/orchestra/icons/Mr-potato.png new file mode 100644 index 0000000000000000000000000000000000000000..8b4ce4059c7e452ac46703ad69739f0e634a2f31 GIT binary patch literal 4811 zcmV;+5;X0JP)!2ZIZ*4v}LQ`)5000(r zMObu0a%Ew3X>V>IRB3Hx08L?eATls8H#G`GF8}}$%}GQ-RA_<4nR}F7Rei@ld!N^R z-+3pKnM@`L34s7X7ziez2@kD+r4-bbgo+hWS1nb`vQ5-#sjUf^RxLtTsVI*^>k><&Xz@smNxx51m3X)F1Ym4RTB9xsZVKLg2h5OX{pp z?MbCljbm;Tg&~nrXM9d;JqdW=R|qOTLQ2}tns@T_3t}p64~% zwl(ha>NKu1QKXdEwvAQ}N-4B*(2hdLQ$1J+@$Gkl=^YWk@Bz!R(AuD@e*bnYXxTQF zQrOA?AeG6GPG`m!u6IWQwE4tOf3fGGcLM2*F4WP{kq?FS=fGm%LZCjANu#wUiqvT{ z&*{V%gE0mvB?72R!-8ps8jeyL8bH8dC9O;TVZ+GV&P->tK#2WSFn>Jpn(qe)A!1i} z2jDFUO{tLPbeZXCpQf}=U0pqv?P1I`_8zD(JW`tF2I~IDgs1?EfY}DN13wb*Ppga> zK6%|UTHsxmT(VRn{|#t;yFi5i&xVW(Ej5~!T*Qn_K#f;HMG-+5qN+hJgd~-phO}(L zFygh{`%qeQ_Ew+Vz$wbvqu_TxY|O9DOh5?n7~Blp1YD(s=sS|j_U>Mko)ZY!RM*%{ zGLt8put?iJxuhYNut>YmHK-`~7ACBaPby~SHBl7OJ){Yu5XX|pa0FpaR1(s1NY_jQ z1l>KobRO*`TN2#fHI0>GQ2k_$a5L|Vc_zz4=(U`#%j zD$Z?4hScV>yzi=6IIaV#jELe4gAlyfmFKb#-p1_tizpX|`Q^`^WarPHz?OzZOK;@- z%Rhvd&U0+eFeqA3e&k z;o-c1ZvpQ+#RA_K!U|vuu-LP0&TedES~iQ)nunT>)16f;yYxcZ+h^if65|)JZ3|0E z4hTRMq~0nhH=PY^{&xc+8io`MObL;!aHi!-jvve~nlSyO}CnrC$Bbh@(& z&vj^RnT{nbh|1WOg>Bn7jswk?p8_7>ymw#E!gpVP%Jpo0E3+@T>BI$N@Jn%g*5q^8 zmZWoJfZp;b)Fn_$TCpvQdG+#lo0D^^^=>eXw;ck9)w*RW#66LFU&sAx1;`ipA<(j+|_PqX3mCg6z1N=o~2kP@m7CjX^~r zy1D}^6_*lW7{cHi@ghf$(cj;XZO7Sp)25%WY12=zZJYl7{_!~g$M?N(3ISs@TE$w< zq>@z(u@b}AyZKdbTwxga$CE8^KmY@!5-p>__<{p|%s89PqKzR6eNc)>2oP~?qohP1 zc$(~-E4byBRebGh>&Ks;HLDGP#~=UK@oTr-vWn0jW$%{9P9XrGA|Gklq^bmt4HxKo zW*2X4-AfSf;9FP04<-??;1La9esgFj-L~gAxpNx8vnYo#8QZZ?N}-|vp%kL(WJbqX zBMf`#-}YU;_{GnYOeWdBeLKy~&3yW!*Fjijc1s-xyZX8C!gq1!pM8dxfAA&B!#z_H z7_)%m#8qqW(LQ#*&^31ZKNWmrVhKFHkv%5F-vD0_LNITBD-Fx%^Jw?03{;9NUve?o zOd1^&5!#PiM9V^25-CJn02xEF>0D|ST#J*gqqP6WB!W(q3K@ygBwCjcm;`&bJj$`X zFHQ*_z)56Cq-zPnfE};z!|~1Nh0j&KDPY@5c=F`>eVo8uLdbdWmj=EBNS#ixeW`^K z5mzkh!1EHQ$`I0oacyr&EGZE}00J=MeS+iI%$au%j+X#qC=K_rYsWVFhDwxub;cgT zc9SHt4JfVn<&HNHmR)SU^3L`<@BDJ#RP)DonJ*ioR~mB{a9Qc13-9Y*eP>xqJ*He# zwS64xfx{po2ttA|Bn(4>ApQ-*GBx=e(vk=v5z@kTlhoJO;8_udGdDoS#8s$O?HSNI zt#Eg&`@40QoRPqoblv^cmcg4p^zl?fqw6G67()zNsVew*1Yrz52*$t%=wO(n49R5E zNO_`8wB01xbdtJ^O~y3{@z!0OF<<~UnI@CYG)ZH&+;h*`8%~}#iNN~x>(fXcsIPCZ zZoJ_pD_z}TD-}PsfHH*9n5M%MB*Fj_j*?5eG&IyA1PE!Jc!^~Ln!1`itQP!jwrSJ-l2aDh$Ur!&n453bEV-HMuNF_w)qB$prFsc%H|yrOTu-rZLjN z=T1w&lH#+oXV0OjG5*%%tN2B+)+3ZQQ)oXVSw9D(G?mg2#l9m%VVNkboKTEkBnm5p zE<=q*L|(h%gB8!;lx>80O*UHS0EnyuPo8V_jVodX6yK-$@jd2>l|3)OZWk4KuZOp5@r znHcm-TVFJ8O?w5lB!@>zG*1J|c7eE|?<*=E{K+r}dP3mG0BqkYxO!p0Rp(VWyHp|H z*NM>)qoX7A4VL)+3l*N(=`&g}@cKpo<}^9nc2$nrw1p6|`VJT#j;jEtc2@bNtuH3t z`<~wxW7v#kT7W~72p}0mL&f)rqKG4%M{x2j4+0+@Ef!NsYb+_T-6Wx3X@zoy|d2CB%O7LUSJFGy6cx_-{a2R6@!g8NSIv>yH@>(f{>&Dix z4fl;@BMG4KZ`a1GxN^mnw9Rb8^E~Fxoofjp95+$77T{=iFG_2KwD6KSZd{x}im3sO z$vF$o=A3gDoNlD}m8rQ~2;t0~JJ<3&kG7d@T)E;(v@t6* z{_QaCLe?jzPjAv^oi$HFTxIUwvs+>K>AEKlJo}mVrT#q%!yAwH4A3&Y0XLar@q&n% z^){P#l_?Yzeq_MO;pSR6|AGs+`s%B>_S(2@+^}IIn>KA?=Z+l&!(9*#?cTHd%6DCOktKxSth45^YtJs--QDv^;2Tu} zW=2Cp<9J3oeE1N>V$l*t+z%j>`l7L{>yCH#ddY-GT}_rmrjAz2;RE@hc-yQ>BIDCE z2in>_qRI%yC@z?3Q+u&tMV(J&R046!h!A+m9BwijyG~(*U=Z|<6EK>GJKyZQtLtbNEi+nBN+E+TBkWM&p{bY!};&t&>RC zl1SI$CbKwR8q4;^!G}@A@t%HO+jD@P-afQUFxdJbis=QQ{XgmW*rV$KA%qadn4qV( zmo3k4VdK*qG(zk$O56(*`hm4;*EUMqd;|EPF~-s8N51eEU%GA4AKZJkldaoXTa#wt zf;J(gz?6G1FWrqf_KT`33zDfMp6ihCTwKS-a~-^D+VjHz-wz4G2;UFzgOF0GJl+@h z`Ewa+zJbsr2%{2%gGEZIbJ04waocx4_SE0r{nrml4C8CsHYzljypTF<9`?`P#P=GNLKu`4t zhaip(2k?y6g9dX->1d25^2>ykB4JP=5;?-;Y(m+DZ_Q&M8lfZ;$l7`2^I1KAR=pG= zLi;{ZSVo5xOcbG26#q?YRP{JSYlV;wVARpqo+?$b=<1l{GE;rH93x->w(?7_3=Ngk zXz@gOY_uk-6bUP%F@Z{vpgc-Y9%Z1fkFH~VrX=f4C3GGiJoxhZp`j7A_0@f*v$LBZ z@-ZsHC>4X&Ax4F06;(5sLPa4JcZN19++FHBVyakV7270v%Rl)B_Wk19P3K+pTibUY znAYCbBx~|%ToDoaMWRZHN@;}Qp#eq;1x80ljdtsVPSwA0eD`zr9^d}det_E9m%Q4# zc;#)!v{})8yiaG7$do5ZIwl6J6-GrwVL+uEQF7Xh_8LpWov*EZD|}PMfUFYp#zy_Z ztD0vmUU|Q?ybg4PB+@4GQBg$-V?as}P9@UjrDFH~jr%rz^Ji6UjcLpPrfta|F07f= z@q2C}z0Bw!OIXIJA_9yhEtK?xNHzlIQ1|XL#F?-o<=cO9wTqLEF z5mF|Ev_0jI?iuRZ_1~Ra9y&lAu;GL;We}f|MkiQdEDo#*$7A;<0z4V;RNzl0F`3Zh l^RYlx)nhf`c#`%3{s&PlIRvN?`>p^0002ovPDHLkV1n-h43q!> literal 0 HcmV?d00001 diff --git a/orchestra/static/orchestra/icons/Mr-potato.svg b/orchestra/static/orchestra/icons/Mr-potato.svg new file mode 100644 index 00000000..85c5b744 --- /dev/null +++ b/orchestra/static/orchestra/icons/Mr-potato.svg @@ -0,0 +1,4273 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Mr. Potato + May 2007 + + + F.Bellaiche <frederic.bellaiche@gmail.com> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/Text-x-boo.svg b/orchestra/static/orchestra/icons/Text-x-boo.svg new file mode 100644 index 00000000..887265ac --- /dev/null +++ b/orchestra/static/orchestra/icons/Text-x-boo.svg @@ -0,0 +1,997 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Boo Source Code + + + boo + source code + file + + + + + + Vinicius Depizzol + + + + + + Paper sheet by Jakub Steiner <http://jimmac.musichall.cz> + + + 2007-10-30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/Text-x-script.svg b/orchestra/static/orchestra/icons/Text-x-script.svg new file mode 100644 index 00000000..9784f053 --- /dev/null +++ b/orchestra/static/orchestra/icons/Text-x-script.svg @@ -0,0 +1,419 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Generic Script + + + text + plaintext + regular + script + shell + bash + python + perl + php + ruby + + + + + + Jakub Steiner + + + http://jimmac.musichall.cz + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/Ticket_star.png b/orchestra/static/orchestra/icons/Ticket_star.png new file mode 100644 index 0000000000000000000000000000000000000000..b975d5975fbd19b103d088bc6b2d959fc22b3181 GIT binary patch literal 4230 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/TuxBox.png b/orchestra/static/orchestra/icons/TuxBox.png new file mode 100644 index 0000000000000000000000000000000000000000..72eb7a0ff238e628fb65baf450b853f65a78ac9a GIT binary patch literal 3490 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 Q`~Uy|07*qoM6N<$f+#q@(*OVf literal 0 HcmV?d00001 diff --git a/orchestra/static/orchestra/icons/TuxBox.svg b/orchestra/static/orchestra/icons/TuxBox.svg new file mode 100644 index 00000000..6aa10910 --- /dev/null +++ b/orchestra/static/orchestra/icons/TuxBox.svg @@ -0,0 +1,4661 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/X-office-address-book.png b/orchestra/static/orchestra/icons/X-office-address-book.png new file mode 100644 index 0000000000000000000000000000000000000000..2225059244ba0d9d930017462becbcda7fcca14d GIT binary patch literal 3193 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 literal 0 HcmV?d00001 diff --git a/orchestra/static/orchestra/icons/X-office-address-book.svg b/orchestra/static/orchestra/icons/X-office-address-book.svg new file mode 100644 index 00000000..0b4a7f79 --- /dev/null +++ b/orchestra/static/orchestra/icons/X-office-address-book.svg @@ -0,0 +1,301 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Addess Book + + + + Jakub Steiner + + + http://jimmac.musichall.cz + + + address + contact + book + office + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/apps.png b/orchestra/static/orchestra/icons/apps.png new file mode 100644 index 0000000000000000000000000000000000000000..85a372f628fc6b16165912fe3c054eca0a3d42d2 GIT binary patch literal 2122 zcmV-Q2(|Z#P)LOc{LDi?c)hJ&D*|uq|HaR2OaCyHNo8Y?zQ&BwRvm9)LYs-|XpmXp7Tf{| z_iTRT>65Lr?8XQC_I=%2X+E~Ie`qjkS=Kx|4WTUuP&&G23LwI9##{%>0!-3m+(O~U z0JgO3tMS2dcm4ekvY-@*xQkFErbdVaEGiNr0U7Ep)GW4bD^n_c<~xa0r00rl{x~4e!X+Rr07ewols;t zci1VSwEzk=1V}(8MhLa(#YrUl{9^tov>;FyLIvCXhUGYh1ua9y7>cA*C;yZJ)TLtn+W>x2 z_q=lP*I#{WlVz)0EhiauHm|;Fl!X<5Y6fFM%n)kdqn*ko-MQ)S0P3eeH77>|9LL#~ zSu*T6i8PdDL2Db@wxP5QUAea`DAkm>Le%8_RCWM%A{E_d`j^0zyf)3kb|M@K6NC&z z2*LzrD%TN)pPx;=^vu@JJ+Ko|VT%Y1j;vkZKX{9i&SuL-HI!E2wbD=uP)eXw_nAy) z95~5zbgyX+2Bu>h5dnnDe-%VD1PTx<`fcgRN8q{s>e_Y@0fs=BA#AFS>>=|3fR*fr zQq>SDxj6*xT=SskX_uLr1{Dzy;V@rwrx})(mrYtkkQzJ84C-h%9+(A<<)rEXCa_R% z1qbzfNCZ~R-lzM-#Oy;D?L9yOq56(`%y@8bT!mkljqXj!Z9`xlJqW<%`cR9SHNYT| z1#xxB%f}wne84Ssv~?b6c6EW| z)jZTxn<4@ft$}zm6Fa&Z6nzt5#lTW*{qjSQ7?n~%XW*lD!K z^})h`0$4oc{WD$lQ_ea75*AwF4j$m@zJFoS5MInJUSwlfkuXBqdVobjiMoOj42f4MoDuW?K)Cv?^rr2NqY2gv-N^OCZz?*8rh$Hy05I z0%Io|2`oqZ2buN4nug232~bzm`e5|tCWxFIB!fD-``shsPklu~ICae*pJ%}&@ zHVBBiieUl?!@pUfYj(p+K^&fp=&)us5z*gZggG0oYaP4MpVFKuw{WeO3k47I>@V2v5Z z`%7|oC^uB0#HPGm{5gNiwE{DUTquBiM4aj@vTWGaMo~8gy0RYm8mAE|Z%lL(h*A-za5H4Z{ zV1W1pn&0d?4`_q85+^U7!>VP2STVQ^Zs~Gt0u?w`KV09z!4t>Kxv|-82X_Da1^{RY zKb-_h*sDES=_Ju9S|rj@EY{fQuEwixjlnNDP?D)TDDEaq8nzzB!Q-dFSLAOmKX>*- zg;*toJl8kpN}m6Dpj5&ld=*OoSSs1%tEWa2_kHpXtA#0B^>o|I0T z=K=VQ1l)sfO#I=F4a@(omCoqizASZxXi?;tr_Y?9TzmVi%dNb33Ejdkl^Dj=*#gEc zOcvyn_0%gz&K^U#1_0%ceC1R+gz!jtQNK#w{qVYxyY659+P!yf>hDVj@JiEdb!{sR z$6A2{$H%;>cdmbP|E}@F0DKX-9_avph_F&B_hAtD#|sy8w|#bS+mV01^OyTRu_*)O zV90IKQ_4anIgBIY=Zl_K+P#0*_$vtA<=5)frcuAq$Rc9@jy;?2Jw9<^>!)v9lktNo zNW&>bHnj{B?_LXLayR}pvvS>!M2@vAP(8fdEex(K&F{Q+JwJKzy%|baLv>llQhk^! z5XLXP=MnSQ_HNnRwz}Rl0(-ZNl>&ta$0x6P1&?6qEDRAuYyerDn3yUm!lS=^^4O(L zc5WI00NC^Rn-dJ#e)8Ovf>K!kHWKy#MlW7*12WG)yZy}34W A6951J literal 0 HcmV?d00001 diff --git a/orchestra/static/orchestra/icons/apps.svg b/orchestra/static/orchestra/icons/apps.svg new file mode 100644 index 00000000..741e1198 --- /dev/null +++ b/orchestra/static/orchestra/icons/apps.svg @@ -0,0 +1,518 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/auth.svg b/orchestra/static/orchestra/icons/auth.svg new file mode 100644 index 00000000..ebd09c56 --- /dev/null +++ b/orchestra/static/orchestra/icons/auth.svg @@ -0,0 +1,2449 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/bill.svg b/orchestra/static/orchestra/icons/bill.svg new file mode 100644 index 00000000..2eafda24 --- /dev/null +++ b/orchestra/static/orchestra/icons/bill.svg @@ -0,0 +1,1348 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/contact.png b/orchestra/static/orchestra/icons/contact.png new file mode 100644 index 0000000000000000000000000000000000000000..619edf79e2b31b1e98dd3c117c0eae71db89746a GIT binary patch literal 2729 zcmX9=3p|r;8-HeEB%39MB{Z|Vl2c4M%qiRCe8_TUyc9WXSkB8KL?zPiL%b=;CQNTq z3zMP zZGW^6gzS7YkAJE;*G1D(2IH)Fz$4ml2U~r%k z_sYPYs;8iJ{CKdTp`qFRqEoa&;{PzbI;fQ-SloAErK`{5#CCf-D!7=xDzx7OC@o-smNWmHta5 z{6|L}UNl0OrPbq~7yG3x)vs>fd{X!^0wTlE#@RFF|@u zz~h8M(BM0&OH}Prh8@SK`(#g}bx-5ah2#EqaI|RxUH488X?y6zhGpx`o#n;|uTz(Q z%$#|=FMJ*Xpn16gsKfio{w`*KWbJ+DJT@U))Y;yFfuka z;yjCIwM!edf?{J2tuRIt*7@h$O(-dCGHx>v0EI#|d|Ozki4b~vdZyuW5jHZnFscPa z6}fBe3+r`LN*K7Csy~%#t(GytV?sT>ygL5VCh-iE=c3Lj(t?}{F z-atBC7R*o9_)>kz+b5Ew>cJh5bn`VeHTv~!zy8Zb0tm~jEA12Tj>!fwpg0)9d$@jL z4(;mdijSFrDJm+CMs;g!)FyLyQ@#jMJslmVwuXjfFJOth)V7y$w-UF@jr%?z)e)5= zd1XQw{$qdqpOsZr>gZE+*u$`?mFcd<;a=EDLqkhMM!nXDkzK2MP7~jsjbAFjRdLX$ie_No)?y@qB-PJ2FC{$Hl#vo?gKR za!_$gV=XlZjar4EE3Z+)Di(`MDONh=6W#KYyE&E!heDwsqIg%2 zq+}n|aF`f+?1fL+!FP6c`j=pbhK6)ZP4km~ZcKi>Z;L{swJxG_TogTI-xT)DPB2s| z7*9o{I(VNyE4sPcfEw|0REBWu&5s{?XKZc3GJW9lGf&4a5@c=i^7ENA>A7Y-L&L0< zmFTVA6tr>7gV#h}VaXQv7AEIm*sG@R$bnLcxBYN;cL+eE(LBAq>0QB3{=X}RRFat=N!sM(O_hRY;&wF|4 z>g!LN!9_*tYz{+!iS>klk`T(PSy4Wpe><`Nkj*4A%~4)Xu2o(#^TEzQ19GM!9gD?E zzJ>C3c1-9#ws>@07p?Ce>q7fO&pLTgh;RL~39oG${^r@io%xxWnYU9@ffsFUZG%)0 z!vkuwAX~8-J$uLWPoB)YdpiDw2Q@DsEUX42o|_|*#$wfp8%-a*YfKA~;g#=~*WSV1 zChv;zHla)a+;*~+71TueMk4+X@xIFzqjwT3-ccpZ7K~7Js5i!{=FYKIUU7cmMNyvg=N)d)ct{(jF{9ttf5i{E1^J`|C`;^5(*hJgC z;;%6Yz_ZA4hqI23sns)=)<&PxZU8&8VI8HBt;alz`L`XacY!2_)$OD<@ekk5dcRZ> zyohi3GOOKJVcrj%HILBN_I=4vCa?ogL<|xXx)lcco)e7CW`p_9wpI%b4JD``2nLFo z<4d*}B$LHjPH6F@P%LOP{egJ{5hj4E%I=!}5PO*8DFu=}`%PeIM1+lrvTbs*X}B<} zu$pklW;X;hX-?eK#%gFp&kv}-4%AIr`)CU~Y^Kj6JG;8tjk`1|lCDtoa@gnUr$AbO zo||K4CBJ_C8nyb>9#p>d^;i(s0U;q`=jwfbqu%V-RuHC;G}@Z1R6u+{% zH0eD8I2Josf2LhOoTm%&!NwVpe@4((Zpz(xFH-sOpUlb0Nk3^Rsn9-$(i<20Jor2v zsl4mgLxu!`J(ydxQ3>2K8~7_0{6leX96r6l_SeA^V9u<&)!mW$!X_iotZV5DH$ECH zyezLubqp^#`2jpu0wfy;>pDySEB^z%*ZlGT literal 0 HcmV?d00001 diff --git a/orchestra/static/orchestra/icons/contact.svg b/orchestra/static/orchestra/icons/contact.svg new file mode 100644 index 00000000..39162b9e --- /dev/null +++ b/orchestra/static/orchestra/icons/contact.svg @@ -0,0 +1,2810 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/daemon.png b/orchestra/static/orchestra/icons/daemon.png new file mode 100644 index 0000000000000000000000000000000000000000..c65a62f51a8ebb3a843b8fb0275ec12d031a5932 GIT binary patch literal 3252 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/database.png b/orchestra/static/orchestra/icons/database.png new file mode 100644 index 0000000000000000000000000000000000000000..bba97076297a71c77f235772cd717ba72602b1b3 GIT binary patch literal 3039 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 literal 0 HcmV?d00001 diff --git a/orchestra/static/orchestra/icons/database.svg b/orchestra/static/orchestra/icons/database.svg new file mode 100644 index 00000000..d0d20658 --- /dev/null +++ b/orchestra/static/orchestra/icons/database.svg @@ -0,0 +1,401 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/dns.png b/orchestra/static/orchestra/icons/dns.png new file mode 100644 index 0000000000000000000000000000000000000000..9be09aabc46e797b8de708f7f661453760144f04 GIT binary patch literal 4206 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 Eg2}%NY5)KL literal 0 HcmV?d00001 diff --git a/orchestra/static/orchestra/icons/dns.svg b/orchestra/static/orchestra/icons/dns.svg new file mode 100644 index 00000000..9234b23e --- /dev/null +++ b/orchestra/static/orchestra/icons/dns.svg @@ -0,0 +1,3045 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/domain.png b/orchestra/static/orchestra/icons/domain.png new file mode 100644 index 0000000000000000000000000000000000000000..c4aa11dcaf413cfc99a70c0e95d50d7f91a8341f GIT binary patch literal 4559 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 literal 0 HcmV?d00001 diff --git a/orchestra/static/orchestra/icons/domain.svg b/orchestra/static/orchestra/icons/domain.svg new file mode 100644 index 00000000..eaa48b97 --- /dev/null +++ b/orchestra/static/orchestra/icons/domain.svg @@ -0,0 +1,3045 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/email-alter.png b/orchestra/static/orchestra/icons/email-alter.png new file mode 100644 index 0000000000000000000000000000000000000000..9d68dd46d07b33d9385e28646a9ce27257766bae GIT binary patch literal 2892 zcmV-S3$yfzP)6+ww7hyDfI@V&M7b)5!*Fm$bwxP^E|WOoj1Awf4bNQ@Xj}n^?Wh5mGxQh0dp?Ap0yZCh_&4qLQTqF$d*JB0@}TMMO9$=kPY-&6$P z%(tI^w0-{48@6r)mY*>i%4M-|ZPBmvYzgaMbZ%DIS1QuAq%<`P${67XhR_PjnV8Tk zUK$#I`Xe`X^@=Sldxf(@Ul^v0?*DX&x|jvZh@V`vW1c0201O5U_@NfTlF>UYRd!w1 zdj0j+|8C#Dec!YJOu6!nSa+{T_TCSHkI@=cE@L(2Y5nX0SJ<{&GjQheB9(=C9_>zG zOG}7l1Pi4Sy;~Z1xH}iGcfN4+W&DOloZs0C%d?HvgvBDMeNT$E?%rhNe5g4*G)2c| zMar?!7_@;vDg01zVAmGeoOY9yN`=9}!9ND70F>5_27;@T6wjT3M3UIHZW{JKg%$$T zN(H?=Hpkf2BH%4+1VX^>t}GATn?cph#TdMk11N8CRfz~GX?*q%vG46(HNRL6Idx$U zr47b_HUYrvMR8wk{CJaJUCP%qk2EZ6|$A{MXhGC3BDTU{GlqSYm_|Jc% z@zo!uVVOpi0sp#()z(haXP(0MeLT;Lkl#t?*XjU*AYgue9;r1mfBRR6P@%#quh;

      >@Q=OwVp|3DO zKcfP!WI{h<7f`l1bQf=F`;c6Nv;J9UTl057X1rLpq&~QuynDsGlRHL`uoI zbLY@n)6>%vu~6iT(v%mkQ__Oj-}{4ks4Jl}<3;Px$&0-G{_MI;5R1j=>FMG8`SZ-p z&EYss^eOT1jyYDy-N+|{g254(*qphtia{dM% z9EYu6{Te|KME51YVp->;WrF}?ZXP`$1h#F{*Vo77xz*Od@xT{Xgoq+AGrWm)w0_EIPm7#tkLah#hPURy5pCFyw{ zhYug-fd?KSm&@Ij^R=v3TM47kMxoX*wqE3Q#;$Il`VjduV8AxaFE!JKno8&%hPc5Ul~N6oDV2gMj9AX4T5SSzQ}rux*>I zTeo6a7B9Z|BF{YY47P1Yz0_KfQj$z285tR2XlRK3{(fB7#kTELHg6@TKA7e^KN;cj zOqg6LA*GF1Eb!pAO?>kHwwtEDrc+8q_iWoHm&=jQ=Q()rAmwtIcs#yt){@C2Z@u*v zZ@&2^yLa!#aU5LN-Ei3~l>`3zg%enbhKOt=z`c#;{G;2_!BY?2dqXFvltL+0ZDW_O z*LH~~Po5;7&(q)E&!IzySXfvfkw`?DvMh^OEXMKU$0-yF^!N93_Uu{a=H@o+yfKE7 zA0Pw>%fV>10wCx3lRY^Z)*QW=vuj5fh71f0(AU>TUtb@wSd6`U_j2smF{Y-bNF)+C zj)QI696frJcsx#5R~NZljwhddk~3${FgZDSTOf>4gi5alP%IY18lP|UMM^0K2M6iu z>Y}%|7tixZCX=|X%g&uU85$bm!i5WHt$FRW*Jy2RC7;ieOeP~Xd;00885tR2YHI2> z0BAz3(90#Xs+Fjxxm$_Wn$xFG)6vnv_U+s8JTH1KnXG=|Zr{G0OP4Nj?%X+gdwa1g zi-v}Vh^fluGO<{U{rmTG{P=MIHgDd1OS{Gxvj!r8=bzt&B`{G2oSzNt))UiK-Z9ma zfOX2q7{kSj7s+O`bar-9EEYes++5d<%HmdYE0qe4k3GAJ~x17K!mhEys=OG^u-QtAKP`I-blfNk3m0Z1uHr_(pgt0$Z4 zPHaGFjZ!NasEzsgc@l{P`Fx&oxqMqx`>Tu~sDAI(TJ>6~){jevUcvg_@m!PvqhYC{ znUgUd-1&(pJ;h=X*L7)ZY@}Q+|HdM}R%#tUKA-2>wQDRbEfJ4bPb}a6?;rC1e;h&w zffE5x8s^HH-GA{%v^=&mVh6`@NG6jJQ`|{WN@0v4o6S-z7ONjl#S&-#`33GvYKBeq zpxXurc6ASK`Rtx&+xPE@*XYt$hL>J{hsn7`v|jD}mp}I~ZB3a~y2AKXhX3^?bP%l4 zOUvS+zx)c8>#WkB{$QLVCr54wOj9PsR}MTDfzVo$OeXoo;bVO8KQEJVBw-k!j9mvH z>6T?%v#yoSQYGM9|Nb(UrV9jEAOr%?VFg?JeDR6jUbP_=CZ;*|wQrMN@^Dx=u~<+x zWU~ZIC4{~5Yv9x`e#ze)d=>4csUEFYkI;2_k?yu8l+uX0KLaB#{D}Ae>j;^`EGen* zjG%U;U;}ubH#jynCOpq87-Pf_PEW?iW|u6-je!7B`)3gP9C)zRA9-g`kJLr&`aga@ zX1pb#91EyAY7B&~YZ|_|&p-CY&!aGx11Vopkg^@MmvNW!j{ZsSU#Nwc<#C9cDSzlP!I(1sU_3tg`y2(Bw&oG z7F^Q!(`i|mV~u2|i}yGclUl34-W0oBNjk-~`e~EuHON(eYcQB*QTV_TCaOUxrLuhE zY?>-|3xT$U6 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/email.png b/orchestra/static/orchestra/icons/email.png new file mode 100644 index 0000000000000000000000000000000000000000..013e7c5d326a1d626cdfd7179d0691d5807df572 GIT binary patch literal 2646 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/extrafield.png b/orchestra/static/orchestra/icons/extrafield.png new file mode 100644 index 0000000000000000000000000000000000000000..2f11d1eefa80ae5e5c8d451608f8bc3ee9c7d6d6 GIT binary patch literal 2512 zcmV;>2`~1EP)esLIB&CU`PT45}Kz5RZ7wd zMXJT9J)ed=4PzEtW%)kKY2b+3f_h7@HbO}Z#e8mbZ?h2R7**fSo_ zK6gD|AMWCrJL7~!2Kq_H_nz;Zd%pGmedib@BK$v-ak~Y$S+W40_|_8>S;su8mCoF5 zfku%bhW0G|`mx^}yOidsXP$ZLX=R$fC>4u z##(GFI-IR+M2o@B^`Q)N^^F)xvu9J4TDQPrr%a(xAY)}%w=Q=VFqbF*V45b`Z1#o~ zxDhH?4&QyOMX@x_vA1h1whMgc(a;btyw+h;smGHK=jnA?{N&X#nM?*y;u+=u@K_>( zVHnYbTSv+O$JOL=4xVJus(>Ff0nlm#zGwo^#&H9bnZqy)lmaUV)PMpIhJaFP#5QY3 zzGQjvue&H`ZK}O>0Q~F?3xJkaqUNmQg*WT8y#l^u5fMZX-%NSnb}0bh>O4z!o_OoF zKL(Jz*PIQ>dz@#6XACJIBEr$5N12?Qyfui48-n zviJOHlevT3}`{<|kRclj#w)dtyYYyb&26^SaUBtO#6PsVgq zdZmvCgxmMc)zN&xwmo)lzmMHJwqQht_l@BkNIp<+w!z9#p4b9}vA5J{A|f<9Jsjzf zD;1K(!)dRQ(E)p2L!Fb&-bV<)o66XN*y>o%6S73 z!F61gmYP%=^?_{%tSo|#Js~TCgkFk0As{*#lh`ZiIe>3>*;d*`T&nc;9YBgxMWiu^k)7bw?UD35kBe`*|uguYZQGYmr*K0A*Dum0EDR0ox8Nim8CkRQW1b& z&*n;XnS--aoV&C@wb>$Hu=;i)M3*EOVg!kqeW=tQIuapIj6nU!6h|VOZl}Z4=Do~L zZcRFcArAneP-v}bce`jKLw0;40CuoUqaC79*S4{B7b}w=w%98FhL7=n{rjfQO)@+~ z0o~>jonCJx2N>-Et&~DU@I9AGWr96*tS@9E6mqCln^cx#6p9fgMjAb6$^PHb z^AIA9T0?|TB8cm9S8-+~)D);R3dIjvYYxorL;*b~L>g;sjQz9W2kxDj#8e6|(1XTH z#qnq5!u8Ar$XF)#%uFUQYA6Gpt^=TwN^F`Y2lwt8xp6+5;r=}{pSj^$QG!AS7(iiVn`bf*(f zQpvz5-?cpl+jf!=GL(`b4@gu8wAN@ppt@M0-e^P$xNIyJ!am9MXx4hvm)nUb{ixE9 zJc3TV5@LSe+!%QhRML^~dS!j|E$#Vi$xkPtC1DB-dO(3zgfO~)_Y}E|h3$sTT*g|* zo}JsHK53FtK8F`o=>hv*XKat+iD=GTIf1XeosWEr;>3)Wf;pH3YAf6*aHeQ5emJ1_D`I?`UbPx?*?GL za)pl<|HTuBpX1l>`~btUnf}7`nkdk9!EOflfuEeoLj??c?S%@U&`PHcG<}7vYq!}{ zwzy}Dl$uY zV3k+l)Leyg^KHt-!T{&+p1t_Kho4eFYma+&%&^@0XR$=^d`WEz0IJR9yrmEwSK`3e zbh+W;@b^vz>CskARqt>65$O0(1Bkp~{)$brMj*&rs@@1#5co^SK*$G;9? z^(z1vhQ*Km@KVQ;u*N3~`~ae66wg~}S1mJxA1GY!=C2?F4SwL$>dn)2u72KZ78@T7 zjczF2ixw@GlCT#N5%z9>h?8?~a{A-Y1Eyi}_~EYtuxslfKK%4=)EeGRE6{XI0`24b z{y?}~4|%=|0#rsmLK>(|733NKrWXro;`bZtz`#b zzO~4i)@k1R=={L=zUl3JaqrB4SCkTLx5w{Ze}j(gg3>k-GP{0|v$U^%TW{4jzW4sS z-xHDXTcQENxb>xhSab>t+4)kf;O=tNAdCu*=TK|68C$ylnfAoL|IxS1R(HyOHF)&7 z@>AO;CSLl=1AE81PBj@<%Ak=mR_N-zy~)kSxb z0upGA?+2Jhv2R%6diJV|?j!}=dZ(NF%c=8?G(*Dcy|Gb??j!|{d%pkd)#YYx75?0) a!T$j-jl<9YT2t=;0000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/gnome-terminal.png b/orchestra/static/orchestra/icons/gnome-terminal.png new file mode 100644 index 0000000000000000000000000000000000000000..33ddd945ea4b16bc84aec50b47ca493274997360 GIT binary patch literal 1690 zcmV;L24(q)P)(^b8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H11{X<0K~!jg?U_Gl6iXb(KeKz6%fCj`2nQkvY9-j(2$qVdMcxAu z;el7!q_VcK@L?%fh|Lor%}Ze+@6=jICkZ@3h)2*qOQA+3$CLe`il}&48km zT0C>+%mSc=)|$tUAOCXr@ZmoW9y~ZHgcvoxzM{2eb#=A<=FOYm&Ye5=X9XY(!;|%T zeX`Tp88tqSXxv;|uh%EvzI}TVNC`+ONs@#p8a;s`SQmUc4#N;BCD8Q&r4&jjq9_KS z+5C&wum9n{vL>xHNs^$o_V$xRbM)v@E?&F{XvW8DD5Y`&=>zFP04pmigkeaMBnTl2 z4d^*%|7Opbbqy#Rv}yKl29s$3FJJ!6`Sa%s&pHIsX2wDYTCLVl1bpD_U3P&fr3z!; zoKlL($;s}#LTj>nG6GU2nHKCFuwQIM2+?;=Yn_b^3A4lIIw)|{kK?#I)}(%*k^EPxDxfLg7_&Q9*=HW42Q8@SuIf!gynaMM>x5yx@1sp(oJAJ8RRH9*2J zE&K41d?xN_wR z$B!Qe;QICJJb3VcSFc_b0NZBQKceK|ZQw2myI-kPh+~;W9^1Ktl#$s9vS?&$ zynp{5r4+SVjYgxv>C>l4k_0IwQc9vIDqN+M#Bp5co3Xjap0oSzvuTS98)3)<`HuN_ zO>52i`Z_mm++b;GDch~HXU}r!(j|lt1rX*MMNtOI^nKuWYqmymOpXTZJm71_N$=db z!@|M>Ql{!WapDAXb8}2jPZwV510O|EK}-EWo65j7yL^MS&xBN#3tXEuFRfH6Jbn6< z7cX99YOL4mT)1$-3&J%IL!=i8|8AE9K$56(Cbsi{F%v0eA?r;|O>ynoHI5xSmM!q) z$rI-1=c(0d*^6yQ_C*;RlpT(HWo-L7*NF>J$V|p@Ob`Ubaqc*W4jrP=Xs~bJK9o{O zDY<|DK8uTsjE#*EMNzi68xGwv!Pno7L^0ZVN{QTrAdV$LATm&=PMuiU${I+@P8>x$m=oETFPns6NEAg`De>&tGn&mN6B82|dmH@o=g+-> zjm*u>vAn$87kDocCieIAf%HIA^m4mQFeECKiWj^Pg3Zm%z63g*PRTz8C1How9IrQw zmJb_tX>0>_`;A5;1K{(C4<9~w9efZHILqTLHxk(+r?fw z_7EuASV9m4B`0+cD)zh&*j|aaBy26D_?$4BX1k&Z8P-1VZqykT_^>35z2_Rqu{wzi z%-Ec$h6TTy8SR!2d?bP(a5AxDLTPoGJ6L-5?Cr!p@WYx>KR<9uq#6)~AW--&WHcZ| z(0!=C6T2_ms~HWNon2onMIn<1!q9w0$PW#@qE0U|dyzq+k`O_`2RwN|Wwng~?{i`s zybt(Zd|)>cX;dsHwgLeu6+sYoZzzVXLG8aEz+pk#<8*ytVQjq4RDPRhi0LFnTsfh_j_jhapXg~rn&;hoAme#rj76Bt74ILpV_>h#ys5GHT kfh$rHN;`l{1b#I67f>A%Hj>g1s{jB107*qoM6N<$f*oE38vp + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/hal.png b/orchestra/static/orchestra/icons/hal.png new file mode 100644 index 0000000000000000000000000000000000000000..94915222ab5b13d63a1c2d9b24c090d76bdad570 GIT binary patch literal 4052 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 literal 0 HcmV?d00001 diff --git a/orchestra/static/orchestra/icons/hal.svg b/orchestra/static/orchestra/icons/hal.svg new file mode 100644 index 00000000..185e65f0 --- /dev/null +++ b/orchestra/static/orchestra/icons/hal.svg @@ -0,0 +1,1002 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/job.svg b/orchestra/static/orchestra/icons/job.svg new file mode 100644 index 00000000..9d09e63d --- /dev/null +++ b/orchestra/static/orchestra/icons/job.svg @@ -0,0 +1,3386 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/monitor.png b/orchestra/static/orchestra/icons/monitor.png new file mode 100644 index 0000000000000000000000000000000000000000..060bc32891057bc92892efb9253c9cf118ef0d20 GIT binary patch literal 2591 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 B=iC4Q literal 0 HcmV?d00001 diff --git a/orchestra/static/orchestra/icons/monitor.svg b/orchestra/static/orchestra/icons/monitor.svg new file mode 100644 index 00000000..a3974a6e --- /dev/null +++ b/orchestra/static/orchestra/icons/monitor.svg @@ -0,0 +1,3134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/mysql.png b/orchestra/static/orchestra/icons/mysql.png new file mode 100644 index 0000000000000000000000000000000000000000..37b93dcb1d7419c91a0ab9fe8319c756e7fab1b5 GIT binary patch literal 3221 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 Hu0mjfyo2f( literal 0 HcmV?d00001 diff --git a/orchestra/static/orchestra/icons/mysql.svg b/orchestra/static/orchestra/icons/mysql.svg new file mode 100644 index 00000000..61fd6971 --- /dev/null +++ b/orchestra/static/orchestra/icons/mysql.svg @@ -0,0 +1,1686 @@ + + + + + + + + image/svg+xml + + MySQL Server + Aug 2007 + + + F.Bellaiche <frederic.bellaiche@gmail.com> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/order.png b/orchestra/static/orchestra/icons/order.png new file mode 100644 index 0000000000000000000000000000000000000000..ead6336195e29e105197105527ecaeeb5bcda382 GIT binary patch literal 3506 zcmV;j4NdZiP)%@3F`Bcx=axv11aFkOz;1B)p1R zig@p;ER;%JsJ7Cs)S{}|QdaFB-9OqtkX9xAV^J%rszg<{XqVLj3oHr=3G9ZHngqz3 zY#xq7NbGso9?$FEJNMkvKPEH2cRXVUsvhac=l<^R{Lb(Dd!2LcSSY2aw^v_%_1i*- zU7=9OAVk~6z0dXWwftR< z*6O{KlJW6zsgzp(#1l`PsjqAJ&516wNGzRtK^TNXe&t5hmw_P+P-B6_J*qFgTfXi6!v+3cc)DwPVQ zQfZNlxw*MTyk8qY-2=_twyT^xb;9Q%r4*KBVcYg1;DthAQH8Q?8_Tj5(MzS0Pi}K- z=3h1knwgYR4b;taDP<#Hym8uG2A88RrF1jBncrw;@b>1i*{*)K0_{!Wc08?&vedX$ z2(<;!Y7x^k8_z-r9LGTj(V*8CQA%N&Cc3US=2oj!T-Wu5(L#vY=(^sZX_|(nX)PmR zNdR?*c02CW)D%L9247uRu~_seyw6thJ5sFHj5B~o}M0`jC1GCF)}jZv*OIm436W_+uQ4tdEvqZ1_lOv-f9W)R-Ry>soVvm~(Qzt@xl z?U1)}pX<7fobb}x@q1;LDmI0(jg&S1<`DYY0G8TRGMS7Ilx0~!t->^e^(L|Fx{VF5 zTCF04;J07o>FW*?)F7A3`OaUFNQ7uKx@@^_>w#951~3exAy5cGwOR!r7!3Lro1dR2 z5D574LrRI1l5jZ8#pwd3qv%g|lFQ`?1Oh$})aQMz9B75I{#-9@^=6tT7pIHdl@57};!=cs!0_7|SO1HHT0;4>Z$r^Cr*z z^8u2HD9=2;g}ZMa_AS;dLn+0FM=lXCH2Qm@K1IF!4aHrTYPE(4rV9rI;_->=~cnna6KkRH5oQFg5vWa#ucPc50lN z^l_?InRsG=p8i{L3t=5;4#TsbeI*an7qV@;K|65tOD3k~`TienCe_=+jtwjL<$<&O z;KhBc9g6c8Pxi2)H{Qq>DJ8!=Fv04f1l{pKBY%`qIF5sD=J{mbUsB1R;f5PGasBqy zr26k98jUfN%aEC#CLFw^pFMf_7ccy^{#Vcbqx>;6UFu(W;f0g8-+udg@1-7U=l(a} zex1ASsC^J9n^k`B(qX>)@ESIZCVbYcSPpM}^dbHycKzK$sA{=otON9PhIsCeZ{ysh)%@!lM@i3CcyRlsw&(v)=SDu*{Sshv z{n}gT=m>!_0S80~M+TV9&G4H&yU5RF0|hg72(IxgRDZ5IlAW&{;!hvhL{~h->}gLNY%OBBI`;xU`ATKwF(HKS=H;cw6QiUj z5A(=F0x2Prg-k~A(2eym|QoS&phW9^EA?a!d7kQxVeL%TrLhzkOHWLo&4SJza zs4X6k`{opjMLIe<79Gc>l*Hq4AFW(26NyBy%9ja-1h2iJ=uJXzFRWN0h{a%L1|}yJ zhYo{jf^JZCZMg}UT-F0Z2n3*Mf}pYZ=&Wg4%gWNK6gMBez3+mdF5DnI`mi9It6Bf( zF-5Tm$t0wDVe1w_I1KOXR)&Fgd65U|!kQ~ZyBnF8$E&y(Tr;S4RS01)GYk9QR}2r= ztiO3PL?SRZTMMblNyUXp5TL4}_DyRM0M~W-(a-;!6Bou8(SLU0?;Fp9&NjlbxA9!i zFOo5iw*)AJ;9Ivp!(HpYLA7c(fpD>H3){+b-F1TgK1in(M~*7y=0E_tQ46WQ6#V|Z zg8loI>q?bs3E)EJ1g9?@;;wJ5M_b~1<@6hUJp9NvNcE+dI6uL`Pv2wxeXTzcW-?iR ze&9u%TogkKw&bzqDyWi+ZNsXOn)QQ0D3_rl4s&_PX5ip|E2PA55udjNu=m7&vF7F> zw2;f`V-whAN#FHR;>iF2X2GG90Z|%d?b;g%hJplx0ru|QOY!m~v7|vbt|P08=`$tF zg3G!a*AVNTMJ8srf9q3ME$(yu<5Kw;q5cwD8D!LA-hxZxF8MDM5tU?ZEJQF=vu00s54x^1 zaX?~;8M;OULt8b1{h*?XV3)(X+Xi`O-!AUEm&6B>E$Cc2rLLjc@VlW1p4xwr% zeSN8gi2{22l3beVqH5#`M6dG4!3Yb^{Prw^-K&Wt4Bk8PDi3aX%BMU)K6jEes|DxI zD?UC{vwkWCu_&B5TLU8m46m$hWKLPX5BSdpfE&Lh*2EIw_>K)bn5kUBuse~CB1V*O zxW?AK1>|N$@!$}{D@S}*935FjE;vNTof6$x$Q`rjP&UgH=8KGN9^mcY{5!w5`Edez z$Tz>UdmWSKk8|(u3c9-?o2^xzQb{p3CfL3W5(zkd0wyk~&|JmY*YJQ4!r1(@cs@DM zCFb&ZG@^b5iwi>?2$WLv_4WA_7#bYp=$Sr(rvEp97!Vt+n;Vo~`YLZcR2d1W6R7|960qBoBQwcC9);sV8Ua6ASf0foq_4JqEv!p z4~&foc5DaJg5B?`Kw;iJ3TX8Z5=sg=u%eGpIMi}kZ!^Bo85Hs8t6hFR;k+0O2JcvVgEBVWDD-5qX8B?X zy1SrSt$E?oPpLSLIvq&m-vsYQrck9)$6X;>A?NC?k%kpRtlk9wEB2JZQ804s{hYIW7l#pOHPV_=r&=9mC6-|%c0oeojbp%v{0_~8NXc + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/periodictask.svg b/orchestra/static/orchestra/icons/periodictask.svg new file mode 100644 index 00000000..29a1f844 --- /dev/null +++ b/orchestra/static/orchestra/icons/periodictask.svg @@ -0,0 +1,3801 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/postgresql.png b/orchestra/static/orchestra/icons/postgresql.png new file mode 100644 index 0000000000000000000000000000000000000000..e7edb89060ece854ce90a1753fa278b6e91403ed GIT binary patch literal 3868 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 eu86?jlKd~>Ua5~RI8cxP0000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + PostgreSQL Server + Aug 2007 + + + F.Bellaiche <frederic.bellaiche@gmail.com> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/scriptlog.png b/orchestra/static/orchestra/icons/scriptlog.png new file mode 100644 index 0000000000000000000000000000000000000000..9f9e13bd8756d1cf5b097dee334cb5c409f02550 GIT binary patch literal 2709 zcmV;G3TpLbY*F7Wpd{G_b&hd03mcmSaefw zb#h~60BCe{a5^t9YH4k4VPh_Bb#rNBXklz@E@OIWX5(G}013rOL_t(&fz?@SY#hZM z|IO^|?d?5$=ZkZ39Onh4RtmU)OK?%0Q=0Sx7>N&25Hw1fN^OIbT!bn`vDNaaqCh}U zQG`UwhY)O))=dN?R*Fh1P@|?XlprcZXk_jZ+viu~+{3=tKBga>-R$k{-JVS()W5X$ z?9A-m@ArSs%o5Hy{-1?eZ;l>4D$ZqRAA*imMjs)Rm~F0`ZB7VT?Bi8^i_cm5`Hg;n z__;>j?Cb0M@@5bqRtvoFo9F(xspDG@wYRUmLCpHo#ti{D=LFm~Zs6pW0kZfU2S=$? z!e9UP7e*=7nZV8}<-$)69s1>^uR@^00-W<7Y~8v|E|<&Db-fx`140NvJ-7TaF^ZtS zi7w>ld%jGLe)7z7&+OjkZ&tz3p^wNThiMK!96Y zZzG|muBe z&m)S*jy(VLk8c_QH^7aOa3VqpMX6jyBoYB*4B=21-?`^LA7kQgpMCzz>*0zusU?R)(Hy71xeo_^u!kNbb# z|DQ$)xLSZQ22m8v|3;fJ20;+awsqV-$7&H(RpAc=plKQeK`^hTrVwtswIjel&%F5J zi}F_@U?rhx8dO!S_;0L15;;GiZslK$0Xxqfx9{wF-W}ACywGwXMO* zmgoi!_2^BrfFKC2T6faE(Kak#$kbE{lT(vOCKH&RnZnG>G_u(YCQ@S<8^40)a5Lqc z?|SyxU)|ma0oNEPm&+9~;N!6fC z(=|dx&mREXv1|ec0H4nXQ4}k{xdpMc-|O{O+~4+%Zz3FOMn0d%!omW|<+5p2kH-U9 zmLZA^!)H!)xPh-r!1z&J*P*IvP4I3c>{Xc%0$G;f^ZBr5%^EPqs>XxCVBKo&LV#C* zGlH^F@OBr{b-ij{Fc@qITqELYfkrjIoMnYo42>^k-32%js06|ot4M|Q06LK{id8Le zcHlRGK;=t}{N5;d`*=E?Mmn9Y4}vkYx3@0|xcO|Y0RxDW1?+Q{l9e1mSOR&e7H}64ydzO`}#~%L!a=>bj!jNZVsLN}2M!#7s;ann@ghV~L|0cA4j(>@xw*N93AhHa;loB> zJk6$t3)KKelYy(khDLNIep+JF;4uuT>AM zn%6W9qobpE{q@&z;J|?@@Pv>G5}$qc85Biqw%CfIWjW;_ps=KRP;! z zA`zjxyW0rtHGo4Qvv1|+uK`=F6?@){og1NalOifKe(=D$O z{PWLCs;a)=8pLifVDb%(^2j3?9v;SPuf2xu z?rt!~5C{a2N~O@&)`nazhbvdEprxe+oO4slUauE^zaN^WVRm*FjIoM$dpsVfs*2O6 zPn+O@sxLG*J~+SP_F`gU0+~z(ilTsX4#pTV znT)BR&dyFS#>}i`6j1A?N>LQN^Ugc@d_Hf22P|on5aI)P?RBt=*t6H}-Mew`z4zku z>C*@V0`U2KD3{AnRkh-b)#LG?SS*^EM^TiDTI2P4p=lc4e*5hz@FoF52m=sUF4$W4 zd-m+X=FOY&?z`_I5{XnS6AXZqQjCp_nf0PvE}L^CNrIv%IC=7Pvu&NrImezodjJ4u&YZ!64?c*6g$47N#(D{Y07|KOqAiLd3WWkroH$Y8^R);7 z!1b@E9A2;25p=D6uG>l6-o1N42*L31FnW7?Q7jhC#2^R)Zn@mTC!bXE`TU^| zKKS4$=e*Dmf$Jnh05Sj%7InAmtW-~WdV2a>TU&qn*kh0R30XYBW{e>kjpC)3UMeP& z$$jU}o%=oKTyc9BY4jZaX3Ms1+n!pnV#UE-yLJT_V+aO=c=gp+3lkF)KdKe5PV%)8 z02WC!H#Z;b?d=VmJb5yo%jIfjn_BX<5&(eh+qXZZX<8rW{L!U?2Y~+pIT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + text + plaintext + regular + script + shell + bash + python + perl + php + ruby + + + + + + Jakub Steiner + + + http://jimmac.musichall.cz + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/ssh.svg b/orchestra/static/orchestra/icons/ssh.svg new file mode 100644 index 00000000..5e358e7b --- /dev/null +++ b/orchestra/static/orchestra/icons/ssh.svg @@ -0,0 +1,890 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + SSH Server + Aug 2007 + + + F.Bellaiche <frederic.bellaiche@gmail.com> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/taskstate.png b/orchestra/static/orchestra/icons/taskstate.png new file mode 100755 index 0000000000000000000000000000000000000000..777c58989462c1b767a27a19d2ca1be90061cfeb GIT binary patch literal 3868 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/users.png b/orchestra/static/orchestra/icons/users.png new file mode 100644 index 0000000000000000000000000000000000000000..854e90e6648f3695568c498464ac95d344dea169 GIT binary patch literal 3081 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/vps.png b/orchestra/static/orchestra/icons/vps.png new file mode 100644 index 0000000000000000000000000000000000000000..71880e1cfeff68c2325720b05848f5cf316f73bb GIT binary patch literal 2710 zcmV;H3TgF;P)sZ@%OKKiIj2yxGj9XqD7 z*=X}U{Px>#zgAjW+P+-m0iw|;_uhMNsZ#2FKxDJY8o;hyyEcYGpw#!A`tSDk_PS^^ddp6~Z&;Sa;lqbh)6>%rzVgZ||H)jxVG2c~ z(Y=k0jgQw7K40S5XP>o%Qi^xpdFK?cHW&=fyDQx(P)Z@CRF-8;IF8fk z0q}agO^-hMsOC71YqXROm%1NK(?mx{M_xxqN8AHoS=O{+7&%i@S|FJWfYP2SNy%nwseB>|}Iwltd!I(9jS)Jw4oW z&pj;kJjo2e)YKH+-QAd`No{Q{XU?1<8jTVPg-9loY}~k!+1Xj7lw>l1X_^cS4DjLj zhaA{)fTs^WO(Z`;I6U_-?&`Xtt;)*E=olalNfN7c-hJnZ9VRP$dT2d{<*2Pdtk&}~yrfD~fWM;cv2&9w>uqD1FflP@)f9yYB`)07@y_Fbt-rrR&zRW;F2-H^5O{rmS*U0u!EwQD(YbBTexLLsD-6ciL>Mubo(L{U)@QcA|h$La0urJjV`C!#9*>8%wl*xw!Zc0hWMFt~n6Ik7;&T3FlI2NKmr`_ebg*yV zKAwO6d1`8E0J!hI`+zGab#rsG`#m0y6OYG{Qc_u2Nm*H$EBY3L;o)J%$Hy5P8>6nS zj^5s0{C+>tXq0p3&e7J^hH0ADGT=ClG7N*V;xaaUyouseF-=`f6mKZz*x!#~7zUl4 zom5s<0?^abvtTkSSFT)i2EySmIXO8L6%`Q-2C1m1K-YD=UM~#|4Oo_iVHmgp*tYG8 zxrcuE5bH+Q5&dBlr4)}n_89f`_0-qbyCa!-cp=D$xy4{=YKn9^O*)+>8jW)K@?~^g zCpR~jk&zLqs;X|xKxPeO5Q#)GZ~u5a9_s7sT`~X_6%~sjwAd*W2m}ZO0u&S!08ms^ z}2XIx53L%8%3H+t1 zs`8E|4-k)6MRc#{4_mfuiNIAg`r^(P&w6xymB;IyyIrVxe`G~gi#Zpw_cL$rH{Ea? z8{2WFW~OHn9)L(;WaX=`zA9|nzAZtw&t)t>3L!Xg;)L(L_ui|^EaFMSFme|n<`&{? zp@eQ_mpMB!6}gPP5CWgihi%&qTsw)~%G|)f0B8D7v$}GP`*MG?vy&%JvSP&ws;a8^ z;)^d_z0%p)Nh*~hkx06;lc&C{BeCHA@h5_=JWZ8$K%x1)e#5;Xl!g`#flZ= z=jYSX(&F|rCj&hfd+@aWgJZQ|Zy1TovEQ?jER`KPRUlNT*-3;7+|NV2s9Hwcq zGPaVlpZ${5PrX(p`oFSu)LN5+qRjVokb}{US1x}d8=gq)i$|mM+HKn{>FC`@rErbv|cka|DCMFg|>YLgvm5pH-LMbJL zQi>N|cwvtaqE;!TDWwFUT`4mvvDm`wQX=D92%&@!3UID|ca&1nG|gYQVonI*0sMgX zYSXz^xm&Jkmvh~x4Omy3p_Gy^&%afK5E@rVXWDYrixgMLJAk8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/web.png b/orchestra/static/orchestra/icons/web.png new file mode 100644 index 0000000000000000000000000000000000000000..e5c771fa19ef5b842731751a9aed1aa774311404 GIT binary patch literal 3984 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/zone.png b/orchestra/static/orchestra/icons/zone.png new file mode 100644 index 0000000000000000000000000000000000000000..00de2afb093233852b1824487ec6607de6069978 GIT binary patch literal 3119 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 JPDHLkV1n(m{5${v literal 0 HcmV?d00001 diff --git a/orchestra/static/orchestra/icons/zone.svg b/orchestra/static/orchestra/icons/zone.svg new file mode 100644 index 00000000..66cb148b --- /dev/null +++ b/orchestra/static/orchestra/icons/zone.svg @@ -0,0 +1,2875 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/images/favicon.png b/orchestra/static/orchestra/images/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..0ff8c430a7b4f160ecf48ba039431849d26a05a9 GIT binary patch literal 1150 zcmaizUrbU_9LEoeR?AXbLMy<31q5n;P;^aaA(d^~GP9NG{EJ>hox+%fEFm;NTaph- zIwLh*B5pn8dd#e?_j>WEna-m1(vx#5_w62=ci?oLyYIbc=XXB8^E}SUTfP_k`lBeI$l+7}ww-={uHH&^*!>6al3V&B+k%QVo z_lC|{=e(AZeDnJObMS)d5tPY8c;jIZobBT@=W70umXa5sd$+TW+H3v=j~&A%J7Qye zAmjzgFF7#zK!+8-lk;&)Yrv`0hQ&}H^xTzc7cORbHaW^t3F3WlFfCa2Kg77T3D&A6 z+$>;N+XSSdnmHN+z0=FC)}OA%`^Z*4=JyQJR2EOMP6gk zWsh3X4~^P}p$-GatnKi18Zl*S$I^5U$FI(~k#e+$<7s?p$-1}7Dm^QMwk$izK3eWDZEObg_REx{x4BkNak`XCm1G2^V=mF?B*p_19qTE^gB zWgRLDt8rY!tVx94H5$v_=m|_ZYT5I9cl4nz(IYx)6gkRH6lS+==1>slT$hOlmbdbq zDIbL2x$_+~stQE$pM~8&^_X=vFWvHLi|0p9z;wrmuOWHc%7MAO1`?qoj8`Nv3+I&i zS%+hNf2fbEx!sr_G=#-ZYsXw(2jQW z?mL^0`eGeq5_=dg6;*H659Mhss!!TKXQX;@{k#ie!6*Xm`mi`OnRZio_`?0++S zVHy7R9>`I&`9uk$ziBLk9gSi9Yo`hCo|uA6aqeA~jR<8!=LKieG78nkDN9x9N7}pp f>)-W6LVoQbWImD=j}RNH-3GN$(Y;Htgi!V``FcS< literal 0 HcmV?d00001 diff --git a/orchestra/static/orchestra/images/orchestra-logo.png b/orchestra/static/orchestra/images/orchestra-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..1adc0739b8e3852b0431d892d623cf1e52f0d5bf GIT binary patch literal 1964 zcmV;d2UGZoP)$?tSwar#?%CzrdSO)VhxxGDk>TT z4C2DFEU?Ra@4nkVu#}ZuwP)u2@$UKF_nmXzJNKRkAp{pADU{djNLDmeX9FlFy+U^WLXEw4tNyAbYVPh9cSbqDdy9qZxbX3TmUQ^XD`9Sw{O zKRrQMlB6}un6mR}Jvvr}u4!dBU%3g4FsPCWmosaEDCY*cZZxCk^bvTP>oBXW86-|n zCnaX>6Qr7RefL=!~r=xDa`>p#`(DmC;mU|_~$sDI(-$j1O~i)3VU#&4&gW&mChjVQ^n+LP66BhI0#X><3k#MP-xfuTCTWI(AVsI{DF`9h$#-rE7L%r{rOPF*MG69G44(A+(>mP9@IZKcaA+wHEu5N}hk7f5_Q@ruqc=K;U||vDR5#L+G9Fr(5DITmIl|rTaCWvKt7bps zj}E{}aYO5B2ff~c87*gV(l7=HQ`GlgO?EBzm^$fmxp8*YE6Dd2LuDGAwzU7F(4%(e zRvXece+S;dP+Y$b;AgU0D_NZ1ZcIw~e&M1@IsepQxY}B9QH+3j{7{$*lN%E*Ap|5E zxAk%DljA6&X}_I=!g4Ot=N1(yK(y_lU40VYQjYK4BW10s!a6#+z3g ztzb_Fyk9DT`>W4_DE0(l3q1bM_t1I#Px!p26$by`iPnFv29jZ*!;_C2{vrfZV?WGe zdek(Gk#)ehL4Yw&V_nGX|24?=OZcrIF>UyB)&=0KBexY>0P zU0s)vnO6vjskmPAHl91O2NXaJQ}`iEtkt=`3mA7en5MZ$QEl^o_sZgmb=zvS;DR4R zl2u?Rg!HS8P{RGOWt}-6!4ww=r5JsCfX_P}KJWA~%rH|x0o2-(`Mccwn{Ev68jE}U z%yoCdJe`s4FRlK~QBBoGr;grZe?-xq&0{z{!;9nVUV|rdGLqC}IBjVYw$^AQ3}e$% zXz#tv4{`Ieh#r|ee%Oma3n7G|M;;4&apT~x*OW!OJ1zqNv?^eA94Z$xBd^ zQs7C+L#jPJ?tb3<_Ewy@bBn)Y8t>jKIw9ln#u!^cDBJ0qGK)R`@-8jqjTe!9r3t5g zv;o$<1t@S&haBhB3itQmFWc5cJ6q2QQ}DH>Y3_~3|DazOg)wC-fU4OUIr6s|mCr1# zgvVcmskx;9fWiI%2Ku^jq2U9o>D1xqU?@NU{N6P6EkX!AA@}`+#|1zD%wvkWfl_If yutc(DnRWo6lnoO~!j{GVZR#U$0;m@cLHZvjOCH!?kz4El0000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/images/page-gradient.png b/orchestra/static/orchestra/images/page-gradient.png new file mode 100644 index 0000000000000000000000000000000000000000..8e16a28052af469ecf10b0d513f4dc814e94a61d GIT binary patch literal 280 zcmeAS@N?(olHy`uVBq!ia0vp^j6ht>!3HEF|7SRZ1d4;)ofy`glX(f`uqAoByDpwJ9Y7sn8d z^KVZm`Z))RxL&ONR{MAVLe9+B3J0$!&R}3xIB3w*C&Rwc5JY{QZrJ-sqz9M)R)(`xUy?_1ryk-BVFv*Yg{Y)nE)^UufpT^@f; XRCAfuT^%=|8yP%Z{an^LB{Ts5xR7Q; literal 0 HcmV?d00001 diff --git a/orchestra/templates/admin/base.html b/orchestra/templates/admin/base.html new file mode 100644 index 00000000..bf45da0f --- /dev/null +++ b/orchestra/templates/admin/base.html @@ -0,0 +1,104 @@ +{% load theming_tags staticfiles %} + + + +{% block title %}{% endblock %} + + +{% block extrastyle %}{% endblock %} + +{% if LANGUAGE_BIDI %}{% endif %} +{% render_theming_css %} +{% block adminextraprettystyle %}{% endblock %} + +{% block extrahead %}{% endblock %} +{% block blockbots %}{% endblock %} + +{% load i18n %} + + + + +{% block branding-stetic %} + {% if not is_popup %} +
      +
      + {% endif %} +{% endblock %} + +{% block breadcrumb-stetic %} + {% if not is_popup %} +
      + {% endif %} +{% endblock %} + + +{% block container-stetic %}
      {% endblock %} + + {% if not is_popup %} + + {% block header-stetic %} + + {% block breadcrumbs %}{% endblock %} + {% endif %} + + {% block messages %} + {% if messages %} +
        {% for message in messages %} + {{ message }} + {% endfor %}
      + {% endif %} + {% endblock messages %} + + +
      + {% block pretitle %}{% endblock %} + {% block content_title %}{% if title %}

      {{ title }}

      {% endif %}{% endblock %} + {% block content %} + {% block object-tools %}{% endblock %} + {{ content }} + {% endblock %} + {% block sidebar %}{% endblock %} +
      +
      + + + {% block footer %}{% endblock %} +
      + + + + + diff --git a/orchestra/templates/admin/base_site.html b/orchestra/templates/admin/base_site.html new file mode 100644 index 00000000..ead258a6 --- /dev/null +++ b/orchestra/templates/admin/base_site.html @@ -0,0 +1,26 @@ +{% extends "admin/base.html" %} +{% load admin_tools_menu_tags utils %} + +{% block title %}{% if header_title %}{{ header_title }}{% else %}{{ title }}{% endif %} | {{ SITE_VERBOSE_NAME }} {% endblock %} + +{% block extrastyle %} +{{ block.super }} +{% if user.is_active and user.is_staff %} +{% if not is_popup %} +{% admin_tools_render_menu_css %} +{% endif %} +{% endif %} +{% endblock %} + +{% block branding %} +

      {{ SITE_VERBOSE_NAME }} {% version %} +{% endblock %} + +{% block nav-global %} +{% if user.is_active and user.is_staff %} +{% if not is_popup %} +{% admin_tools_render_menu %} +{% endif %} +{% endif %} +{% endblock %} + diff --git a/orchestra/templates/admin/index.html b/orchestra/templates/admin/index.html new file mode 100644 index 00000000..9c841f69 --- /dev/null +++ b/orchestra/templates/admin/index.html @@ -0,0 +1,18 @@ +{% extends "admin/base_site.html" %} +{% load i18n admin_tools_dashboard_tags %} + +{% block extrastyle %} +{{ block.super }} +{% block dashboard_css %}{% admin_tools_render_dashboard_css %}{% endblock %} +{% endblock %} + +{% block title %}{{ SITE_VERBOSE_NAME }}{% endblock %} + +{% block breadcrumb-stetic %}{% endblock %} +{% block bodyclass %}dashboard{% endblock %} + +{% block breadcrumbs %}{% endblock %} +{% block content_title %}{% endblock %} +{% block content %} +{% admin_tools_render_dashboard %} +{% endblock %} diff --git a/orchestra/templates/admin/login.html b/orchestra/templates/admin/login.html new file mode 100644 index 00000000..41505d1b --- /dev/null +++ b/orchestra/templates/admin/login.html @@ -0,0 +1,62 @@ +{% extends "admin/base_site.html" %} +{% load i18n admin_static utils %} + +{% block extrastyle %} + {{ block.super }} + +{% endblock %} + +{% block adminextraprettystyle %} +{% endblock %} +{% block bodyclass %}login{% endblock %} + +{% block nav-global %}{% endblock %} + +{% block content_title %}{% endblock %} + +{% block breadcrumbs %}{% endblock %} + +{% block content %} +{% if form.errors and not form.non_field_errors and not form.this_is_the_login_form.errors %} +

      +{% if form.errors.items|length == 1 %}{% trans "Please correct the error below." %}{% else %}{% trans "Please correct the errors below." %}{% endif %} +

      +{% endif %} + +{% if form.non_field_errors or form.this_is_the_login_form.errors %} +{% for error in form.non_field_errors|add:form.this_is_the_login_form.errors %} +

      + {{ error }} +

      +{% endfor %} +{% endif %} + +
      +
      {% csrf_token %} +
      + {% if not form.this_is_the_login_form.errors %}{{ form.username.errors }}{% endif %} + {{ form.username }} +
      +
      + {% if not form.this_is_the_login_form.errors %}{{ form.password.errors }}{% endif %} + {{ form.password }} + + +
      + {% url 'admin_password_reset' as password_reset_url %} + {% if password_reset_url %} + + {% endif %} +
      + +
      +
      + + +
      +{% endblock %} + diff --git a/orchestra/templates/rest_framework/api.html b/orchestra/templates/rest_framework/api.html new file mode 100644 index 00000000..05f93fd5 --- /dev/null +++ b/orchestra/templates/rest_framework/api.html @@ -0,0 +1,27 @@ +{% extends "rest_framework/base.html" %} +{% load rest_framework utils %} + +{% block head %} + {{ block.super }} + +{% endblock %} + +{% block title %}{{ SITE_VERBOSE_NAME }} REST API{% endblock %} +{% block branding %}{{ SITE_VERBOSE_NAME }} REST API {% version %}{% endblock %} +{% block userlinks %} +
    1. Admin
    2. + {% if user.is_authenticated %} + + {% else %} +
    3. {% optional_login request %}
    4. + {% endif %} +{% endblock %} + diff --git a/orchestra/templatetags/__init__.py b/orchestra/templatetags/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/orchestra/templatetags/markdown.py b/orchestra/templatetags/markdown.py new file mode 100644 index 00000000..ad7d6faf --- /dev/null +++ b/orchestra/templatetags/markdown.py @@ -0,0 +1,13 @@ +from __future__ import absolute_import + +from django import template +from markdown import markdown + + +register = template.Library() + + +@register.filter(name='markdown') +def do_markdown(text): + return markdown(text) + diff --git a/orchestra/templatetags/utils.py b/orchestra/templatetags/utils.py new file mode 100644 index 00000000..ce5d5c70 --- /dev/null +++ b/orchestra/templatetags/utils.py @@ -0,0 +1,47 @@ +from django import template +from django.core.urlresolvers import reverse, NoReverseMatch +from django.forms import CheckboxInput + +from orchestra import get_version + + +register = template.Library() + + +@register.simple_tag(name="version") +def controller_version(): + return get_version() + + +@register.simple_tag(name="admin_url", takes_context=True) +def rest_to_admin_url(context): + """ returns the admin equivelent url of the current REST API view """ + view = context['view'] + model = getattr(view, 'model', None) + url = 'admin:index' + args = [] + if model: + url = 'admin:%s_%s' % (model._meta.app_label, model._meta.module_name) + pk = view.kwargs.get(view.pk_url_kwarg) + if pk: + url += '_change' + args = [pk] + else: + url += '_changelist' + try: + return reverse(url, args=args) + except NoReverseMatch: + return reverse('admin:index') + + +@register.filter +def size(value, length): + value = str(value)[:int(length)] + num_spaces = int(length) - len(str(value)) + return str(value) + (' '*num_spaces) + + +@register.filter(name='is_checkbox') +def is_checkbox(field): + return isinstance(field.field.widget, CheckboxInput) + diff --git a/orchestra/urls.py b/orchestra/urls.py new file mode 100644 index 00000000..0b5892d9 --- /dev/null +++ b/orchestra/urls.py @@ -0,0 +1,31 @@ +from __future__ import absolute_import + +from django.contrib import admin +from django.conf import settings +from django.conf.urls import patterns, include, url + +from . import api + + +admin.autodiscover() +api.autodiscover() + +urlpatterns = patterns('', + # Admin + url(r'^admin/', include(admin.site.urls)), + url(r'^admin_tools/', include('admin_tools.urls')), + # REST API + url(r'^api/', include(api.router.urls)), + url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')), + url(r'^api-token-auth/', + 'rest_framework.authtoken.views.obtain_auth_token', + name='api-token-auth' + ), + +) + +if settings.DEBUG: + import debug_toolbar + urlpatterns += patterns('', + url(r'^__debug__/', include(debug_toolbar.urls)), + ) diff --git a/orchestra/utils/__init__.py b/orchestra/utils/__init__.py new file mode 100644 index 00000000..921dfe14 --- /dev/null +++ b/orchestra/utils/__init__.py @@ -0,0 +1 @@ +from .options import * diff --git a/orchestra/utils/apps.py b/orchestra/utils/apps.py new file mode 100644 index 00000000..69ebfa29 --- /dev/null +++ b/orchestra/utils/apps.py @@ -0,0 +1,42 @@ +from django.utils.importlib import import_module +from django.utils.module_loading import module_has_submodule + +def autodiscover(module): + """ Auto-discover INSTALLED_APPS module.py """ + from django.conf import settings + for app in settings.INSTALLED_APPS: + mod = import_module(app) + try: + import_module('%s.%s' % (app, module)) + except ImportError: + # Decide whether to bubble up this error. If the app just + # doesn't have the module, we can ignore the error + # attempting to import it, otherwise we want it to bubble up. + if module_has_submodule(mod, module): + print '%s module caused this error:' % module + raise + +def isinstalled(app): + """ returns True if app is installed """ + from django.conf import settings + return app in settings.INSTALLED_APPS + + +def add_app(INSTALLED_APPS, app, prepend=False, append=True): + """ add app to installed_apps """ + if app not in INSTALLED_APPS: + if prepend: + return (app,) + INSTALLED_APPS + else: + return INSTALLED_APPS + (app,) + return INSTALLED_APPS + + +def remove_app(INSTALLED_APPS, app): + """ remove app from installed_apps """ + if app in INSTALLED_APPS: + apps = list(INSTALLED_APPS) + apps.remove(app) + return tuple(apps) + return INSTALLED_APPS + diff --git a/orchestra/utils/functional.py b/orchestra/utils/functional.py new file mode 100644 index 00000000..2dcb5fee --- /dev/null +++ b/orchestra/utils/functional.py @@ -0,0 +1,9 @@ +def cached(func): + """ caches func return value """ + def cached_func(self, *args, **kwargs): + attr = '_cached_' + func.__name__ + if not hasattr(self, attr): + setattr(self, attr, func(self, *args, **kwargs)) + return getattr(self, attr) + return cached_func + diff --git a/orchestra/utils/options.py b/orchestra/utils/options.py new file mode 100644 index 00000000..d3e7c42c --- /dev/null +++ b/orchestra/utils/options.py @@ -0,0 +1,39 @@ +import urlparse + +from django.core.mail import EmailMultiAlternatives +from django.template.loader import render_to_string +from django.template import Context + + +def send_email_template(template, context, to, email_from=None, html=None): + """ + Renders an email template with this format: + {% if subject %}Subject{% endif %} + {% if message %}Email body{% endif %} + + context can be a dictionary or a template.Context instance + """ + + if isinstance(context, dict): + context = Context(context) + if type(to) in [str, unicode]: + to = [to] + + if not 'site' in context: + from orchestra import settings + url = urlparse.urlparse(settings.SITE_URL) + context['site'] = { + 'name': settings.SITE_NAME, + 'scheme': url.scheme, + 'domain': url.netloc, + } + + #subject cannot have new lines + subject = render_to_string(template, {'subject': True}, context).strip() + message = render_to_string(template, {'message': True}, context) + msg = EmailMultiAlternatives(subject, message, email_from, to) + if html: + html_message = render_to_string(html, {'message': True}, context) + msg.attach_alternative(html_message, "text/html") + msg.send() + diff --git a/orchestra/utils/paths.py b/orchestra/utils/paths.py new file mode 100644 index 00000000..73d3e03f --- /dev/null +++ b/orchestra/utils/paths.py @@ -0,0 +1,24 @@ +import os + + +def get_project_root(): + """ Return the current project path site/project """ + from django.conf import settings + settings_file = os.sys.modules[settings.SETTINGS_MODULE].__file__ + return os.path.dirname(os.path.normpath(settings_file)) + + +def get_project_name(): + """ Returns current project name """ + return os.path.basename(get_project_root()) + + +def get_site_root(): + """ Returns project site path """ + return os.path.abspath(os.path.join(get_project_root(), '..')) + + +def get_orchestra_root(): + """ Returns orchestra base path """ + import orchestra + return os.path.dirname(os.path.realpath(orchestra.__file__)) diff --git a/orchestra/utils/plugins.py b/orchestra/utils/plugins.py new file mode 100644 index 00000000..6b53a4ea --- /dev/null +++ b/orchestra/utils/plugins.py @@ -0,0 +1,13 @@ +class PluginMount(type): + def __init__(cls, name, bases, attrs): + if not hasattr(cls, 'plugins'): + # This branch only executes when processing the mount point itself. + # So, since this is a new plugin type, not an implementation, this + # class shouldn't be registered as a plugin. Instead, it sets up a + # list where plugins can be registered later. + cls.plugins = [] + else: + # This must be a plugin implementation, which should be registered. + # Simply appending it to the list is all that's needed to keep + # track of it later. + cls.plugins.append(cls) diff --git a/orchestra/utils/python.py b/orchestra/utils/python.py new file mode 100644 index 00000000..adba5e05 --- /dev/null +++ b/orchestra/utils/python.py @@ -0,0 +1,60 @@ +import collections + + +class OrderedSet(collections.MutableSet): + def __init__(self, iterable=None): + self.end = end = [] + end += [None, end, end] # sentinel node for doubly linked list + self.map = {} # key --> [key, prev, next] + if iterable is not None: + self |= iterable + + def __len__(self): + return len(self.map) + + def __contains__(self, key): + return key in self.map + + def add(self, key): + if key not in self.map: + end = self.end + curr = end[1] + curr[2] = end[1] = self.map[key] = [key, curr, end] + + def discard(self, key): + if key in self.map: + key, prev, next = self.map.pop(key) + prev[2] = next + next[1] = prev + + def __iter__(self): + end = self.end + curr = end[2] + while curr is not end: + yield curr[0] + curr = curr[2] + + def __reversed__(self): + end = self.end + curr = end[1] + while curr is not end: + yield curr[0] + curr = curr[1] + + def pop(self, last=True): + if not self: + raise KeyError('set is empty') + key = self.end[1][0] if last else self.end[2][0] + self.discard(key) + return key + + def __repr__(self): + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, list(self)) + + def __eq__(self, other): + if isinstance(other, OrderedSet): + return len(self) == len(other) and list(self) == list(other) + return set(self) == set(other) + diff --git a/orchestra/utils/system.py b/orchestra/utils/system.py new file mode 100644 index 00000000..9ff0e4f9 --- /dev/null +++ b/orchestra/utils/system.py @@ -0,0 +1,116 @@ +import errno +import fcntl +import getpass +import os +import re +import select +import subprocess +import sys + +from django.core.management.base import CommandError + + +def check_root(func): + """ Function decorator that checks if user has root permissions """ + def wrapped(*args, **kwargs): + if getpass.getuser() != 'root': + cmd_name = func.__module__.split('.')[-1] + msg = "Sorry, '%s' must be executed as a superuser (root)" + raise CommandError(msg % cmd_name) + return func(*args, **kwargs) + return wrapped + + +class _AttributeString(str): + """ Simple string subclass to allow arbitrary attribute access. """ + @property + def stdout(self): + return str(self) + + +def make_async(fd): + """ Helper function to add the O_NONBLOCK flag to a file descriptor """ + fcntl.fcntl(fd, fcntl.F_SETFL, fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK) + + +def read_async(fd): + """ + Helper function to read some data from a file descriptor, ignoring EAGAIN errors + """ + try: + return fd.read() + except IOError, e: + if e.errno != errno.EAGAIN: + raise e + else: + return '' + + +def run(command, display=True, error_codes=[0], silent=True): + """ Subprocess wrapper for running commands """ + if display: + sys.stderr.write("\n\033[1m $ %s\033[0m\n" % command) + out_stream = subprocess.PIPE + err_stream = subprocess.PIPE + + p = subprocess.Popen(command, shell=True, executable='/bin/bash', + stdout=out_stream, stderr=err_stream) + make_async(p.stdout) + make_async(p.stderr) + + stdout = str() + stderr = str() + + # Async reading of stdout and sterr + while True: + # Wait for data to become available + select.select([p.stdout, p.stderr], [], []) + + # Try reading some data from each + stdoutPiece = read_async(p.stdout) + stderrPiece = read_async(p.stderr) + + if display and stdoutPiece: + sys.stdout.write(stdoutPiece) + if display and stderrPiece: + sys.stderr.write(stderrPiece) + + stdout += stdoutPiece + stderr += stderrPiece + returnCode = p.poll() + + if returnCode != None: + break + + out = _AttributeString(stdout.strip()) + err = _AttributeString(stderr.strip()) + p.stdout.close() + p.stderr.close() + + out.failed = False + out.return_code = returnCode + out.stderr = err + if p.returncode not in error_codes: + out.failed = True + msg = "\nrun() encountered an error (return code %s) while executing '%s'\n" + msg = msg % (p.returncode, command) + sys.stderr.write("\n\033[1;31mCommandError: %s %s\033[m\n" % (msg, err)) + if not silent: + raise CommandError("\n%s\n %s\n" % (msg, err)) + + out.succeeded = not out.failed + return out + + +def get_default_celeryd_username(): + """ Introspect celeryd defaults file in order to get its username """ + user = None + try: + with open('/etc/default/celeryd') as celeryd_defaults: + for line in celeryd_defaults.readlines(): + if 'CELERYD_USER=' in line: + user = re.findall('"([^"]*)"', line)[0] + finally: + if user is None: + raise CommandError("Can not find the default celeryd username") + return user diff --git a/orchestra/utils/tests.py b/orchestra/utils/tests.py new file mode 100644 index 00000000..87112a27 --- /dev/null +++ b/orchestra/utils/tests.py @@ -0,0 +1,100 @@ +import string +import random + +from django.conf import settings +from django.contrib.auth import BACKEND_SESSION_KEY, SESSION_KEY, get_user_model +from django.contrib.sessions.backends.db import SessionStore +from django.test import LiveServerTestCase, TestCase +from orm.api import Api +from selenium.webdriver.common.keys import Keys +from selenium.webdriver.firefox.webdriver import WebDriver +from xvfbwrapper import Xvfb + +from orchestra.apps.accounts.models import Account + + +User = get_user_model() + + +class AppDependencyMixin(object): + DEPENDENCIES = () + + @classmethod + def setUpClass(cls): + current_app = cls.__module__.split('.tests.')[0] + INSTALLED_APPS = ( + 'orchestra', + 'orchestra.apps.accounts', + current_app + ) + INSTALLED_APPS += cls.DEPENDENCIES + INSTALLED_APPS += ( + # Third-party apps + 'south', + 'django_extensions', + 'djcelery', + 'djcelery_email', + 'fluent_dashboard', + 'admin_tools', + 'admin_tools.theming', + 'admin_tools.menu', + 'admin_tools.dashboard', + 'rest_framework', + # Django.contrib + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'django.contrib.admin', + ) + settings.INSTALLED_APPS = INSTALLED_APPS + super(AppDependencyMixin, cls).setUpClass() + + +class BaseTestCase(TestCase, AppDependencyMixin): + pass + + +class BaseLiveServerTestCase(AppDependencyMixin, LiveServerTestCase): + @classmethod + def setUpClass(cls): + cls.vdisplay = Xvfb() + cls.vdisplay.start() + cls.selenium = WebDriver() + super(BaseLiveServerTestCase, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + cls.selenium.quit() + cls.vdisplay.stop() + super(BaseLiveServerTestCase, cls).tearDownClass() + + def setUp(self): + super(BaseLiveServerTestCase, self).setUp() + self.rest = Api(self.live_server_url + '/api/') + self.account = Account.objects.create(name='orchestra') + self.username = 'orchestra' + self.password = 'orchestra' + self.user = User.objects.create_superuser(username='orchestra', password='orchestra', + email='orchestra@orchestra.org', account=self.account) + + def admin_login(self): + session = SessionStore() + session[SESSION_KEY] = self.user.pk + session[BACKEND_SESSION_KEY] = settings.AUTHENTICATION_BACKENDS[0] + session.save() + ## to set a cookie we need to first visit the domain. + self.selenium.get(self.live_server_url + '/admin/') + self.selenium.add_cookie(dict( + name=settings.SESSION_COOKIE_NAME, + value=session.session_key, # + path='/', + )) + + def rest_login(self): + self.rest.login(username=self.username, password=self.password) + + +def random_ascii(length): + return ''.join([random.choice(string.hexdigits) for i in range(0, length)]).lower() diff --git a/orchestra/utils/time.py b/orchestra/utils/time.py new file mode 100644 index 00000000..ca1f838d --- /dev/null +++ b/orchestra/utils/time.py @@ -0,0 +1,34 @@ +from __future__ import absolute_import + +from datetime import datetime + +from django.utils.timesince import timesince as django_timesince +from django.utils.timezone import is_aware, utc + + +def timesince(d, now=None, reversed=False): + """ Hack to provide second precision under 2 minutes """ + if not now: + now = datetime.now(utc if is_aware(d) else None) + + delta = (d - now) if reversed else (now - d) + s = django_timesince(d, now=now, reversed=reversed) + + if len(s.split(' ')) is 2: + count, name = s.split(' ') + if name in ['minutes', 'minute']: + seconds = delta.seconds % 60 + extension = '%(number)d %(type)s' % {'number': seconds, 'type': 'seconds'} + if int(count) is 0: + return extension + elif int(count) < 2: + s += ', %s' % extension + return s + + +def timeuntil(d, now=None): + """ + Like timesince, but returns a string measuring the time until + the given time. + """ + return timesince(d, now, reversed=True) diff --git a/scripts/container/create.sh b/scripts/container/create.sh new file mode 100755 index 00000000..8d3718de --- /dev/null +++ b/scripts/container/create.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +# This is a helper script for creating a basic LXC container with some convenient packages +# ./create.sh [container_name] + +set -u + +NAME=${1:-orchestra} +CONTAINER="/var/lib/lxc/$NAME/rootfs" +PASSWORD=$NAME +export SUITE="wheezy" + + +[ $(whoami) != 'root' ] && { + echo -e "\nErr. This script should run as root\n" >&2 + exit 1 +} + +lxc-create -h &> /dev/null || { + echo -e "\nErr. It seems like LXC is not installed, run apt-get install lxc\n" >&2 + exit 1 +} + + +lxc-create -n $NAME -t debian + +mount --bind /dev $CONTAINER/dev +mount -t sysfs none $CONTAINER/sys +trap "umount $CONTAINER/{dev,sys}; exit 1;"INT TERM EXIT + + +sed -i "s/\tlocalhost$/\tlocalhost $NAME/" $CONTAINER/etc/hosts +sed -i "s/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/" $CONTAINER/etc/locale.gen +chroot $CONTAINER locale-gen + + +chroot $CONTAINER apt-get install -y --force-yes \ + nano git screen sudo iputils-ping python2.7 python-pip wget curl dnsutils rsyslog + +chroot $CONTAINER apt-get clean + + +sleep 0.1 +umount $CONTAINER/{dev,sys} +trap - INT TERM EXIT diff --git a/scripts/container/deploy.sh b/scripts/container/deploy.sh new file mode 100755 index 00000000..98a26597 --- /dev/null +++ b/scripts/container/deploy.sh @@ -0,0 +1,118 @@ +#!/bin/bash + +# Automated development deployment of django-orchestra + +# This script is safe to run several times, for example in order to upgrade your deployment + + +set -u +bold=$(tput bold) +normal=$(tput sgr0) + + +[ $(whoami) != 'root' ] && { + echo -e "\nErr. This script should run as root\n" >&2 + exit 1 +} + +USER='orchestra' +PASSWORD="orchestra" +HOME="/home/$USER" +PROJECT_NAME='panel' +BASE_DIR="$HOME/$PROJECT_NAME" + + +run () { + echo " ${bold}\$ su $USER -c \"${@}\"${normal}" + su $USER -c "${@}" +} + + +# Create a system user for running Orchestra +useradd orchestra -s "/bin/bash" +echo "$USER:$PASSWORD" | chpasswd +mkdir $HOME +chown $USER.$USER $HOME +sudo adduser $USER sudo + + +CURRENT_VERSION=$(python -c "from orchestra import get_version; print get_version();" 2> /dev/null || false) + +if [[ ! $CURRENT_VERSION ]]; then + # First Orchestra installation + run "git clone https://github.com/glic3rinu/django-orchestra.git ~/django-orchestra" + echo $HOME/django-orchestra/ | sudo tee /usr/local/lib/python2.7/dist-packages/orchestra.pth + sudo cp $HOME/django-orchestra/orchestra/bin/orchestra-admin /usr/local/bin/ + sudo orchestra-admin install_requirements +fi + +if [[ ! -e $BASE_DIR ]]; then + cd $HOME + run "orchestra-admin startproject $PROJECT_NAME" + cd - +fi + +MANAGE="$BASE_DIR/manage.py" + +if [[ ! $(sudo su postgres -c "psql -lqt" | awk {'print $1'} | grep '^orchestra$') ]]; then + # orchestra database does not esists + # Speeding up tests, don't do this in production! + POSTGRES_VERSION=$(psql --version | head -n1 | awk {'print $3'} | cut -d'.' -f1,2) + sudo sed -i "s/^#fsync =\s*.*/fsync = off/" \ + /etc/postgresql/${POSTGRES_VERSION}/main/postgresql.conf + sudo sed -i "s/^#full_page_writes =\s*.*/full_page_writes = off/" \ + /etc/postgresql/${POSTGRES_VERSION}/main/postgresql.conf + + sudo service postgresql restart + sudo python $MANAGE setuppostgres --db_name orchestra --db_user orchestra --db_password orchestra + # Create database permissions are needed for running tests + sudo su postgres -c 'psql -c "ALTER USER orchestra CREATEDB;"' +fi + +if [[ $CURRENT_VERSION ]]; then + # Per version upgrade specific operations + sudo python $MANAGE postupgradeorchestra --no-restart --from $CURRENT_VERSION +else + sudo python $MANAGE syncdb --noinput + sudo python $MANAGE migrate --noinput +fi + +sudo python $MANAGE setupcelery --username $USER --processes 2 + +# Install and configure Nginx web server +run "mkdir $BASE_DIR/static" +run "python $MANAGE collectstatic --noinput" +sudo apt-get install -y nginx uwsgi uwsgi-plugin-python +sudo python $MANAGE setupnginx +sudo service nginx start + +# Apply changes +sudo python $MANAGE restartservices + +# Create a orchestra user +cat <<- EOF | python $MANAGE shell +from django.contrib.auth.models import User +from orchestra.apps.accounts.models import Account +if not User.objects.filter(username=$USER).exists(): + print 'Creating orchestra superuser' + user = User.objects.create_superuser($USER, "'$USER@localhost'", $PASSWORD) + user.account = Account.objects.create(user=user) + user.save() + +EOF + +# Change to development settings +PRODUCTION="from orchestra.conf.production_settings import \*" +DEVEL="from orchestra.conf.devel_settings import \*" +sed -i "s/^$PRODUCTION/# $PRODUCTION/" $BASE_DIR/$PROJECT_NAME/settings.py +sed -i "s/^#\s*$DEVEL/$DEVEL/" $BASE_DIR/$PROJECT_NAME/settings.py + + +cat << EOF + +${bold} + * Admin interface login * + - username: $USER + - password: $PASSWORD +${normal} +EOF diff --git a/scripts/migration/README.md b/scripts/migration/README.md new file mode 100644 index 00000000..ff9bfebb --- /dev/null +++ b/scripts/migration/README.md @@ -0,0 +1,6 @@ +Migration Scripts +================= + +**Warnign, this scripts will not work for you !** + +They are just examples of how I migrated my existing system data to Orchestra. diff --git a/scripts/migration/accounts.sh b/scripts/migration/accounts.sh new file mode 100644 index 00000000..473849b1 --- /dev/null +++ b/scripts/migration/accounts.sh @@ -0,0 +1,17 @@ + +echo "from orchestra.apps.accounts.models import Account" +echo "from orchestra.apps.users.models import User" + +cd /etc/apache2/sites-enabled/ +ls | while read line; do + USERNAME=$(echo $line|sed "s/\.conf//") + SHADOW=$(grep "^$USERNAME:" /var/yp/ypfiles/shadow) + [[ $SHADOW ]] && { + echo "user,__ = User.objects.get_or_create(username='$USERNAME')" + echo "account, __ = Account.objects.get_or_create(user=user)" + echo "user.password = '$(echo $SHADOW|cut -d: -f2)'" + echo "user.account = account" + echo "user.save()" + } +done +cd - &> /dev/null diff --git a/scripts/migration/apache2.py b/scripts/migration/apache2.py new file mode 100644 index 00000000..b6d7d375 --- /dev/null +++ b/scripts/migration/apache2.py @@ -0,0 +1,62 @@ +import re +import glob + + +print "from orchestra.apps.accounts.models import Account" +print "from orchestra.apps.domains.models import Domain" +print "from orchestra.apps.webapps.models import WebApp" +print "from orchestra.apps.websites.models import Website, Content" + + +for conf in glob.glob('/etc/apache2/sites-enabled/*'): + username = conf.split('/')[-1].split('.')[0] + with open(conf, 'rb') as conf: + print "account = Account.objects.get(user__username='%s')" % username + for line in conf.readlines(): + line = line.strip() + if line.startswith(''): + port = 443 + elif line.startswith("ServerName"): + domain = line.split()[1] + name = domain + domains.append("'%s'" % domain) + elif line.startswith("ServerAlias"): + for domain in line.split()[1:]: + domains.append("'%s'" % domain) + elif line.startswith("Alias /fcgi-bin/"): + fcgid = line.split('/')[-1] or line.split('/')[-2] + fcgid = fcgid.split('-')[0] + apps.append((name, fcgid, '/')) + elif line.startswith("Alias /webalizer"): + apps.append(('webalizer', 'webalizer', '/webalizer')) + elif line == '': + if port == 443: + name += '-ssl' + print "# SITE" + print "website, __ = Website.objects.get_or_create(name='%s', account=account, port=%d)" % (name, port) + domains = ', '.join(domains) + print "for domain in [%s]:" % str(domains) + print " try:" + print " domain = Domain.objects.get(name=domain)" + print " except:" + print " domain = Domain.objects.create(name=domain, account=account)" + print " else:" + print " domain.account = account" + print " domain.save()" + print " website.domains.add(domain)" + print "" + for name, type, path in apps: + print "try:" + print " webapp = WebApp.objects.get(account=account, name='%s')" % name + print "except:" + print " webapp = WebApp.objects.create(account=account, name='%s', type='%s')" % (name, type) + print "else:" + print " webapp.type = '%s'" % type + print " webapp.save()" + print "" + print "Content.objects.get_or_create(website=website, webapp=webapp, path='%s')" % path + print '\n' diff --git a/scripts/migration/domains.sh b/scripts/migration/domains.sh new file mode 100644 index 00000000..9729613e --- /dev/null +++ b/scripts/migration/domains.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# GENERATES Python code that will fill your Orchestra database with the existing zone files +# +# DEPENDS on bind9utils +# sudo apt-get install bind9utils +# +# EXAMPLE +# 1) bash bind9-domains.sh /etc/bind/master > bind9-domains.migrate.py +# 2) python manage.py shell < bind9-domains9.migrate.py + + +ZONE_PATH=${1:-/etc/bind/master/} + +echo "from orchestra.apps.domains.models import Domain" +echo "from orchestra.apps.accounts.models import Account" + +echo "account = Account.objects.get(pk=1)" +ERRORS="" +while read name; do + ZONE=$(named-checkzone -D $name ${ZONE_PATH}/$name) + if [[ $? != 0 ]]; then + ERRORS="${ERRORS} $name" + else + for DOMAIN in $(echo "$ZONE" | awk {'print $1'} | uniq); do + echo "try:" + echo " domain = Domain.objects.get(name='${DOMAIN%?}')" + echo "except:" + echo " domain = Domain.objects.create(name='${DOMAIN%?}', account=account)" + echo "" + RECORDS=$(echo "$ZONE" | grep '\sIN\s' | grep "^${DOMAIN}\s") + echo "$RECORDS" | while read record; do + TYPE=$(echo "$record" | awk {'print $4'}) + VALUE=$(echo "$record" | sed "s/.*IN\s[A-Z]*\s*//") + # WARNING This is example code for exclude default records !! + if [[ + ! ( $TYPE == 'SOA' ) && + ! ( $TYPE == 'MX' && $(echo $VALUE | grep 'pangea.org') ) && + ! ( $TYPE == 'A' && $VALUE == '77.246.179.81' ) && + ! ( $TYPE == 'CNAME' && $VALUE = 'web.pangea.org.' ) && + ! ( $TYPE == 'NS' && $(echo $VALUE | grep 'pangea.org') ) + ]]; then + echo "domain.records.get_or_create(type='$TYPE', value='$VALUE')" + fi + done + done + fi +done < <(ls $ZONE_PATH) + +[[ $ERRORS != "" ]] && echo "Not included due to errors:$ERRORS" >& 2 diff --git a/scripts/migration/mailbox.sh b/scripts/migration/mailbox.sh new file mode 100644 index 00000000..94388f4b --- /dev/null +++ b/scripts/migration/mailbox.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# This script assumes accounts.sh has already been executed + +echo "from orchestra.apps.users.models import User" +echo "from orchestra.apps.users.models.roles.mailbox import Mailbox" + +SHADOW="/var/yp/ypfiles/shadow" +BASE_ACCOUNT=1 + +cat $SHADOW | while read line; do + USERNAME=$(echo "$line" | cut -d':' -f1) + PASSWORD=$(echo "$line" | cut -d':' -f2) + echo "try:" + echo " user = User.objects.get(username='$USERNAME')" + echo "except:" + echo " user = User.objects.create(username='$USERNAME', password='$PASSWORD', account_id=$BASE_ACCOUNT)" + echo " Mailbox.objects.create(user=user)" + echo "" + + UNDERSCORED_ACCOUNT_NAME=${USERNAME//*_/} + DOTTED_ACCOUNT_NAME=${USERNAME//*./} + echo "if user.account_id == $BASE_ACCOUNT:" + echo " try:" + echo " account = User.objects.get(username='$UNDERSCORED_ACCOUNT_NAME').account" + echo " user.account = account" + echo " user.save()" + echo " except:" + echo " try:" + echo " account = User.objects.get(username='$DOTTED_ACCOUNT_NAME').account" + echo " user.account = account" + echo " user.save()" + echo " except:" + echo " pass" + echo "" +done diff --git a/scripts/migration/mysql.sh b/scripts/migration/mysql.sh new file mode 100644 index 00000000..8dadda58 --- /dev/null +++ b/scripts/migration/mysql.sh @@ -0,0 +1,11 @@ +#!/bin/bash + + +QUERY="select db,db.user,user.user,password from user left join db on user.user=db.user;" + +mysql mysql -sN -e "$QUERY" | while read line; do + DBNAME=$(echo "$line" | awk {'print $1'}) + OWNER=$(echo "$line" | awk {'print $2'}) + USER=$(echo "$line" | awk {'print $3'}) + PASSWORD=$(echo "$line" | awk {'print $4'}) + if OWNER diff --git a/scripts/migration/virtusertable.sh b/scripts/migration/virtusertable.sh new file mode 100644 index 00000000..212f73a8 --- /dev/null +++ b/scripts/migration/virtusertable.sh @@ -0,0 +1,34 @@ +#!/bin/bash + + +VIRTUALTABLE="/etc/postfix/virtusertable" + + +echo "from orchestra.apps.users import User" +echo "from orchestra.apps.users.roles.mailbox import Address, Mailbox" +echo "from orchestra.apps.domains import Domain" + +cat "$VIRTUALTABLE"|grep -v "^\s*$"|while read line; do + NAME=$(echo "$line" | awk {'print $1'} | cut -d'@' -f1) + DOMAIN=$(echo "$line" | awk {'print $1'} | cut -d'@' -f2) + DESTINATION=$(echo "$line" | awk '{$1=""; print $0}' | sed -e 's/^ *//' -e 's/ *$//') + echo "domain = Domain.objects.get(name='$DOMAIN')" + for PLACE in $DESTINATION; do + if [[ ! $(echo $PLACE | grep '@') ]]; then + echo "try:" + echo " user = User.objects.get(username='$PLACE')" + echo "except:" + echo " print 'User $PLACE does not exists'" + echo "else:" + echo " mailbox, __ = Mailbox.objects.get_or_create(user=user)" + echo " if user.account_id != 1:" + echo " user.account=domain.account" + echo " user.save()" + echo "" + fi + done + echo "address, __ = Address.objects.get_or_create(name='$NAME', domain=domain)" + echo "address.account=domain.account" + echo "address.destination='$DESTINATION'" + echo "address.save()" +done diff --git a/scripts/services/README.md b/scripts/services/README.md new file mode 100644 index 00000000..2a6f1ba1 --- /dev/null +++ b/scripts/services/README.md @@ -0,0 +1,6 @@ +Service Scripts +=============== + +Here you'll find some recipes that I used for installing my servers. + +They are compatible with the backends that ship with Orchestra. diff --git a/scripts/services/apache_full_stack.md b/scripts/services/apache_full_stack.md new file mode 100644 index 00000000..229bd26a --- /dev/null +++ b/scripts/services/apache_full_stack.md @@ -0,0 +1,96 @@ +Apache 2 MPM Event with PHP-FPM, FCGID and SUEXEC on Debian Jessie +================================================================== + +The goal of this setup is having a high-performance state-of-the-art deployment of Apache and PHP while being compatible with legacy applications. + +* Apache Event MPM engine handles requests asynchronously, instead of using a dedicated thread or process per request. + +* PHP-FPM is a FastCGI process manager included in modern versions of PHP. + Compared to FCGID it provides better process management features and enables the OPCache to be shared between workers. + +* FCGID and SuEXEC are used for legacy apps that need older versions of PHP (i.e. PHP 5.2 or PHP 4) + + +*Sources:* + * Source http://wiki.apache.org/httpd/PHP-FPM + + +*Related:* + * [PHP4 on debian](php4_on_debian.md) + * [VsFTPd](vsftpd.md) + * [Webalizer](webalizer.md) + + + +1. Install the machinery + ```bash + apt-get update + apt-get install apache2-mpm-event php5-fpm libapache2-mod-fcgid apache2-suexec-custom php5-cgi + ``` + + +2. Enable some convinient Apache modules + ```bash + a2enmod suexec + a2enmod ssl + a2enmod auth_pam + a2enmod proxy_fcgi + a2emmod userdir + ``` + * TODO compat module + https://httpd.apache.org/docs/trunk/mod/mod_access_compat.html + + +3. Configure `suexec-custom` + ```bash + sed -i "s#/var/www#/home#" /etc/apache2/suexec/www-data + sed -i "s#public_html#webapps#" /etc/apache2/suexec/www-data + ``` + + +4. Create logs directory for virtualhosts + ```bash + mkdir -p /var/log/apache2/virtual/ + chown -R www-data:www-data /var/log/apache2 + ``` + + +5. Restart Apache + ```bash + service apache2 restart + ``` + + +* TODO + libapache2-mod-auth-pam + https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=710770 + + +* ExecCGI + ```bash + + Options +ExecCGI + + ``` + + +TODO CHRoot + https://andrewbevitt.com/tutorials/apache-varnish-chrooted-php-fpm-wordpress-virtual-host/ + + ```bash + echo ' + [vhost] + istemplate = 1 + listen.mode = 0660 + pm.max_children = 5 + pm.start_servers = 1 + pm.min_spare_servers = 1 + pm.max_spare_servers = 2 + ' > /etc/php5/fpm/conf.d/vhost-template.conf + ``` + + ```bash + mkdir -p /var/run/fpm/socks/ + chmod 771 /var/run/fpm/socks + chown orchestra.orchestra /var/run/fpm/socks + ``` diff --git a/scripts/services/bind9.sh b/scripts/services/bind9.sh new file mode 100644 index 00000000..9aeb303a --- /dev/null +++ b/scripts/services/bind9.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +# Installs and confingures bind9 to work with Orchestra + + +apt-get update +apt-get install bind9 + +echo "nameserver 127.0.0.1" > /etc/resolv.conf diff --git a/scripts/services/mailman.sh b/scripts/services/mailman.sh new file mode 100644 index 00000000..e3edf7a4 --- /dev/null +++ b/scripts/services/mailman.sh @@ -0,0 +1,3 @@ +apt-get install mailman + + diff --git a/scripts/services/mysql.sh b/scripts/services/mysql.sh new file mode 100644 index 00000000..71bfee8c --- /dev/null +++ b/scripts/services/mysql.sh @@ -0,0 +1 @@ +apt-get install mysql-server diff --git a/scripts/services/php4_on_debian.md b/scripts/services/php4_on_debian.md new file mode 100644 index 00000000..9538403d --- /dev/null +++ b/scripts/services/php4_on_debian.md @@ -0,0 +1,94 @@ +PHP 4.4.9 for Debian Wheezy / Jessie +==================================== + +**This recipe is for compiling a Debian Wheezy/Jessie compatible version of PHP 4.4.9** + + +1. Debootstrap a Debian Wheezy + ```bash + debootstrap --include=build-essential wheezy php4strap + chroot php4strap + ``` + + +2. Download and install PHP 4.4.9 + ```bash + mkdir /tmp/php4-build + cd /tmp/php4-build + wget http://de.php.net/get/php-4.4.9.tar.bz2/from/this/mirror -O php-4.4.9.tar.bz2 + tar jxf php-4.4.9.tar.bz2 + ``` + + +3. Install PHP building dependencies + ```bash + cat /etc/apt/sources.list | sed "s/^deb /deb-src /" >> /etc/apt/sources.list + apt-get update + apt-get build-dep php5 + ``` + +4. Create some links + ```bash + ln -s /usr/lib/x86_64-linux-gnu/libjpeg.so /usr/lib/ + ln -s /usr/lib/x86_64-linux-gnu/libpng.so /usr/lib/ + ln -s /usr/lib/x86_64-linux-gnu/libexpat.so /usr/lib/ + ln -s /usr/lib/x86_64-linux-gnu/libmysqlclient.so /usr/lib/libmysqlclient.so + ``` + +4. Configure PHP4 + + *Notice that some common features are not enabled, this is because are not supported by related libraries that ship with modern Debian releases* + + ```bash + ./configure --prefix=/usr/local/php4 \ + --enable-force-cgi-redirect \ + --enable-fastcgi \ + --with-config-file-path=/usr/local/etc/php4/cgi \ + --with-gettext \ + --with-jpeg-dir=/usr/local/lib \ + --with-mysql=/usr \ + --with-pear \ + --with-png-dir=/usr/local/lib \ + --with-xml \ + --with-zlib \ + --with-zlib-dir=/usr/include \ + --enable-bcmath \ + --enable-calendar \ + --enable-magic-quotes \ + --enable-sockets \ + --enable-track-vars \ + --enable-mbstring \ + --enable-memory-limit \ + --with-bz2 \ + --enable-dba \ + --enable-dbx \ + --with-iconv \ + --with-mime-magic \ + --disable-shmop \ + --enable-sysvmsg \ + --enable-wddx \ + --with-xmlrpc \ + --enable-yp \ + --with-gd + ``` + +5. Compile and install PHP4 + ```bash + make + make install + strip /usr/local/php4/bin/* + ``` + + +6. Grab the binaries + ```bash + exit + scp -r php4strap/usr/local/php4 root@destination-server:/usr/local/ + ``` + + +7. I needed to install some extra dependecies on my server + ```bash + apt-get install libmysqlclient18 libpng12-0 libjpeg8 + ``` + diff --git a/scripts/services/postfix.md b/scripts/services/postfix.md new file mode 100644 index 00000000..47226145 --- /dev/null +++ b/scripts/services/postfix.md @@ -0,0 +1,12 @@ +apt-get install postfix + + +# http://www.postfix.org/VIRTUAL_README.html#virtual_mailbox +# https://help.ubuntu.com/community/PostfixVirtualMailBoxClamSmtpHowto + + +# http://wiki2.dovecot.org/HowTo/VirtualUserFlatFilesPostfix + + +root@web:~# apt-get install dovecot-core dovecot-imapd dovecot-pop3d dovecot-lmtpd dovecot-sieve + diff --git a/scripts/services/vsftpd.md b/scripts/services/vsftpd.md new file mode 100644 index 00000000..6bde4a11 --- /dev/null +++ b/scripts/services/vsftpd.md @@ -0,0 +1,25 @@ +VsFTPd with System Users +======================== + + +1. Install `vsftpd` + ```bash + apt-get install vsftpd + ``` + + +2. Make some configurations + ```bash + sed -i "s/anonymous_enable=YES/anonymous_enable=NO/" /etc/vsftpd.conf + sed -i "s/#local_enable=YES/local_enable=YES/" /etc/vsftpd.conf + sed -i "s/#write_enable=YES/write_enable=YES" /etc/vsftpd.conf + sed -i "s/#chroot_local_user=YES/chroot_local_user=YES/" /etc/vsftpd.conf + + echo '/bin/false' >> /etc/shells + ``` + + +3. Apply changes + ```bash + /etc/init.d/vsftpd restart + ``` diff --git a/scripts/services/webalizer.md b/scripts/services/webalizer.md new file mode 100644 index 00000000..509bf337 --- /dev/null +++ b/scripts/services/webalizer.md @@ -0,0 +1,21 @@ +Webalizer +========= + + +1. Install `vsftpd` + ```bash + apt-get install webalizer + ``` + + +2. Modify Apache/Nginx log postrotate + ```bash + for i in /home/httpd/webalizer/*.conf; do + file=$(grep ^LogFile "$i" | tr -s ' ' | cut -f2 -d ' ').1 + if [ -f "$file" ]; then + /usr/bin/webalizer -q -c "$i" "$file" 2>&1 \ + # Supress truncating warnings + | grep -v '^Warning: Truncating oversized ' >&2 + fi + done + ``` diff --git a/scripts/tests/setup.sh b/scripts/tests/setup.sh new file mode 100644 index 00000000..05d7a6bb --- /dev/null +++ b/scripts/tests/setup.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +# Setup the test environment + + +apt-get update +apt-get install python-pip iceweasel xvfb +pip install selenium xvfbwrapper + diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..10202805 --- /dev/null +++ b/setup.py @@ -0,0 +1,49 @@ +import os, sys +from distutils.sysconfig import get_python_lib +from setuptools import setup, find_packages + + +# allow setup.py to be run from any path +os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) + +packages = find_packages('.') + +# Dynamically calculate the version based on orchestra.VERSION. +version = __import__('orchestra').get_version() + + +setup( + name = "django-orchestra", + version = version, + author = "Marc Aymerich", + author_email = "marcay@pangea.org", + url = "http://orchestra.pangea.org", + license = "GPLv3", + description = "A framework for building web hosting control panels", + long_description = ( + "There are a lot of widely used open source hosting control panels, " + "however none of them seems apropiate when you already have a production " + "service infrastructure or simply you want a particular architecture.\n" + "The goal of this project is to provide the tools for easily build a fully " + "featured control panel that fits any service architecture."), + include_package_data = True, + scripts=['orchestra/bin/orchestra-admin'], + packages = packages, + classifiers = [ + 'Development Status :: 1 - Alpha', + 'Environment :: Web Environment', + 'Framework :: Django', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', + 'Topic :: Internet :: WWW/HTTP :: Site Management', + 'Topic :: Software Development :: Libraries :: Application Frameworks', + 'Topic :: Software Development :: Libraries :: Python Modules', + 'Topic :: Server/Management', + ] +)