Changeset - f397b9db8183
[Not reviewed]
default
0 6 0
Branko Majic (branko) - 11 years ago 2013-04-14 11:37:14
branko@majic.rs
Styling updates to reflect 2SoD recommendations.
6 files changed with 49 insertions and 44 deletions:
0 comments (0 inline, 0 general)
conntrackt/admin.py
Show inline comments
 
# Import all the models from the application.
 
from conntrackt.models import *
 

	
 
# Import the administrator application.
 
# Django imports.
 
from django.contrib import admin
 
from django.core.urlresolvers import resolve
 

	
 
# Import resolver used for figuring-out the view being called in custom query
 
# calls.
 
from django.core.urlresolvers import resolve
 
# Application imports.
 
from .models import Project, Location, Entity, Interface, Communication
 

	
 

	
 
class InterfaceInline(admin.StackedInline):
 
    """
 
    This class implements the inline admin view of the Interface instances. This
 
    is used when adding the entities (since it's easier to specify interface for
 
    an entity right away).
 

	
 
    Properties:
 

	
 
      - model - Model that this admin class refers to.
 
      - extra - Number of interfaces that should be show inline for
 
        adding/editing.
 
    """
 

	
 
    model = Interface
 
    extra = 1
 

	
 

	
 
class CommunicationAdmin(admin.ModelAdmin):
 
    """
 
    Modifies the default admin class for the Communication class. The
 
    communication class needs to be modified in a number of ways in order to
 
    cater for easier adding of communication links, letting us limit the
 
    interfaces being shown as source/destination to specific project and/or
 
    site.
 
    """
 

	
 
    # Show standard fields of the model, and also include a separate edit link
 
    # so that other fields can be editable.
 
    list_display = ('source', 'destination', 'protocol', 'port', 'edit_link')
 
@@ -72,50 +71,53 @@ class CommunicationAdmin(admin.ModelAdmi
 
            if 'source__entity__project__id__exact' in request.GET:
 
                interface_filter['entity__project'] = request.GET['source__entity__project__id__exact']
 
            # If location was specified in GET request, add it as a filter.
 
            if 'source__entity__location__id__exact' in request.GET:
 
                interface_filter['entity__location'] = request.GET['source__entity__location__id__exact']
 
            # If there are any filtering options for the show interfaces, apply them.
 
            if interface_filter:
 
                kwargs["queryset"] = Interface.objects.filter(**interface_filter)
 

	
 
        # Call the parent's method so it would do any of its magic.
 
        return super(CommunicationAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
 

	
 

	
 
class EntityAdmin(admin.ModelAdmin):
 
    """
 
    This class implements the admin view of the entity instances. It adds some
 
    inline capability that can be edited for the entity, and also adds inline
 
    editing of interfaces related to the entity.
 
    """
 

	
 
    # Show the interfaces inline when editing an entity.
 
    inlines = [InterfaceInline]
 
    # Specify what should be viewed in a list display.
 
    list_display = ('name', 'project', 'location')
 
    # Allow the user to change project and location directly in the list.
 
    list_editable = ('project', 'location')
 
    # Enable filtering based on project and location.
 
    list_filter = ['project', 'location']
 

	
 

	
 
class InterfaceAdmin(admin.ModelAdmin):
 
    """
 
    This class implements the admin view of the interface instances. It allows
 
    editing the IP address and netmask of an interface directly in the listing.
 

	
 
    It also adds some filtering capability based on project and/or location.
 
    """
 

	
 
    # Specify fields that should be visible in the list view.
 
    list_display = ('entity', 'address', 'netmask')
 
    # Allow changing of IP address and netmask directly in the list view.
 
    list_editable = ('address', 'netmask')
 
    # Enable filtering based on project and location.
 
    list_filter = ['entity__project', 'entity__location']
 

	
 

	
 
# Register our admin classes.
 
admin.site.register(Project)
 
admin.site.register(Location)
 
admin.site.register(Entity, EntityAdmin)
 
admin.site.register(Interface, InterfaceAdmin)
 
admin.site.register(Communication, CommunicationAdmin)
 

	
conntrackt/models.py
Show inline comments
 
# Django-specific imports
 
# Django imports.
 
from django.core.exceptions import ValidationError
 
from django.db import models
 

	
 
# Create your models here.
 

	
 
class Project(models.Model):
 
    """
 
    Implements a model with information about a project. A project has some
 
    basic settings, and mainly serves the purpose of grouping entities for
 
    easier handling and administration.
 

	
 
    Fields:
 

	
 
      name - String denoting the project name.
 
      description - Free-form description of the project.
 
    """
 

	
 
    name = models.CharField(max_length = 100)
 
    description = models.TextField(blank = True)
 
    name = models.CharField(max_length=100)
 
    description = models.TextField(blank=True)
 

	
 
    def __unicode__(self):
 
        """
 
        Returns:
 
          String representation of a project.
 
        """
 

	
 
        return self.name
 

	
 

	
 
class Location(models.Model):
 
    """
 
    Implements a model with information about location. Locations can further be
 
    assigned to entities, letting the user group different servers and equipment
 
    based on location.
 

	
 
    Locations are not tied to specific project, and they do not have to be
 
    actual physical locations. Such generic locations are therefore reusable
 
    accross multiple projects.
 

	
 
    For example, locations can be:
 

	
 
      - Main site
 
      - Backup site
 
      - Disaster recovery site
 
      - Belgrade
 
      - Stockholm
 

	
 
    Fields:
 

	
 
      name - String denoting the location name.
 
      description - Free-form description of a location.
 
    """
 

	
 
    name = models.CharField(max_length = 100)
 
    description = models.TextField(blank = True)
 
    name = models.CharField(max_length=100)
 
    description = models.TextField(blank=True)
 

	
 
    def __unicode__(self):
 
        """
 
        Returns:
 
          String representation of a location.
 
        """
 

	
 
        return self.name
 

	
 

	
 
class Entity(models.Model):
 
    """
 
    Models an entity in a project. An entity can be a server, router, or any
 
    other piece of networking equipment that has its own IP address.
 

	
 
    Entities can also be used for representing subnets etc. This is useful when
 
    the communication restrictions need to be applied across a subnet.
 

	
 
    Entities are tied to specific projects and locations.
 

	
 
    Fields:
 

	
 
      name - String denoting the entity name.
 
      description - Free-form description of an entity.
 
      project - Foreign key pointing to the project to which the entity
 
      belongs.
 
      location - Foreign key pointing to the location at which the entity is
 
      located.
 
    """
 

	
 
    name = models.CharField(max_length = 100)
 
    description = models.TextField(blank = True)
 
    name = models.CharField(max_length=100)
 
    description = models.TextField(blank=True)
 
    project = models.ForeignKey(Project)
 
    location = models.ForeignKey(Location)
 

	
 
    class Meta:
 
        """
 
        Overrides some of the default parameters used by Django for this model.
 

	
 
        Properties:
 
          verbose_name_plural - Changes the way Django Admin displays the model
 
          name in plural.
 
        """
 

	
 
        verbose_name_plural = 'entities'
 

	
 
    def __unicode__(self):
 
        """
 
        Returns:
 
          String representation of an entity. This identifier contains name of
 
          entity, its project name, and location name.
 
        """
 

	
 
        return "%s (%s - %s)" % (self.name, self.project, self.location)
 

	
 

	
 
class Interface(models.Model):
 
    """
 
    Models a representation of an interface on an entity. It can be used for
 
    representing the subnets as well.
 

	
 
    Each interface is coupled with a specific Entity.
 

	
 
    Fields:
 
      name - String denoting the interface name. For example 'eth0', 'eth1'
 
      etc.
 
      description - Free-form description of an interface.
 
      entity - Foreign key pointing to the entity to which the interface
 
      belongs.
 
      address - IP address of an interface. It's possible to store network
 
      address in it as well.
 
      netmask - Netmask of the interface. By default this is /32
 
      (255.255.255.255), but in case of subnet entities this can be used for
 
      denoting the network netmask.
 
    """
 

	
 
    name = models.CharField(max_length = 100, default = 'eth0')
 
    description = models.TextField(blank = True, default = 'Main network interface.')
 
    name = models.CharField(max_length=100, default='eth0')
 
    description = models.TextField(blank=True, default='Main network interface.')
 
    entity = models.ForeignKey(Entity)
 
    address = models.IPAddressField()
 
    netmask = models.IPAddressField(default = '255.255.255.255')
 
    netmask = models.IPAddressField(default='255.255.255.255')
 

	
 
    def __unicode__(self):
 
        """
 
        Returns:
 
          String representation of an interface. In case of single IP this will
 
          simply be the interface name and IP address. In case of subnet it will
 
          include the netmask as well.
 
        """
 

	
 
        if self.netmask == '255.255.255.255':
 
            return '%s (%s)' % (self.entity.name, self.address)
 
        else:
 
            return '%s (%s/%s)' % (self.entity.name, self.address, self.netmask)
 

	
 

	
 
class Communication(models.Model):
 
    """
 
    Models a representation of allowed network communication. This lets the user
 
    display the possible network connections that should be allowed. Information
 
    from the communication instances is also used for generating the iptables
 
    rules for the entities.
 

	
 
    Communication instances allow the user to specify one of the three possible
 
    protocols and related information:
 

	
 
      - TCP, along with the TCP port.
 
      - UDP, along with the UDP port.
 
@@ -174,29 +178,29 @@ class Communication(models.Model):
 
      type (in numeric form).
 
      description - Free-form text that can be used to describe the
 
      communication. This is also used when generating the iptables rules for
 
      documenting the rules.
 
    """
 

	
 
    PROTOCOL_CHOICES = (
 
        ('TCP', 'TCP'),
 
        ('UDP', 'UDP'),
 
        ('ICMP', 'ICMP'),
 
        )
 

	
 
    source = models.ForeignKey(Interface, related_name = 'source_set')
 
    destination = models.ForeignKey(Interface, related_name = 'destination_set')
 
    protocol = models.CharField(max_length = 10, choices = PROTOCOL_CHOICES)
 
    port = models.IntegerField(default = 0)
 
    description = models.TextField(blank = True)
 
    source = models.ForeignKey(Interface, related_name='source_set')
 
    destination = models.ForeignKey(Interface, related_name='destination_set')
 
    protocol = models.CharField(max_length=10, choices=PROTOCOL_CHOICES)
 
    port = models.IntegerField(default=0)
 
    description = models.TextField(blank=True)
 

	
 
    def __unicode__(self):
 
        """
 
        Returns:
 
          String representation of an interface. This involves showing the
 
          source and destination _entity_ name, protocol, and port.
 
        """
 

	
 
        return "%s -> %s (%s:%s)" % (self.source.entity.name, self.destination.entity.name, self.protocol, self.port)
 

	
 
    def clean(self):
 
        """
conntrackt/templatetags/conntrackt_tags.py
Show inline comments
 
# Import Django's template library.
 
# Django imports.
 
from django import template
 
# Import for determining the active URL.
 
from django.core import urlresolvers
 

	
 

	
 
# Get an instance of Django's template library.
 
register = template.Library()
 

	
 

	
 
@register.inclusion_tag('conntrackt/html_link.html')
 
def html_link(text, view, *args, **kwargs):
 
    """
 
    A small wrapper for showing HTML links.
 

	
 
    Positional arguments:
 

	
 
        text - Text that should be used as a link.
 
@@ -63,25 +63,25 @@ def iptables(communication):
 

	
 
        communication - Instance of a models.Communication object.
 
    """
 
    values = (communication.source.address, communication.source.netmask, communication.protocol.lower(), communication.protocol.lower(), communication.port)
 
    if communication.protocol in ('TCP', 'UDP'):
 
        rule_template = "-A INPUT -s %s/%s -p %s -m %s --dport %s -j ACCEPT"
 
    elif communication.protocol in ('ICMP'):
 
        rule_template = "-A INPUT -s %s/%s -p %s -m %s --icmp-type %s -j ACCEPT"
 

	
 
    return rule_template % values
 

	
 

	
 
@register.simple_tag(takes_context = True)
 
@register.simple_tag(takes_context=True)
 
def active_link(context, url_name, return_value='active', **kwargs):
 
    """
 
    This template tag can be used to check if the provided URL matches against
 
    the path from the request or not.
 

	
 
    Arguments:
 

	
 
      context - Context of the current view being called.
 

	
 
      url_name - Name of the URL that's being checked against current path from
 
      request.
 
    """
conntrackt/urls.py
Show inline comments
 
# Import basic functions for URL pattern processing.
 
# Django imports.
 
from django.conf.urls import patterns, url
 

	
 
# For logging-in the users
 
from django.contrib.auth.views import login, logout
 

	
 
# Import some app-specific views.
 
# Application imports.
 
from .views import IndexView, ProjectView, EntityView, entity_iptables, project_iptables
 

	
 

	
 
urlpatterns = patterns(
 
    'conntrackt.views',
 
    # Homepage/index view.
 
    url(r'^$', IndexView.as_view(), name="index"),
 
    # View for showing information about a project.
 
    url(r'^project/(?P<pk>\d+)/$', ProjectView.as_view(),
 
        name = 'project'),
 
        name='project'),
 
    # View for showing information about an entity.
 
    url(r'^entity/(?P<pk>\d+)/$', EntityView.as_view(),
 
        name = 'entity'),
 
        name='entity'),
 
    # View for rendering iptables rules for a specific entity.
 
    url(r'^entity/(?P<pk>\d+)/iptables/$', entity_iptables, name="entity_iptables"),
 
    # View for rendering zip file with iptables rules for all entities in a project.
 
    url(r'^project/(?P<project_id>\d+)/iptables/$', project_iptables, name="project_iptables"),
 
    # View for rendering zip file with iptables rules for all entities in a project for a specific location.
 
    url(r'^project/(?P<project_id>\d+)/location/(?P<location_id>\d+)/iptables/$', project_iptables, name="project_location_iptables"),
 
    # Views for logging-in/out the users.
 
    url(r'^login/$', login, {'template_name': 'conntrackt/login.html'}, name = "login"),
 
    url(r'^logout/$', logout, name = "logout"),
 
    url(r'^login/$', login, {'template_name': 'conntrackt/login.html'}, name="login"),
 
    url(r'^logout/$', logout, name="logout"),
 
)
 

	
conntrackt/utils.py
Show inline comments
 
# Standard library imports.
 
import re
 

	
 
# Django-specific imports.
 
# Django imports.
 
from django.template import Context, loader
 

	
 

	
 
def generate_entity_iptables(entity):
 
    """
 
    Generates full iptables rules for the supplied entity. The generated rules
 
    can be fed directly to the iptables-restore utility.
 

	
 
    Arguments:
 

	
 
        entity - An Entity instance for which the iptables rules should be
 
        generated.
conntrackt/views.py
Show inline comments
 
# For generating ZIP files.
 
# Standard library imports.
 
from StringIO import StringIO
 
from zipfile import ZipFile, ZIP_DEFLATED
 

	
 
# Django-specific imports.
 
# Django imports.
 
from django.http import HttpResponse
 
from django.shortcuts import render_to_response, get_object_or_404
 
from django.views.generic import TemplateView, DetailView
 

	
 
# Application-specific imports.
 
# Application imports.
 
from .models import Project, Entity, Location
 
from .utils import generate_entity_iptables
 

	
 

	
 
class IndexView(TemplateView):
 
    """
 
    Custom view used for rendering the index page.
 
    """
 

	
 
    template_name = 'conntrackt/index.html'
 

	
 
    def get_context_data(self, **kwargs):
 
@@ -53,26 +53,26 @@ class ProjectView(DetailView):
 
        Location, and entities is a query set of entities that belong to that
 
        particular location, and to related project.
 
        """
 

	
 
        # Set the context using the parent class.
 
        context = super(ProjectView, self).get_context_data(**kwargs)
 

	
 
        # Set-up an array that will contaion (location, entities) tuples.
 
        location_entities = []
 

	
 
        # Add the (location, entities) tuple for each location that has entities
 
        # belonging to the related project.
 
        for location in Location.objects.filter(entity__project = self.object).distinct():
 
            entities = Entity.objects.filter(project = self.object, location = location)
 
        for location in Location.objects.filter(entity__project=self.object).distinct():
 
            entities = Entity.objects.filter(project=self.object, location=location)
 
            location_entities.append((location, entities))
 

	
 
        # Add the (location, entities) tuples to context.
 
        context['location_entities'] = location_entities
 

	
 
        # Finally return the context.
 
        return context
 

	
 

	
 
class EntityView(DetailView):
 
    """
 
    Custom view for presenting entity information.
 
@@ -109,76 +109,76 @@ def entity_iptables(request, pk):
 
    Arguments:
 

	
 
        pk - Primary key of the Entity object for which the rules should be
 
        generated.
 

	
 
    Returns:
 

	
 
        Response object that contains the iptables rules for specified entity.
 
    """
 

	
 
    # Fetch the entity, and construct the response with iptables rules as
 
    # content.
 
    entity = get_object_or_404(Entity, pk = pk)
 
    entity = get_object_or_404(Entity, pk=pk)
 
    content = generate_entity_iptables(entity)
 
    response = HttpResponse(content, mimetype='text/plain')
 
    
 
    # Add the Content-Disposition information for the browser, telling the
 
    # browser to download the file with suggested filename.
 
    response['Content-Disposition']="attachment; filename=%s-iptables.conf" % entity.name.lower().replace(" ", "_")
 

	
 
    return response
 

	
 

	
 
def project_iptables(request, project_id, location_id = None):
 
def project_iptables(request, project_id, location_id=None):
 
    """
 
    Custom view for obtaining iptables for all entities of a project or project
 
    location in a single ZIP file.
 

	
 
    Arguments:
 

	
 
        request - Request object.
 

	
 
        project_id - Unique ID of the project for whose entities the iptables
 
        rules should be generated.
 

	
 
        location_id - Optional unique ID of the project location for whose
 
        entities the iptables rules should be generated. Default is None, which
 
        means generate rules for _all_ entities in a project.
 

	
 
    Returns:
 

	
 
        Response object that contains the ZIP file and Content-Disposition
 
        information.
 
    """
 

	
 
    # Fetch the project.
 
    project = get_object_or_404(Project, pk = project_id)
 
    project = get_object_or_404(Project, pk=project_id)
 

	
 
    # Set-up a string IO object to which we'll write the ZIP file (in-memory).
 
    buff = StringIO()
 

	
 
    # Create a new ZIP file in-memory.
 
    zipped_iptables = ZipFile(buff, "w", ZIP_DEFLATED)
 

	
 
    # Create the response object, setting the mime type so browser could offer
 
    # to open the file with program as well.
 
    response = HttpResponse(mimetype='application/zip')
 

	
 
    # If specific location was specified, get the entities that are part of that
 
    # project location only, otherwise fetch all of the project's entities. Also
 
    # set-up the filename that will be suggested to the browser.
 
    if location_id:
 
        location = get_object_or_404(Location, pk=location_id)
 
        entities = project.entity_set.filter(location = location)
 
        entities = project.entity_set.filter(location=location)
 
        filename = '%s_%s-iptables.zip' % (project.name, location.name)
 
    else:
 
        entities = project.entity_set.all()
 
        filename = '%s-iptables.zip' % (project.name)
 

	
 
    # Lower-case the filename, and replace spaces with underscores (_).
 
    filename = filename.lower().replace(" ", "_")
 

	
 
    # Render iptables rules for each entity, placing them in the ZIP archive.
 
    for entity in entities:
 
        entity_iptables = generate_entity_iptables(entity)
 
        zipped_iptables.writestr("%s-iptables.conf" % entity.name.lower().replace(" ", "_"), entity_iptables)
0 comments (0 inline, 0 general)