Django Configuration
General Information
Configuring the Django Web Framework and its dependencies.
The EXAMPLE sections of code end up building an inventory website.
Checklist
Database: MariaDB
Configuring the MariaDB Database.
- Configure MariaDB to listen only on localhost
vim /etc/my.cnf [mysqld] bind-address=127.0.0.1
- Start/Enable the database
systemctl enable mariadb systemctl start mariadb
- Run secure setup
mysql_secure_installation
- Prompts for the following:
- Current password for root (should be none, just press enter)
- Set database root password
- Remove anonymous users
- Disallow root logins remotely
- Remove test databases
- Reload privilege tables
- Connect to the database
mysql -u root -p
- Create your project's database
create database myprojecthere character set utf8;
- Create a database user that Django will use
create user appuserhere@localhost identified by 'PASSWORDHERE';
- Grant permissions for the app user on your project's database
grant all privileges on myprojecthere.* to appuserhere@localhost;
- Flush privileges
flush privileges;
Django
Configuring Django.
- Verify django works
python >>> import django >>> print(django.get_version()) >>> exit()
Project/App Setup
- Create a directory to store the project
mkdir /home/django cd /home/django
- Create a new Django project
django-admin startproject myprojecthere cd /home/django/myprojecthere
- Create a new Django application inside of the project
python manage.py startapp myapphere
- Edit project settings (/home/django/myprojecthere/myprojecthere/settings.py)
# Allowed Referrer Hosts (hostname and any cnames) ALLOWED_HOSTS = ['djangoserver.mycorps.domain.org','myapp.mycorps.domain.org'] # Application definition (add the application, the rest are built in) INSTALLED_APPS = [ # APPNAME.apps.APPCLASSNAME -> see ../myapphere/apps.py 'myapphere.apps.MyapphereConfig', # add your application here 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] # Static files (CSS, JavaScript, Images) #https://docs.djangoproject.com/en/1.11/howto/static-files/ STATIC_URL = '/static/' STATIC_ROOT = '/var/www/static/'
Database Setup
- Edit project settings (/home/django/myprojecthere/myprojecthere/settings.py)
# Database Configuration # https://docs.djangoproject.com/en/1.11/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'myprojecthere', 'USER': 'appuserhere', 'PASSWORD': 'PASSWORDHERE', 'HOST': '127.0.0.1', 'PORT': '3306', 'OPTIONS': { 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", }, } }
- Run the initial migrate command to create database tables for built in apps that come with Django
cd /home/django/myprojecthere python manage.py migrate
- Describe your data models (objects that will be stored in the database) (/home/django/myprojecthere/myapphere/models.py) EXAMPLE
# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models # Asset Inventory Example class AssetEntry(models.Model): ##-- Tuples for Use in Field Choices --## #asset_type choices DEVICE_TYPES = ( ('Firewalls', 'Firewalls'), ('Routers', 'Routers'), ('Servers', 'Servers'), ('Storage', 'Storage'), ('Switches', 'Switches'), ('Workstations', 'Workstations'), ) # asset_env choices ENV_NAMES = ( ('Dev', 'Development'), ('Test', 'Testing'), ('Prod', 'Production'), ) # Linux OS List OS_NAMES_LINUX = ( ('CentOS 6', 'CentOS 6'), ('CentOS 7', 'CentOS 7'), ('RHEL 6', 'Red Hat Enterprise Linux 6'), ('RHEL 7', 'Red Hat Enterprise Linux 7'), ('Ubuntu 16.04', 'Ubuntu 16.04'), ('Ubuntu 18.04', 'Ubuntu 18.04'), ) # Windows OS List OS_NAMES_WINDOWS = ( ('Win 2008', 'Windows 2008'), ('Win 2012', 'Windows 2012'), ('Win 2016', 'Windows 2016'), ('Win 7', 'Windows 7'), ('Win 10', 'Windows 10'), ) # Other OS List OS_NAMES_OTHER = ( ('Cisco IOS', 'Cisco IOS'), ('Extreme XOS', 'Extreme XOS'), ('Juniper JunOS', 'Juniper JunOS'), ('NA', 'None'), ('Other', 'Other'), ('VMware ESXi', 'VMware ESXi'), ) # Combined OS List - for asset_os choices OS_NAMES = OS_NAMES_LINUX + OS_NAMES_WINDOWS + OS_NAMES_OTHER # asset_hardware choices HW_TYPES = ( ('Virtual', 'Virtual'), ('Physical', 'Physical'), ) ##-- AssetEntry Fields --## asset_name = models.CharField('Name', max_length=50) asset_type = models.CharField('Device Type', max_length=15, choices=DEVICE_TYPES) asset_description = models.CharField('Description', max_length=100) asset_env = models.CharField('Environment', max_length=15, choices=ENV_NAMES) asset_os = models.CharField('OS Name/Version', max_length = 20, choices=OS_NAMES) asset_hardware = models.CharField('Hardware: Virtual or Physical', max_length = 15, choices=HW_TYPES) # Object representation def __str__(self): return self.asset_name
- Stage changes to the database
cd /home/django/myprojecthere python manage.py makemigrations myapphere
- Make changes to the database
cd /home/django/myprojecthere python manage.py migrate
Logging Setup
- Edit project settings (/home/django/myprojecthere/myprojecthere/settings.py)
# Logging Configuration LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'verbose': { 'format' : "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s", 'datefmt' : "%d/%b/%Y %H:%M:%S", }, 'simple': { 'format': '%(levelname)s %(message)s', }, }, 'handlers': { 'file': { 'level': 'INFO', 'class': 'logging.handlers.RotatingFileHandler', 'filename': '/var/log/myprojecthere/myapphere.log', 'maxBytes': 500000000, # 500 MB 'backupCount': 10, 'formatter': 'verbose', }, }, 'loggers': { 'django': { 'handlers': ['file'], 'level': 'INFO', 'propagate': True, }, 'myapphere': { 'handlers': ['file'], 'level': 'INFO', }, }, } ####---- End of Logging Config ----####
- Create logging directory and setup ownership/permissions
mkdir /var/log/myprojecthere chown :apache /var/log/myprojecthere chmod g+rwxs /var/log/myprojecthere
Admin Interface
- Create an admin user
python manage.py createsuperuser
- Make your models/objects available for editing in the admin portal (/home/django/myprojecthere/myapphere/admin.py)
# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.contrib import admin # Register your models here. # Make Class Available in Admin Portal (see /home/django/myprojecthere/myapphere/models.py) from .models import AssetEntry admin.site.register(AssetEntry)
URLs/Views
A URL+View will generate a web page.
URLs
Project Level URLs File
- Edit project URLs (/home/django/myprojecthere/myprojecthere/urls.py)
# add the "import include" part or you won't be able to refer to app level url files from django.conf.urls import url, include from django.contrib import admin # URL Regex Patterns - Map URIs to App's url file for additional matching urlpatterns = [ # No URI following site name (default app to use) url(r'^$', include('myapphere.urls')), # URI for app's name url(r'^myapphere/', include('myapphere.urls')), # Included /admin URI url(r'^admin/', admin.site.urls), ]
App Level URLs File
- Create a new App level URLs file (/home/django/myprojecthere/myapphere/urls.py) EXAMPLE
# urls.py - App URLs Map to App Views from django.conf.urls import url from . import views urlpatterns = [ # "/myapphere/" beginning is implied because we are in the "myapphere" urls file already # /myapphere/ - All Devices, All Environments (the index) url(r'^$', views.index, name='index'), # /myapphere/stats - All Devices, Statistics url(r'^stats/$', views.stats, name='stats'), # /myapphere/env_name - All Devices, Specific Environment url(r'^(?P<env_name>(dev|test|prod))/$', views.env, name='env'), # /myapphere/device_type/ - Specific Device, All Environments url(r'^(?P<device_type>(firewalls|routers|servers|storage|switches|workstations))/$', views.asset_type_all_env, name='asset_type_all_env'), # /myapphere/device_type/env_name - Specific Device, Specific Environment url(r'^(?P<device_type>(firewalls|routers|servers|storage|switches|workstations))/(?P<env_name>(dev|test|prod))/$', views.type_env, name='type_env'), # /myapphere/<asset_hardware> - Physical or Virtual url(r'^(?P<asset_hardware>(physical|virtual))/$', views.asset_hardware, name='asset_hardware'), ]
Views
- Edit the App Views that are loaded depending upon URL match(/home/django/myprojecthere/myapphere/views.py) EXAMPLE
# views.py - URLs are mapped to views # -*- coding: utf-8 -*- from __future__ import unicode_literals # Render http response+templates from django.shortcuts import render # Object or 404 from django.shortcuts import get_object_or_404 # HTTP Response from django.http import HttpResponse # Logging import logging logger = logging.getLogger(__name__) # Your Apps Model from .models import AssetEntry # /myapphere/ - All Devices, All Environments (the index) def index(request): # Get all asset objects all_asset_list = AssetEntry.objects.order_by('asset_type', 'asset_env', 'asset_name') # Get number of assets in list number_assets = len(all_asset_list) # Map template variable names (1st) to Python variables (2nd) for use in html templates context = { 'all_asset_list': all_asset_list, 'asset_count': number_assets } # Logging logger.info("Rendering index.html - All Devices, All Environments") # Render the http request, using the template, passing the context return render(request, 'myapphere/index.html', context) # /myapphere/stats - All Devices, Statistics def stats(request): # Get all asset objects all_asset_list = AssetEntry.objects.order_by('asset_type', 'asset_env', 'asset_name') # Get all asset device types asset_type_list = [key for key,value in AssetEntry.DEVICE_TYPES] # Get all os names, Linux only, Windows only os_list = [key for key,value in AssetEntry.OS_NAMES] os_list_linux = [key for key,value in AssetEntry.OS_NAMES_LINUX] os_list_windows = [key for key,value in AssetEntry.OS_NAMES_WINDOWS] # Log for debug purposes #for key in os_list: # logger.info("Field names are: " + key) #- Build Statistics -# # Initialize Stats list (will be a list of dictionaries) stats_list = [] ####---- Asset Counts Table: Grand Total Row ----#### sysstats_grand_total = 0 sysstats_grand_physical = 0 sysstats_grand_physical_linux = 0 sysstats_grand_physical_windows = 0 sysstats_grand_physical_other = 0 sysstats_grand_virtual = 0 sysstats_grand_virtual_linux = 0 sysstats_grand_virtual_windows = 0 sysstats_grand_virtual_other = 0 sysstats_grand_vmware = 0 # Calculate stats for each type of device for device_type in asset_type_list: # Initialize an empty device stats list device_stats = [] # Add node to the device_stats list if a node from the all_asset_list matches the current device type for node in all_asset_list: if ''.join(node.asset_type.lower().split()) == device_type.lower(): device_stats.append(node) ####---- Asset Counts Table ----#### # Total Count count_total = len(device_stats) #- Add to grand total sysstats_grand_total += count_total # Physical and Virtual Counts count_physical = 0 count_physical_linux = 0 count_physical_windows = 0 count_physical_other = 0 count_virtual = 0 count_virtual_linux = 0 count_virtual_windows = 0 count_virtual_other = 0 for node in device_stats: if node.asset_hardware == "Physical": count_physical += 1 if node.asset_os in os_list_linux: # Physical+Linux Count count_physical_linux += 1 elif node.asset_os in os_list_windows: # Physical+Windows Count count_physical_windows += 1 else: # Physical+Other OS count_physical_other += 1 elif node.asset_hardware == "Virtual": # Virtual Count count_virtual += 1 if node.asset_os in os_list_linux: # Virtual+Linux Count count_virtual_linux += 1 elif node.asset_os in os_list_windows: # Virtual+Windows Count count_virtual_windows += 1 else: # Virtual+Other OS count_virtual_other += 1 #- Add to grand total physical sysstats_grand_physical += count_physical sysstats_grand_physical_linux += count_physical_linux sysstats_grand_physical_windows += count_physical_windows sysstats_grand_physical_other += count_physical_other #- Add to grand total virtual sysstats_grand_virtual += count_virtual sysstats_grand_virtual_linux += count_virtual_linux sysstats_grand_virtual_windows += count_virtual_windows sysstats_grand_virtual_other += count_virtual_other ####---- OS Counts Table ----#### # Initialize a new empty OS Counts List os_counts = [] # Add all the os names and an initial count of 0 for name in os_list: os_counts.append({ 'os_name': name, 'count': 0 }) # Check each node in the devices specific stats for node in device_stats: # Compare node's OS against each os name in the os_counts list and increment count if a match is found for os_entry in os_counts: if os_entry['os_name'] == node.asset_os: # Increment count value on an os name match os_entry['count'] += 1 ####---- After All Stats Calculations: Add Device Stats to stats_list ----#### # Add device statistics to stats_list stats_list.append({ 'asset_type': device_type, 'total': count_total, 'physical': count_physical, 'physical_linux': count_physical_linux, 'physical_windows': count_physical_windows, 'physical_other': count_physical_other, 'virtual': count_virtual, 'virtual_linux': count_virtual_linux, 'virtual_windows': count_virtual_windows, 'virtual_other': count_virtual_other, 'os_count_stats': os_counts }) # Grand Total Only: VMware ESXi Host Count for node in device_stats: if node.asset_os.startswith("VMware"): sysstats_grand_vmware += 1 #-- END OF Calculate Device Type Stats loop --# ##-- Calculate OS stats grand totals --## # Initialize a new empty OS Counts Total List os_counts_total = [] # Add all the os names and an initial count of 0 for name in os_list: os_counts_total.append({ 'os_name': name, 'count': 0}) # For each device specific stats entry, add up grand total os counts for device_entry in stats_list: # For each device_entry in the stats_list, go through its os_count_stats value, which is a list of os_names and counts for os_device_entry in device_entry['os_count_stats']: # Check each os_count_stats os_name against the os_counts_total os_name list for os_entry_total in os_counts_total: # If we have an os_name match, add the device's os count to the total os count if os_entry_total['os_name'] == os_device_entry['os_name']: os_entry_total['count'] += os_device_entry['count'] ####---- Add grand totals to stats_list ----#### stats_list.append({ 'asset_type': 'Total', 'total': sysstats_grand_total, 'physical': sysstats_grand_physical, 'physical_linux': sysstats_grand_physical_linux, 'physical_windows': sysstats_grand_physical_windows, 'physical_other': sysstats_grand_physical_other, 'virtual': sysstats_grand_virtual, 'virtual_linux': sysstats_grand_virtual_linux, 'virtual_windows': sysstats_grand_virtual_windows, 'virtual_other': sysstats_grand_virtual_other, 'os_count_stats': os_counts_total }) #- Other Grand Totals: Calculate -# sysstats_grand_linux = sysstats_grand_physical_linux + sysstats_grand_virtual_linux sysstats_grand_windows = sysstats_grand_physical_windows + sysstats_grand_virtual_windows sysstats_grand_other = sysstats_grand_physical_other + sysstats_grand_virtual_other # Map template variable names (1st) to the Python variables (2nd) for use in templates context = {'device_stats': stats_list, 'total_linux': sysstats_grand_linux, 'total_windows': sysstats_grand_windows, 'total_other': sysstats_grand_other, 'total_vmware': sysstats_grand_vmware, 'os_list': os_list} # Logging logger.info("Rendering stats.html") # Render the HTTP request, using the template, passing the context return render(request, 'myapphere/stats.html', context) # /myapphere/env_name - All Devices, Specific Environment def env(request, env_name): # Get all asset objects all_asset_list = AssetEntry.objects.order_by('asset_type', 'asset_env', 'asset_name') # New List for the asset types in the passed environment only new_list = [] # Check each asset, add to new list if it matches the requested environment name for node in all_asset_list: if ''.join(node.asset_env.lower().split()) == env_name.lower(): new_list.append(node) # Get number of assets in list number_assets = len(new_list) # Map template variable names (1st) to Python variables (2nd) for use in html templates context = { 'all_asset_list': new_list, 'env': env_name, 'asset_count': number_assets } # Logging logger.info("Rendering index.html - All Devices, " + env_name) # Render the http request, using the template, passing the context return render(request, 'myapphere/index.html', context) # /myapphere/device/ - Specific Device, All Environments def asset_type_all_env(request, device_type): # Get all asset objects all_asset_list = AssetEntry.objects.order_by('asset_type', 'asset_env', 'asset_name') # New List for specific devices only new_list = [] # Check each asset, add to new list if it matches the requested device type for node in all_asset_list: if ''.join(node.asset_type.lower().split()) == device_type.lower(): new_list.append(node) # Get number of assets in list number_assets = len(new_list) # Map template variable names (1st) to Python variables (2nd) for use in html templates context = { 'all_asset_list': new_list, 'asset_type': device_type, 'asset_count': number_assets } # Logging logger.info("Rendering index.html - Devices: " + device_type + ", All Environments") # Render the http request, using the template, passing the context return render(request, 'myapphere/index.html', context) # /myapphere/device/env_name - Specific Device, Specific Environment def type_env(request, device_type, env_name): # Get all asset objects all_asset_list = AssetEntry.objects.order_by('asset_type', 'asset_env', 'asset_name') # New List for specific devices and environment only new_list = [] # Check each asset, add to new list if it matches the requested device and environment for node in all_asset_list: if ''.join(node.asset_type.lower().split()) == device_type.lower(): if ''.join(node.asset_env.lower().split()) == env_name.lower(): new_list.append(node) # Get number of assets in list number_assets = len(new_list) # Map template variable names (1st) to Python variables (2nd) for use in html templates context = { 'all_asset_list': new_list, 'asset_type': device_type, 'env': env_name, 'asset_count': number_assets } # Logging logger.info("Rendering index.html - Devices: " + device_type + ", Env: " + env_name) # Render the http request, using the template, passing the context return render(request, 'myapphere/index.html', context) # /myapphere/<asset_hardware>/ - Physical or Virtual def asset_hardware(request, asset_hardware): # Get all asset objects all_asset_list = AssetEntry.objects.order_by('asset_type', 'asset_env', 'asset_name') # New List for asset_hardware (physical or virtual) new_list = [] # Check each asset, add to new list if it matches the requested hardware type for node in all_asset_list: if ''.join(node.asset_hardware.lower().split()) == asset_hardware.lower(): new_list.append(node) # Get number of assets in list number_assets = len(new_list) # Map template variable names (1st) to the Python variables (2nd) for use in templates context = {'all_asset_list': new_list, 'asset_hardware': asset_hardware, 'asset_count': number_assets } # Logging logger.info("Rendering index.html - All Devices, " + asset_hardware) # Render the HTTP request, using the template, passing the context return render(request, 'myapphere/index.html', context)
Web Page Templates
Creating the web page templates.
- Create directory structure
mkdir -p /home/django/myprojecthere/myapphere/templates/myapphere
- Create new pages
- templates/myapphere/index.html
- templates/myapphere/stats.html
Page Content: Index
Example page content for the index.html page.
/home/django/myprojecthere/myapphere/templates/myapphere/index.html
<!DOCTYPE html> <html> <head> <title>Asset List</title> </head> <body> {% load static %} <link rel="stylesheet" type="text/css" href="{% static 'myapphere/style.css' %}" /> <!-- Export button jquery (https://github.com/kayalshri/tableExport.jquery.plugin) --> <script type="text/javascript" src="{% static 'js/jquery.js' %}"></script> <script type="text/javascript" src="{% static 'myapphere/tableExport.js' %}"></script> <script type="text/javascript" src="{% static 'myapphere/jquery.base64.js' %}"></script> <!-- Layout Divide: Left Side Menu for Devices --> <table class="layout"> <tr> <!-- For white gaps in between Device Types and Phys/Virt, this must be larger than stylesheet width --> <!-- Set to style sheet width+25 px --> <td class="layout" width="696px"> <!-- Device Menu: Determine Active Device --> <ul class="asset_type"> {% if not asset_type %} <li><a class="active" href="/myapphere/">All Devices</a></li> {% else %} <li><a href="/myapphere/">All Devices</a></li> {% endif %} {% if asset_type == 'firewalls' %} <li><a class="active" href="/myapphere/firewalls/">Firewalls</a></li> {% else %} <li><a href="/myapphere/firewalls/">Firewalls</a></li> {% endif %} {% if asset_type == 'routers' %} <li><a class="active" href="/myapphere/routers/">Routers</a></li> {% else %} <li><a href="/myapphere/routers/">Routers</a></li> {% endif %} {% if asset_type == 'servers' %} <li><a class="active" href="/myapphere/servers/">Servers</a></li> {% else %} <li><a href="/myapphere/servers/">Servers</a></li> {% endif %} {% if asset_type == 'storage' %} <li><a class="active" href="/myapphere/storage/">Storage</a></li> {% else %} <li><a href="/myapphere/storage/">Storage</a></li> {% endif %} {% if asset_type == 'switches' %} <li><a class="active" href="/myapphere/switches/">Switches</a></li> {% else %} <li><a href="/myapphere/switches/">Switches</a></li> {% endif %} {% if asset_type == 'workstations' %} <li><a class="active" href="/myapphere/workstations/">Workstations</a></li> {% else %} <li><a href="/myapphere/workstations/">Workstations</a></li> {% endif %} </ul> </td> <!-- END of Layout Divide: Left Side Menu for Devices --> <!-- Layout Divide: Right Side Phys and Virt Links --> <!-- For white gaps in between Phys/Virt and Stats, this must be larger than stylesheet width --> <!-- Set to style sheet width+25 px --> <td class="layout" width="188px"> <!-- Asset Type Menu: Physical and Virtual --> <ul class ="asset_hardware"> {% if asset_hardware == 'physical' %} <li><a class="active" href="/myapphere/physical/">Physical</a></li> <li><a href="/myapphere/virtual/">Virtual</a></li> {% elif asset_hardware == 'virtual' %} <li><a href="/myapphere/physical/">Physical</a></li> <li><a class="active" href="/myapphere/virtual/">Virtual</a></li> {% else %} <li><a href="/myapphere/physical/">Physical</a></li> <li><a href="/myapphere/virtual/">Virtual</a></li> {% endif %} </ul> </td> <!-- END of Layout Divide: Right Side Phys and Virt Links --> <!-- Layout Divide: Right Side Additional Links --> <td class="layout"> <ul class="stats"> <li><a href="/myapphere/stats/">Stats</a></li> </ul> </td> <!-- END of Layout Divide: Right Side Additional Links --> </tr> <!-- Layout Divide: Left Side Environment Menu --> <tr> <td class="layout"> <!-- Environment Menu: Dynamic Asset Type Links --> <ul class="env"> {% if env == 'dev' %} {% if asset_type %} <li><a class="active" href="/myapphere/{{ asset_type }}/dev/">Dev</a></li> <li><a href="/myapphere/{{ asset_type }}/test/">Test</a></li> <li><a href="/myapphere/{{ asset_type }}/prod/">Prod</a></li> {% else %} <li><a class="active"href="/myapphere/dev/">Dev</a></li> <li><a href="/myapphere/test/">Test</a></li> <li><a href="/myapphere/prod/">Prod</a></li> {% endif %} {% elif env == 'test' %} {% if asset_type %} <li><a href="/myapphere/{{ asset_type }}/dev/">Dev</a></li> <li><a class="active" href="/myapphere/{{ asset_type }}/test/">Test</a></li> <li><a href="/myapphere/{{ asset_type }}/prod/">Prod</a></li> {% else %} <li><a href="/myapphere/dev/">Dev</a></li> <li><a class="active" href="/myapphere/test/">Test</a></li> <li><a href="/myapphere/prod/">Prod</a></li> {% endif %} {% elif env == 'prod' %} {% if asset_type %} <li><a href="/myapphere/{{ asset_type }}/dev/">Dev</a></li> <li><a href="/myapphere/{{ asset_type }}/test/">Test</a></li> <li><a class="active" href="/myapphere/{{ asset_type }}/prod/">Prod</a></li> {% else %} <li><a href="/myapphere/dev/">Dev</a></li> <li><a href="/myapphere/test/">Test</a></li> <li><a class="active" href="/myapphere/prod/">Prod</a></li> {% endif %} {% else %} {% if asset_type %} <li><a href="/myapphere/{{ asset_type }}/dev/">Dev</a></li> <li><a href="/myapphere/{{ asset_type }}/test/">Test</a></li> <li><a href="/myapphere/{{ asset_type }}/prod/">Prod</a></li> {% else %} <li><a href="/myapphere/dev/">Dev</a></li> <li><a href="/myapphere/test/">Test</a></li> <li><a href="/myapphere/prod/">Prod</a></li> {% endif %} {% endif %} </ul> </td> <!-- END of Layout Divide: Left Side Environment Menu --> <!-- START of Layout Divide: 2nd row for logged in message --> <td class="layout"> {% if user.is_authenticated %} Logged in: {{ user.username }}<br> <a class="plain" href="/logout/">Logout</a> {% endif %} </td> </tr> <!-- END of Layout Divide: 2nd row for logged in message --> <!-- Layout Divide: Bottom Asset List Table --> <tr> <td class="layout" colspan=3> <!-- Asset List Above Table: Display Device and Environment --> <br> {% if asset_type == 'firewalls' %} {% if env == 'dev' %} <b>Asset List - Firewalls - Development Environment</b> {% elif env == 'test' %} <b>Asset List - Firewalls - Test Environment</b> {% elif env == 'prod' %} <b>Asset List - Firewalls - Production Environment</b> {% else %} <b>Asset List - Firewalls - All Environments</b> {% endif %} {% elif asset_type == 'routers' %} {% if env == 'dev' %} <b>Asset List - Routers - Development Environment</b> {% elif env == 'test' %} <b>Asset List - Routers - Test Environment</b> {% elif env == 'prod' %} <b>Asset List - Routers - Production Environment</b> {% else %} <b>Asset List - Routers - All Environments</b> {% endif %} {% elif asset_type == 'servers' %} {% if env == 'dev' %} <b>Asset List - Servers - Development Environment</b> {% elif env == 'test' %} <b>Asset List - Servers - Test Environment</b> {% elif env == 'prod' %} <b>Asset List - Servers - Production Environment</b> {% else %} <b>Asset List - Servers - All Environments</b> {% endif %} {% elif asset_type == 'storage' %} {% if env == 'dev' %} <b>Asset List - Storage - Development Environment</b> {% elif env == 'test' %} <b>Asset List - Storage - Test Environment</b> {% elif env == 'prod' %} <b>Asset List - Storage - Production Environment</b> {% else %} <b>Asset List - Storage - All Environments</b> {% endif %} {% elif asset_type == 'switches' %} {% if env == 'dev' %} <b>Asset List - Switches - Development Environment</b> {% elif env == 'test' %} <b>Asset List - Switches - Test Environment</b> {% elif env == 'prod' %} <b>Asset List - Switches - Production Environment</b> {% else %} <b>Asset List - Switches - All Environments</b> {% endif %} {% elif asset_type == 'workstations' %} {% if env == 'dev' %} <b>Asset List - Workstations - Development Environment</b> {% elif env == 'test' %} <b>Asset List - Workstations - Test Environment</b> {% elif env == 'prod' %} <b>Asset List - Workstations - Production Environment</b> {% else %} <b>Asset List - Workstations - All Environments</b> {% endif %} {% else %} {% if env == 'dev' %} <b>Asset List - All Types - Development Environment</b> {% elif env == 'test' %} <b>Asset List - All Types - Test Environment</b> {% elif env == 'prod' %} <b>Asset List - All Types - Production Environment</b> {% else %} <b>Asset List - All Types - All Environments</b> {% endif %} {% endif %} <!-- Physical/Virtual Filter --> {% if asset_hardware %} {% if asset_hardware == 'physical' %} <b> - Physical -</b> {% elif asset_hardware == 'virtual' %} <b> - Virtual -</b> {% endif %} {% endif %} <!-- Assets List Above Table: Number of Assets Displayed --> {% if asset_count %} {% if asset_count == 1 %} <b> ({{ asset_count }} asset)</b> {% else %} <b> ({{ asset_count }} assets)</b> {% endif %} {% else %} <b> (0 assets)</b> {% endif %} <!-- Export Buttons --> <a href="#" class="plain" onclick="$('#assetlist').tableExport({type:'csv',escape:'false'});"><img src="{% static 'myapphere/csv.png' %}" width="24px" title="Export CSV"></a> <a href="#" class="plain" onclick="$('#assetlist').tableExport({type:'excel',escape:'false'});"><img src="{% static 'myapphere/xls.png' %}" width="24px" title="Export Excel"></a> <br> <!-- The Assets List Table --> <table class="myapphere" id="assetlist"> <thead> <tr class="myapphere"> <th class="myapphere">Device Type</th> <th class="myapphere">Name</th> <th class="myapphere">Description</th> <th class="myapphere">Environment</th> <th class="myapphere">OS</th> <th class="myapphere">Hardware</th> </tr> </thead> <tbody {% for name in all_asset_list %} <tr class="myapphere"> <td class="myapphere">{{ name.asset_type }}</td> <td class="myapphere">{{ name.asset_name }}</td> <td class="myapphere">{{ name.asset_description }}</td> <td class="myapphere center">{{ name.asset_env }}</td> <td class="myapphere center">{{ name.asset_os }}</td> <td class="myapphere center">{{ name.asset_hardware }}</td> </tr> {% endfor %} </tbody> </table> <!-- END of Assets List Table --> </td> </tr> </table> <!-- END of Layout Divide: Bottom Assets List Table --> <br> <hr> <br> </body> </html>
Page Content: Stats
Example page content for the stats.html page.
/home/django/myprojecthere/myapphere/templates/myapphere/stats.html
<!DOCTYPE html> <html> <head> <title>Asset List - Stats</title> </head> <body> {% load static %} <link rel="stylesheet" type="text/css" href="{% static 'myapphere/style.css' %}" /> <!-- Export button jquery (https://github.com/kayalshri/tableExport.jquery.plugin) --> <script type="text/javascript" src="{% static 'js/jquery.js' %}"></script> <script type="text/javascript" src="{% static 'myapphere/tableExport.js' %}"></script> <script type="text/javascript" src="{% static 'myapphere/jquery.base64.js' %}"></script> <!-- Layout Divide: Left Side Menu for Devices --> <table class="layout"> <tr> <!-- For white gaps in between Device Types and Phys/Virt, this must be larger than stylesheet width --> <!-- Set to style sheet width+25 px --> <td class="layout" width="696px"> <!-- Device Menu: No Devices Active on Stats Page --> <ul class="asset_type"> <li><a href="/myapphere/">All Devices</a></li> <li><a href="/myapphere/firewalls/">Firewalls</a></li> <li><a href="/myapphere/routers/">Routers</a></li> <li><a href="/myapphere/servers/">Servers</a></li> <li><a href="/myapphere/storage/">Storage</a></li> <li><a href="/myapphere/switches/">Switches</a></li> <li><a href="/myapphere/workstations/">Workstations</a></li> </ul> </td> <!-- END of Layout Divide: Left Side Menu for Devices --> <!-- Layout Divide: Right Side Phys and Virt Links --> <!-- For white gaps in between Phys/Virt and Stats, this must be larger than stylesheet width --> <!-- Set to style sheet width+25 px --> <td class="layout" width="188px"> <!-- Asset Type Menu: Physical and Virtual --> <ul class ="asset_hardware"> {% if asset_hardware == 'physical' %} <li><a class="active" href="/myapphere/physical/">Physical</a></li> <li><a href="/myapphere/virtual/">Virtual</a></li> {% elif asset_hardware == 'virtual' %} <li><a href="/myapphere/physical/">Physical</a></li> <li><a class="active" href="/myapphere/virtual/">Virtual</a></li> {% else %} <li><a href="/myapphere/physical/">Physical</a></li> <li><a href="/myapphere/virtual/">Virtual</a></li> {% endif %} </ul> </td> <!-- END of Layout Divide: Right Side Phys and Virt Links --> <!-- Layout Divide: Right Side Additional Links --> <td class="layout"> <ul class="stats"> <li><a class="active" href="/myapphere/stats/">Stats</a></li> </ul> </td> <!-- END of Layout Divide: Right Side Additional Links --> </tr> <!-- START of Layout Divide: 2nd row for logged in message --> <tr> <td class="layout"> </td> <td class="layout"> {% if user.is_authenticated %} Logged in: {{ user.username }}<br> <a class="plain" href="/logout/">Logout</a> {% endif %} </td> </tr> <!-- END of Layout Divide: 2nd row for logged in message --> <!-- Layout Divide: Bottom Stats Table (Asset Counts) --> <tr> <td class="layout" colspan=3> <br> <b>Asset List - Statistics - Asset Counts</b> <!-- Export Buttons --> <a href="#" class="plain" onclick="$('#assetcounts').tableExport({type:'csv',escape:'false'});"><img src="{% static 'myapphere/csv.png' %}" width="24px" title="Export CSV"></a> <a href="#" class="plain" onclick="$('#assetcounts').tableExport({type:'excel',escape:'false'});"><img src="{% static 'myapphere/xls.png' %}" width="24px" title="Export Excel"></a> <br> <!-- Stats Table (Asset Counts) --> <table class="myapphere" id="assetcounts"> <thead> <tr class="myapphere"> <th class="myapphere">Device Type</th> <th class="myapphere">Systems</th> <th class="myapphere">Physical</th> <th class="myapphere">Physical Linux</th> <th class="myapphere">Physical Windows</th> <th class="myapphere">Physical Other</th> <th class="myapphere">Virtual</th> <th class="myapphere">Virtual Linux</th> <th class="myapphere">Virtual Windows</th> <th class="myapphere">Virtual Other</th> </tr> </thead> <tbody> {% for asset in device_stats %} <tr class="myapphere"> {% if asset.asset_type == 'Total' %} <td class="myapphere totals"><b>{{ asset.asset_type }}</b></td> <td class="myapphere center totals">{{ asset.total }}</td> <td class="myapphere center totals">{{ asset.physical }}</td> <td class="myapphere center totals">{{ asset.physical_linux }}</td> <td class="myapphere center totals">{{ asset.physical_windows }}</td> <td class="myapphere center totals">{{ asset.physical_other }}</td> <td class="myapphere center totals">{{ asset.virtual }}</td> <td class="myapphere center totals">{{ asset.virtual_linux }}</td> <td class="myapphere center totals">{{ asset.virtual_windows }}</td> <td class="myapphere center totals">{{ asset.virtual_other }}</td> {% else %} <td class="myapphere">{{ asset.asset_type }}</td> <td class="myapphere center">{{ asset.total }}</td> <td class="myapphere center">{{ asset.physical }}</td> <td class="myapphere center">{{ asset.physical_linux }}</td> <td class="myapphere center">{{ asset.physical_windows }}</td> <td class="myapphere center">{{ asset.physical_other }}</td> <td class="myapphere center">{{ asset.virtual }}</td> <td class="myapphere center">{{ asset.virtual_linux }}</td> <td class="myapphere center">{{ asset.virtual_windows }}</td> <td class="myapphere center">{{ asset.virtual_other }}</td> {% endif %} </tr> {% endfor %} <tr class="myapphere"> <td class="myapphere totals">Total Linux</td> <td class="myapphere center">{{ total_linux }}</td> </tr> <tr class="myapphere"> <td class="myapphere totals">Total Windows</td> <td class="myapphere center">{{ total_windows }}</td> </tr> <tr class="myapphere"> <td class="myapphere totals">Total Other</td> <td class="myapphere center">{{ total_other }}</td> </tr> <tr class="myapphere"> <td class="myapphere totals">Total VMware ESXi*</td> <td class="myapphere center">{{ total_vmware }}</td> </tr> </tbody> </table> *Physical Virtual Hosts (VMware ESXi) count is included in Physical Linux/Total Linux counts. <!-- END of Stats Table (Asset Counts) --> </td> </tr> <!-- END of Layout Divide: Bottom Stats Table (Asset Counts) --> <!-- Layout Divide: Bottom Stats Table (OS Counts) --> <tr> <td class="layout" colspan=3> <br> <b>Asset List - Statistics - OS Counts</b> <!-- Export Buttons --> <a href="#" class="plain" onclick="$('#oscounts').tableExport({type:'csv',escape:'false'});"><img src="{% static 'myapphere/csv.png' %}" width="24px" title="Export CSV"></a> <a href="#" class="plain" onclick="$('#oscounts').tableExport({type:'excel',escape:'false'});"><img src="{% static 'myapphere/xls.png' %}" width="24px" title="Export Excel"></a> <br> <!-- Stats Table (OS Counts) --> <table class="myapphere" id="oscounts"> <thead> <tr class="myapphere"> <th class="myapphere">Device Type</th> {% for os_name in os_list %} <th class="myapphere">{{ os_name }}</th> {% endfor %} </tr> </thead> <tbody> {% for asset in device_stats %} <tr class="myapphere"> {% if asset.asset_type == 'Total' %} <td class="myapphere totals"><b>{{ asset.asset_type }}</b></td> {% for name in asset.os_count_stats %} <td class="myapphere center totals">{{ name.count }}</td> {% endfor %} {% else %} <td class="myapphere">{{ asset.asset_type }}</td> {% for name in asset.os_count_stats %} <td class="myapphere center">{{ name.count }}</td> {% endfor %} {% endif %} </tr> {% endfor %} </tbody> </table> <!-- END of Stats Table (OS Counts) --> </td> </tr> </table> <!-- END of Layout Divide: Bottom Stats Table (OS Counts) --> <br> <hr> <br> </body> </html>
Static Files/Stylesheets
Configuring stylesheets.
- Create directory structure
mkdir -p /home/django/myprojecthere/myapphere/static/myapphere
- Create stylesheet (/home/django/myprojecthere/myapphere/static/myapphere/style.css)
/* style.css : Styles for html templates */ /* ---- Table Styles ---- */ /* table settings */ table.layout { border-collapse: collapse; border-spacing: 0px; padding: 0px; } td.layout { border-spacing: 0px; padding: 0px; } table.myapphere { border-collapse: collapse; } /* table headers, table definitions */ table.myapphere, th.myapphere, td.myapphere { border-collapse: collapse; border: 1px solid #ddd; padding: 5px; } /* th -> table headers */ th.myapphere { background-color: #4CAF50; color: white; } /* tr -> table rows * * tr:nth-child -> color every even table row * * tr:hover -> Mouse hover background color effect */ tr.myapphere:nth-child(even) {background-color: #f2f2f2;} tr.myapphere:hover {background-color: #ddd;} td.center { text-align: center; } td.totals { background-color: #b3b3b3; font-weight: bold; } /* ---- Menu Styles ---- */ ul.asset_type { list-style-type: none; margin: 0; padding: 0; overflow: hidden; background-color: #333; width: 671px; } ul.env { list-style-type: none; margin: 0; padding: 0; overflow: hidden; background-color: #333; width: 163px; } ul.stats { list-style-type: none; margin: 0; padding: 0; overflow: hidden; background-color: #333; width: 60px; } ul.asset_hardware { list-style-type: none; margin: 0; padding: 0; overflow: hidden; background-color: #333; width: 163px; } li { float: left; } li a { display: block; color: white; text-align: center; padding: 10px 10px; text-decoration: none; } a:hover:not(.active) { background-color: #111; } .active { background-color:#4CAF50; } /* ---- Non-Menu Styles ---- */ a:link.plain { background-color: #ffffff; } a:visited.plain { background-color: #ffffff; } a:hover.plain { background-color: #4caf50; }
Apache
Configuring Apache to pass traffic to the application.
- Configure Apache's mod_wsgi for the application (new file)
vim /etc/httpd/conf.d/myapphere.conf # Asset List Django/WSGI Config # WSGIScriptAlias: The base URL to serve the application from WSGIScriptAlias / /home/django/myprojecthere/myprojecthere/wsgi.py # WSGI Daemon Process Config WSGIDaemonProcess myapphere python-path=/home/django/myprojecthere WSGIProcessGroup myapphere # Location of Django Application and wsgi.py config <Directory /home/django/myprojecthere/myprojecthere> <Files wsgi.py> Require all granted </Files> </Directory> # Static files (admin interface and style sheet) Alias /static/ /var/www/static/ <Directory /var/www/static/> Require all granted </Directory>
- Configure the virtual host (new file)
vim /etc/httpd/conf.d/vhosts.conf # Virtual Host Config # Redirect shortname to fully qualified <VirtualHost *:80> ServerName djangoserver Redirect "/" "http://djangoserver.mycorps.domain.org/" </VirtualHost> # Redirect all http to https <VirtualHost *:80> ServerName djangoserver.mycorps.domain.org <IfModule mod_rewrite.c> # Redirect all to https RewriteEngine On RewriteCond %{HTTPS} off RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} </IfModule> </VirtualHost> # Redirect shortname to fully qualified <VirtualHost *:443> ServerName djangoserver Redirect "/" "https://djangoserver.mycorps.domain.org/" </VirtualHost> # Django Application lives here at the root /. See: /etc/httpd/conf.d/myapphere.conf <VirtualHost *:443> # Fully qualified server name ServerName djangoserver.mycorps.domain.org Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains;" </VirtualHost> <VirtualHost *:443> # OPTIONAL: CNAME for website - will only work with SSL if using a wildcard certificate ServerName myapphere.mycorps.domain.org Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains;" </VirtualHost>
- Configure SSL Certificate
vim /etc/httpd/conf.d/ssl.conf SSLCertificateFile /etc/pki/tls/certs/current_crt.crt SSLCertificateKeyFile /etc/pki/tls/certs/current_key.key SSLCertificateChainFile /etc/pki/tls/certs/current_ca.crt
- Generate a self signed cert if this is just a lab environment.
- Start and Enable Apache
systemctl start httpd systemctl enable httpd
Django: Authentication
Adding authentication to Django requires basic auth to be setup first, as LDAP auth builds upon it.
Basic Authentication
Basic authentication uses Django local user accounts.
- Edit Project Settings (/home/django/myprojecthere/myprojecthere/settings.py)
# Redirect Successful Logins Here LOGIN_REDIRECT_URL = '/myapphere/' # Ensure contrib.auth is added to installed apps: # Application definition INSTALLED_APPS = [ 'myapphere.apps.MyapphereConfig', 'django.contrib.admin', 'django.contrib.auth', # must exist for basic auth to work ... ]
- Add login and logout URLs to project URLs (/home/django/myprojecthere/myprojecthere/urls.py)
# auth views - authentication login/logout module from django.contrib.auth import views as auth_views # URL Patterns - Map to Projects Within Django urlpatterns = [ ... # Login URLs url(r'^login/$', auth_views.login, {'template_name': 'login.html'}, name='login'), url(r'^logout/$', auth_views.logout, {'next_page': '/login/'}, name='logout'), ... ]
- Edit the application views (/home/django/myprojecthere/myapphere/views.py)
# Require login decorator from django.contrib.auth.decorators import login_required # Put login decorator before every function definition that you want to require login to view: #-> Require login for the view following it <-# @login_required(login_url="/login/") def index(request):
- Create a HTML Login Page (/home/django/myprojecthere/myapphere/templates/login.html)
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>{% block title %}Asset List{% endblock %}</title> </head> <body> <header> <h1>Asset List</h1> {% if user.is_authenticated %} Greetings, {{ user.username }}. <a href="{% url 'logout' %}">logout</a> {% endif %} </header> <hr> <main> <h2>Login</h2> <form method="post"> {% csrf_token %} {{ form.as_p }} <button type="submit">Login</button> </form> </main> <hr> </body> </html>
LDAP Authentication
Configuring LDAP authentication.
- Install the python django ldap package
pip install django-auth-ldap
- Edit Project Settings (/home/django/myprojecthere/myprojecthere/settings.py)
# LDAP Module Imports import ldap from django_auth_ldap.config import LDAPSearch from django_auth_ldap.config import GroupOfNamesType from django_auth_ldap.config import LDAPGroupQuery # Authentication Backends (LDAP and Django Local) AUTHENTICATION_BACKENDS = ( 'django_auth_ldap.backend.LDAPBackend', 'django.contrib.auth.backends.ModelBackend', ) ####- LDAP Server Config -#### AUTH_LDAP_SERVER_URI = "ldap://LDAPSERVER01.mycorps.domain.org ldap://LDAPSERVER02.mycorps.domain.org" AUTH_LDAP_START_TLS = True # Bind Info: Use authenticating user to get user and group info AUTH_LDAP_BIND_AS_AUTHENTICATING_USER = True AUTH_LDAP_USER_DN_TEMPLATE = "uid=%(user)s,cn=users,cn=accounts,dc=mycorps,dc=domain,dc=org" # Cache LDAP Groups for use with permissions AUTH_LDAP_CACHE_GROUPS = True AUTH_LDAP_GROUP_CACHE_TIMEOUT = 3600 # Group Info Gathering AUTH_LDAP_GROUP_SEARCH = LDAPSearch "cn=groups,cn=accounts,dc=mycorps,dc=domain,dc=org", ldap.SCOPE_SUBTREE, "(objectClass=groupofnames)" ) AUTH_LDAP_GROUP_TYPE = GroupOfNamesType() # Group restrictions - Who can login via LDAP AUTH_LDAP_REQUIRE_GROUP = ( # Allow login for members of the LDAP group "list_users" LDAPGroupQuery("cn=list_users,cn=groups,cn=accounts,dc=mycorps,dc=domain,dc=org") | # Allow login for members of the LDAP group "admin_group" LDAPGroupQuery("cn=admin_group,cn=groups,cn=accounts,dc=mycorps,dc=domain,dc=org") ) # Use LDAP group membership to calculate group permissions. AUTH_LDAP_FIND_GROUP_PERMS = True # Update Django record from LDAP each time user logs in AUTH_LDAP_ALWAYS_UPDATE_USER = True # Populate the Django user info from LDAP directory info AUTH_LDAP_USER_ATTR_MAP = { "first_name": "givenname", "last_name": "sn", "email": "mail" } # Set user permission flags by group - set LDAP group "admin_group" as Django Staff Members AUTH_LDAP_USER_FLAGS_BY_GROUP = { "is_staff": "cn=admin_group,cn=groups,cn=accounts,dc=mycorps,dc=domain,dc=org" }
Django: Jquery
Jquery can be added in order to enable an Export as CSV and/or Excel buttons.
Reference: Export Jquery Buttons: https://github.com/kayalshri/tableExport.jquery.plugin
- From the reference link, download:
- jquery.base64.js
- tableExport.js
- Search the internet for CSV and XLS icons. Download them.
- CSV = save as “csv.png”
- XLS = save as “xls.png”
- Save the Javascript files and small pictures (CSV/XLS) into the same directory as the style sheet
/home/django/myprojecthere/myapphere/static/myapphere/
- Install the Django Jquery integration
pip install django-jquery
- Add Jquery to the “INSTALLED_APPS” list (/home/django/myprojecthere/myprojecthere/settings.py)
# Application definition INSTALLED_APPS = [ 'jquery',
- Add jquery to HTML templates
<!-- Export button jquery support (https://github.com/kayalshri/tableExport.jquery.plugin) --> <script type="text/javascript" src="{% static 'js/jquery.js' %}"></script> <script type="text/javascript" src="{% static 'myapphere/tableExport.js' %}"></script> <script type="text/javascript" src="{% static 'myapphere/jquery.base64.js' %}"></script> <!-- Export Buttons --> <a href="#" class="plain" onclick="$('#assetlist').tableExport({type:'csv',escape:'false'});"><img src="{% static 'myapphere/csv.png' %}" width="24px" title="Export CSV"></a> <a href="#" class="plain" onclick="$('#assetlist').tableExport({type:'excel',escape:'false'});"><img src="{% static 'myapphere/xls.png' %}" width="24px" title="Export Excel"></a>
Django: Static Files
No matter which steps from above you skip, you absolutely need to collect static files to copy them into the location Apache expects them to be.
This is for Apache's access to javascript, css, etc.
- Collect static files
cd /home/django/myprojecthere/ python manage.py collectstatic
DEBUG: TURN OFF
Lastly, when you are not developing/debugging your site, turn off debug mode to avoid dumping sensitive information to the screen in the event of an error.
Edit the project settings (/home/django/myprojecthere/myprojecthere/settings.py)
# SECURITY WARNING: don't run with debug turned on in production! DEBUG = False
Next Steps
Proceed to the Django API section.