Changeset - fd703abd5a2d
[Not reviewed]
0 1 0
Branko Majic (branko) - 6 years ago 2020-05-12 18:35:39
branko@majic.rs
MAR-149: Updated usage documentation:

- Add warning that Majic Ansible Roles support only Python 3.
- Updated the Django Wiki deployment to use Python 3.
- Upgraded the Django Wiki deployment for use with Python 3.
- Removed one stale warning regarding compat links for MariDB devel
files (leftover from Jessie).
1 file changed with 111 insertions and 78 deletions:
0 comments (0 inline, 0 general)
docs/usage.rst
Show inline comments
 
@@ -19,24 +19,37 @@ small infrastructure of your own.
 
Some roles are suited for one-off operations during installation, like the
 
``preseed`` and ``bootstrap``, while some are better suited for periodic runs
 
for maintaining the users and integrity of the system.
 

	
 
By the end of following the instructions, you will have the following:
 

	
 
* Ansible server, used as controller for configuring and managing the
 
  remaining servers.
 
* Communications server, providing the LDAP, mail, and XMPP services.
 
* Web server, providing the web services.
 
* Backup server, used for storing all of the backups.
 

	
 
.. warning::
 
   Majic Ansible Roles support *only* Python 3 - both on the
 
   controller side and on the managed servers side.
 

	
 
   It is important to make sure that both the controller Python
 
   virtual environment used for Ansible *and* the interpreter for
 
   remote servers are *both* set-up to use Python 3.
 

	
 
   Python 3 is specified explicitly during virtual environment
 
   creation and in ``ansible.cfg`` configuration file
 
   (``interpreter_python`` option under ``defaults`` section).
 

	
 

	
 

	
 
Pre-requisites
 
--------------
 

	
 
For the set-up outlined in this usage guide you'll need the following:
 

	
 
* One server where Ansible will be installed at. Debian Stretch will
 
  be installed on top of this server. The server will be set-up
 
  manually (this is currently out of scope for the *Majic Ansible
 
  Roles* automated set-up).
 
* Three servers where the services will be set-up. All servers must be able to
 
  communicate over network with each-other, the Ansible servers, and with
 
@@ -107,38 +120,38 @@ Start-off by installing the operating system on the Ansible server:
 

	
 
20. Finalise the server install, and remove the installation media from server.
 

	
 

	
 
Installing required packages
 
----------------------------
 

	
 
With the operating system installed, it is necessary to install a couple of
 
packages, and to prepare the environment a bit on the Ansible server:
 

	
 
1. Install the necessary system packages (using the ``root`` account)::
 

	
 
     apt-get install -y virtualenv virtualenvwrapper git python-pip python-dev libffi-dev libssl-dev
 
     apt-get install -y virtualenv virtualenvwrapper git python3-pip python3-dev libffi-dev libssl-dev
 

	
 

	
 
2. Set-up the virtual environment (using the ``ansible`` account):
 

	
 
   .. warning::
 
      If you are already logged-in as user ``ansible`` in the server, you will
 
      need to log-out and log-in again in order to be able to use
 
      ``virtualenvwrapper`` commands!
 

	
 
   ::
 

	
 
     mkdir ~/mysite/
 
     mkvirtualenv -a ~/mysite/ mysite
 
     mkvirtualenv -p /usr/bin/python3 -a ~/mysite/ mysite
 
     pip install -U pip setuptools
 
     pip install 'ansible~=2.9.0' dnspython
 

	
 
.. warning::
 
   The ``dnspython`` package is important since it is used internally via
 
   ``dig`` lookup plugin.
 

	
 

	
 
Cloning the *Majic Ansible Roles*
 
---------------------------------
 

	
 
With most of the software pieces in place, the only missing thing is the Majic
 
@@ -175,24 +188,25 @@ First of all, let's set-up some basic directory structure and configuration:
 
      <http://docs.ansible.com/ansible/latest/become.html#becoming-an-unprivileged-user>`_
 
      and other alternatives to this.
 

	
 
   :file:`~/mysite/ansible.cfg`
 

	
 
   ::
 

	
 
     [defaults]
 

	
 
     roles_path=/home/ansible/majic-ansible-roles/roles:/home/ansible/mysite/roles
 
     force_handlers = True
 
     inventory = /home/ansible/mysite/hosts
 
     interpreter_python = /usr/bin/python3
 

	
 
     [ssh_connection]
 
     pipelining = True
 

	
 
2. Create directory where retry files will be stored at (so they woudln't
 
   pollute your home directory)::
 

	
 
     mkdir ~/mysite/retry
 

	
 
3. Create the inventory file.
 

	
 
   :file:`~/mysite/hosts`
 
@@ -309,25 +323,25 @@ Installing the servers with preseed files
 

	
 
You have your preseed files now, so you can go ahead and install the
 
servers ``comms.example.com``, ``www.example.com``, and
 
``bak.example.com`` using them with network install CD. Have a look at
 
`Debian instructions
 
<https://www.debian.org/releases/stretch/amd64/apbs02.html.en>`_ for
 
more details.
 

	
 
If you need to, you can easily serve the preseed files from the Ansible server
 
with Python's built-in HTTP server::
 

	
 
  cd ~/mysite/preseed_files/
 
  python -m SimpleHTTPServer 8000
 
  python -m http.server 8000
 

	
 
Then you can point installer to the preseed file selecting the
 
``Advanced options -> Automated install`` (don't press ``ENTER`` yet),
 
then pressing ``TAB``, and appending the following at the end (just
 
fill-in the correct hostname - ``comms``, ``www``, or ``bak``)::
 

	
 
  url=http://ansible.example.com:8000/HOSTNAME.example.com.cfg
 

	
 

	
 
Bootstrapping servers for Ansible set-up
 
----------------------------------------
 

	
 
@@ -952,25 +966,25 @@ external addresses on those two servers goes through our anti-virus scanner.
 
      # here.Beware the IP spoofing, though! Don't forget to change the bellow
 
      # IP for your server ;)
 
      smtp_allow_relay_from:
 
        - 10.32.64.20
 
        - 10.32.64.23
 

	
 
4. Let's apply the changes::
 

	
 
     workon mysite && ansible-playbook playbooks/site.yml
 

	
 
5. After this you may want to test out sending mail via web or backup server's
 
   local SMTP to the root user (to see if the aliasing works), and to some
 
   external mail address to check if forwarding works correctly too. Run some
 
   external mail address to check if forwarding works correctly too. Run
 
   something similar to the following on your web server::
 

	
 
     swaks --to root@localhost --server localhost
 
     swaks --to YOUR_MAIL --server localhost
 

	
 
   If all went well, you should be able to see a new mail in John Doe's mailbox,
 
   as well as your own mailbox.
 

	
 

	
 
Adding XMPP server
 
------------------
 

	
 
@@ -1454,25 +1468,24 @@ Before we start, here is a couple of useful pointers regarding the
 
        with_items:
 
          - /var/www/tbg.example.com/thebuggenie-{{ tbg_version }}/
 
          - /var/www/tbg.example.com/thebuggenie-{{ tbg_version }}/public/
 
          - /var/www/tbg.example.com/thebuggenie-{{ tbg_version }}/core/config/
 

	
 
      - name: Create symbolic link to TBG application
 
        file:
 
          src: "/var/www/tbg.example.com/thebuggenie-{{ tbg_version }}/public"
 
          path: "/var/www/tbg.example.com/htdocs"
 
          state: link
 
          owner: "admin-tbg_example_com"
 
          group: "web-tbg_example_com"
 
          mode: 02750
 
        become: yes
 
        become_user: admin-tbg_example_com
 

	
 
      - name: Install TBG dependencies
 
        composer:
 
          command: install
 
          working_dir: "/var/www/tbg.example.com/thebuggenie-{{ tbg_version }}"
 
        become: yes
 
        become_user: admin-tbg_example_com
 

	
 
      - name: Deploy database configuration file for TBG
 
        copy:
 
@@ -1660,56 +1673,57 @@ on the safe side:
 
   :file:`~/mysite/roles/wiki/meta/main.yml`
 
   ::
 

	
 
      ---
 

	
 
      dependencies:
 
        - role: wsgi_website
 
          fqdn: wiki.example.com
 
          # In many cases you need to have some development packages available
 
          # in order to build Python packages installed via pip
 
          packages:
 
            - build-essential
 
            - python-dev
 
            - python3-dev
 
            - libjpeg62-turbo
 
            - libjpeg-dev
 
            - libzip-dev
 
            - libtiff-dev
 
            - libfreetype6-dev
 
            - liblcms2-dev
 
            - libwebp-dev
 
            - libopenjp2-7-dev
 
            - libmariadb-client-lgpl-dev
 
            - libmariadb-client-lgpl-dev-compat
 
            - libpng16-16
 
            - libpng-dev
 
            - libmariadb-dev
 
            - libmariadb-dev-compat
 
          # Specify that Python 3 should be used for the application
 
          python_version: 3
 
          # Here we specify that anything accessing our website with "/static/"
 
          # URL should be treated as request to a static file, to be served
 
          # directly by Nginx instead of the WSGI server.
 
          static_locations:
 
            - /static/
 
          # Again, not mandatory, but it is good to have some sort of policy
 
          # for assigning UIDs.
 
          uid: 2001
 
          admin_uid: 3001
 
          # These are additional packages that should be installed in the
 
          # virtual environment.
 
          virtualenv_packages:
 
            - pillow
 
            - django==1.8.13
 
            - wiki
 
            - MySQL-python
 
            - django~=2.2.0
 
            - wiki~=0.5.0
 
            - mysqlclient
 
          # This is the name of the WSGI application to
 
          # serve. wiki_example_com.wsgi will be the Python "module" that is
 
          # accesed, while application is the object instantiated within it (the
 
          # application itself). The module is referenced relative to the code
 
          # directory (in our case /var/www/wiki.example.com/code/).
 
          wsgi_application: wiki_example_com.wsgi:application
 
          # Specify explicitly requirements for installing Gunicorn.
 
          wsgi_requirements:
 
            - gunicorn==20.0.4
 
        - role: database
 
          db_name: wiki
 
          db_password: wiki
 

	
 
3. Let's create a dedicated private key/certificate pair for the wiki website:
 

	
 
   1. Create new template for ``certtool``:
 

	
 
      :file:`~/mysite/tls/wiki.example.com_https.cfg`
 
      ::
 

	
 
         organization = "Example Inc."
 
@@ -1762,25 +1776,24 @@ on the safe side:
 
          - urls.py
 
        notify:
 
          - Restart wiki
 

	
 
      - name: Deploy project database and deploy static files
 
        django_manage:
 
          command: "{{ item }}"
 
          app_path: "/var/www/wiki.example.com/code/"
 
          virtualenv: "/var/www/wiki.example.com/virtualenv/"
 
        become: yes
 
        become_user: admin-wiki_example_com
 
        with_items:
 
          - syncdb
 
          - migrate
 
          - collectstatic
 

	
 
      - name: Deploy the superadmin creation script
 
        copy:
 
          src: "create_superadmin.py"
 
          dest: "/var/www/wiki.example.com/code/create_superadmin.py"
 
          owner: admin-wiki_example_com
 
          group: web-wiki_example_com
 
          mode: 0750
 

	
 
      - name: Create initial superuser
 
@@ -1803,152 +1816,176 @@ on the safe side:
 
          state: restarted
 

	
 
5. There is a couple of files that we are deploying through the above
 
   tasks. Let's create them as well.
 

	
 
   :file:`~/mysite/roles/wiki/files/settings.py`
 
   ::
 

	
 
      """
 
      Django settings for wiki_example_com project.
 

	
 
      For more information on this file, see
 
      https://docs.djangoproject.com/en/1.6/topics/settings/
 
      https://docs.djangoproject.com/en/2.2/topics/settings/
 

	
 
      For the full list of settings and their values, see
 
      https://docs.djangoproject.com/en/1.6/ref/settings/
 
      https://docs.djangoproject.com/en/2.2/ref/settings/
 
      """
 

	
 
      # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
 
      import os
 
      BASE_DIR = os.path.dirname(os.path.dirname(__file__))
 

	
 
      from django.urls import reverse_lazy
 

	
 
      # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
 
      BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 

	
 
      # Quick-start development settings - unsuitable for production
 
      # See https://docs.djangoproject.com/en/1.6/howto/deployment/checklist/
 
      # See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
 

	
 
      # SECURITY WARNING: keep the secret key used in production secret!
 
      SECRET_KEY = 'l^q+t$7h$ebls)v34+w9m9v4$n+^(9guxqntu&#cc4m&lfd-6_'
 
      SECRET_KEY = '8rok13az%bqtb=ya&s9sia_x*@@rhd9a%g=!6nh4tb!g14rlt^'
 

	
 
      # SECURITY WARNING: don't run with debug turned on in production!
 
      DEBUG = False
 

	
 
      TEMPLATE_DEBUG = False
 

	
 
      ALLOWED_HOSTS = ["wiki.example.com", "localhost"]
 

	
 

	
 
      # Application definition
 

	
 
      INSTALLED_APPS = (
 
      INSTALLED_APPS = [
 
          'django.contrib.admin',
 
          'django.contrib.auth',
 
          'django.contrib.contenttypes',
 
          'django.contrib.sessions',
 
          'django.contrib.messages',
 
          'django.contrib.staticfiles',
 
          'django.contrib.sites',
 
          'django.contrib.humanize',
 
          'django_nyt',
 
          'django.contrib.sites.apps.SitesConfig',
 
          'django.contrib.humanize.apps.HumanizeConfig',
 
          'django_nyt.apps.DjangoNytConfig',
 
          'mptt',
 
          'sekizai',
 
          'sorl.thumbnail',
 
          'wiki',
 
          'wiki.plugins.attachments',
 
          'wiki.plugins.notifications',
 
          'wiki.plugins.images',
 
          'wiki.plugins.macros',
 
      )
 

	
 
      MIDDLEWARE_CLASSES = (
 
          'wiki.apps.WikiConfig',
 
          'wiki.plugins.attachments.apps.AttachmentsConfig',
 
          'wiki.plugins.notifications.apps.NotificationsConfig',
 
          'wiki.plugins.images.apps.ImagesConfig',
 
          'wiki.plugins.macros.apps.MacrosConfig',
 
      ]
 

	
 
      MIDDLEWARE = [
 
          'django.middleware.security.SecurityMiddleware',
 
          'django.contrib.sessions.middleware.SessionMiddleware',
 
          'django.middleware.common.CommonMiddleware',
 
          'django.middleware.csrf.CsrfViewMiddleware',
 
          'django.contrib.auth.middleware.AuthenticationMiddleware',
 
          'django.contrib.messages.middleware.MessageMiddleware',
 
          'django.middleware.clickjacking.XFrameOptionsMiddleware',
 
      )
 
      ]
 

	
 
      ROOT_URLCONF = 'wiki_example_com.urls'
 

	
 
      TEMPLATES = [
 
          {
 
              'BACKEND': 'django.template.backends.django.DjangoTemplates',
 
              'DIRS': [],
 
              'APP_DIRS': True,
 
              'OPTIONS': {
 
                  'context_processors': [
 
                      'django.contrib.auth.context_processors.auth',
 
                      'django.template.context_processors.debug',
 
                      'django.template.context_processors.i18n',
 
                      'django.template.context_processors.media',
 
                      'django.template.context_processors.request',
 
                      'django.template.context_processors.static',
 
                      'django.template.context_processors.tz',
 
                      'django.contrib.messages.context_processors.messages',
 
                      "sekizai.context_processors.sekizai",
 
                  ],
 
              },
 
          },
 
      ]
 

	
 
      WSGI_APPLICATION = 'wiki_example_com.wsgi.application'
 

	
 

	
 
      # Database
 
      # https://docs.djangoproject.com/en/1.6/ref/settings/#databases
 
      # https://docs.djangoproject.com/en/2.2/ref/settings/#databases
 

	
 
      DATABASES = {
 
          'default': {
 
              'ENGINE': 'django.db.backends.mysql',
 
              'NAME': 'wiki',
 
              'USER': 'wiki',
 
              'PASSWORD': 'wiki',
 
              'HOST': '127.0.0.1',
 
              'PORT': '3306',
 
          }
 
      }
 

	
 
      # Password validation
 
      # https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
 

	
 
      AUTH_PASSWORD_VALIDATORS = [
 
          {
 
              'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
 
          },
 
          {
 
              'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
 
          },
 
          {
 
              'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
 
          },
 
          {
 
              'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
 
          },
 
      ]
 

	
 

	
 
      # Internationalization
 
      # https://docs.djangoproject.com/en/1.6/topics/i18n/
 
      # https://docs.djangoproject.com/en/2.2/topics/i18n/
 

	
 
      LANGUAGE_CODE = 'en-us'
 

	
 
      TIME_ZONE = 'Europe/Stockholm'
 

	
 
      USE_I18N = True
 

	
 
      USE_L10N = True
 

	
 
      USE_TZ = True
 

	
 

	
 
      # Static files (CSS, JavaScript, Images)
 
      # https://docs.djangoproject.com/en/1.6/howto/static-files/
 
      # https://docs.djangoproject.com/en/2.2/howto/static-files/
 

	
 
      STATIC_URL = '/static/'
 

	
 
      STATIC_ROOT = '/var/www/wiki.example.com/htdocs/static'
 

	
 
      from django.conf import settings
 

	
 
      TEMPLATE_CONTEXT_PROCESSORS = settings.TEMPLATE_CONTEXT_PROCESSORS + (
 
          "django.core.context_processors.debug",
 
          "django.core.context_processors.request",
 
          "sekizai.context_processors.sekizai",
 
      )
 
      SITE_ID = 1
 

	
 
      SITE_ID=1
 
      LOGIN_REDIRECT_URL = reverse_lazy('wiki:get', kwargs={'path': ''})
 

	
 
   :file:`~/mysite/roles/wiki/files/urls.py`
 
   ::
 

	
 
      from django.conf.urls import patterns, include, url
 
      from wiki.urls import get_pattern as get_wiki_pattern
 
      from django_nyt.urls import get_pattern as get_nyt_pattern
 

	
 
      from django.contrib import admin
 
      admin.autodiscover()
 

	
 
      urlpatterns = patterns('',
 
          # Examples:
 
          # url(r'^$', 'wiki_example_com.views.home', name='home'),
 
          # url(r'^blog/', include('blog.urls')),
 
      from django.urls import path, include
 

	
 
          url(r'^admin/', include(admin.site.urls)),
 
      )
 
      urlpatterns = [
 
          path('admin/', admin.site.urls),
 
          path('notifications/', include('django_nyt.urls')),
 
          path('', include('wiki.urls'))
 
      ]
 

	
 
      urlpatterns += patterns('',
 
          (r'^notifications/', get_nyt_pattern()),
 
          (r'', get_wiki_pattern())
 
      )
 

	
 
   :file:`~/mysite/roles/wiki/files/create_superadmin.py`
 
   ::
 

	
 
      #!/usr/bin/env python
 

	
 
      import os
 
      from django import setup
 
      os.environ['DJANGO_SETTINGS_MODULE']='wiki_example_com.settings'
 
      setup()
 
      from django.conf import settings
 
      from django.contrib.auth.models import User
 
@@ -1970,33 +2007,24 @@ on the safe side:
 
        become: yes
 
        roles:
 
          - common
 
          - ldap_client
 
          - mail_forwarder
 
          - web_server
 
          - database_server
 
          - tbg
 
          - wiki
 

	
 
7. Apply the changes:
 

	
 
   .. warning::
 

	
 
      Due to `Debian Bug 766996
 
      <https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=766996>`_ Majic Ansible
 
      Roles try to detect if you install ``libmariadb-client-lgpl-dev-compat``
 
      package, and create symbolic link from ``/usr/local/bin/mysql_config`` to
 
      ``/usr/bin/mariadb_config`` automatically. Otherwise the MySQL-python
 
      package will fail to build due to missing ``mysql_config`` binary.
 

	
 
   ::
 

	
 
     workon mysite && ansible-playbook playbooks/site.yml
 

	
 
8. At this point Django Wiki has been installed, and you should be able to open
 
   the URL https://wiki.example.com/ (if you open http://wiki.example.com/ , you
 
   will be redirected to the HTTPS URL) and log-in into *Django Wiki* with
 
   username ``admin`` and password ``admin``.
 

	
 

	
 
Backups, backups, backups!
 
--------------------------
 
@@ -2228,25 +2256,30 @@ Genie*. So let's fix that one.
 
   ``backup_client`` role deals with basic set-up of backup client and its
 
   configuration, the ``backup`` role is used to define what should be
 
   backed-up. It is important to define unique filename for the backup patterns
 
   file. Take into account that you can use pretty much any globbing pattern
 
   supported by Duplicity.
 

	
 
   .. warning::
 

	
 
      Make sure the addition is properly aligned in the yaml file to previous
 
      role dependency definitions.
 

	
 
   :file:`~/mysite/roles/tbg/meta/main.yml`
 
   ::
 

	
 
   .. Small workaround for Sphinx not preserving leading spaces in
 
      case all lines have the same amount of leading spaces.
 

	
 
   .. code-block:: none
 
      :name: sphinx_workaround
 

	
 
        - role: backup
 
          when: enable_backup
 
          backup_patterns_filename: "tbg"
 
          backup_patterns:
 
            - "/var/www/tbg.example.com/files"
 

	
 
2. Apply the changes::
 

	
 
     workon mysite && ansible-playbook playbooks/site.yml
 

	
 
3. Now rerun the backup on server ``www.example.com`` (as root). If you haven't
0 comments (0 inline, 0 general)