Commits

Frank Becker committed bee896d

models changes, added celery workers

- Some DB schema adjustments
- moved to project/ … Django structure
- added first 3 update scripts run by celery
- ping host
- scrape H/W DB
- update Tapper data from ssh/YAML output

  • Participants
  • Parent commits 61cb44b

Comments (0)

Files changed (34)

 3rd_party
 *pyc
 *sql
+*sublime*
 .*ropeproject/*
 src/media/*
 src/sitestatic/*

install/deploy_myhost_dst.sh

+#!/bin/zsh -xe
+# Frank Becker <fb@alien8.de>
+# 
+# Sun Oct 30 00:17:14 CEST 2011
+
+APP=myhost
+APP_USER=myhost
+
+APP_DIR=/srv/var/lib/django/${APP}/
+SRC_DIR=/home/myhost/code/${APP}/
+VENV_DIR=/srv/var/lib/virtualenvs/myhost/
+STATIC_DIR=/srv/var/lib/django/myhost/sitestatic/
+
+# Save settings.py
+cp $APP_DIR/$APP/settings.py $APP_DIR/$APP/settings.py-save
+#cp $APP_DIR/local_settings.py $APP_DIR/local_settings.py-save
+
+rsync -av \
+	--exclude ".ropeproject*" \
+    --exclude ".DS_Store" \
+    --exclude ".idea" \
+    --exclude ".hg" \
+    --exclude "Session.vim" \
+    --exclude "*swp" \
+    --exclude "*pyc" \
+    --exclude "*xml" \
+    --exclude "*eproj" \
+    --exclude "settings.py" \
+    --exclude "whoosh/*" \
+    --exclude "local_settings.py" \
+    --exclude ".sass-cache*" \
+	$SRC_DIR \
+	$APP_DIR
+chmod -R g+rX $STATIC_DIR
+pushd
+cd $APP_DIR
+$VENV_DIR/bin/python manage.py collectstatic --noinput
+popd
+find $APP_DIR -type d -exec chown ${APP_USER} {} \;
+find $VENV_DIR -type d -exec chgrp ${APP_USER} {} \;
+find $STATIC_DIR -exec chgrp ${APP_USER} {} \;
+chmod -R ag+rX $VENV_DIR
+chmod -R g+rX $APP_DIR
+chown -R ${APP_USER}  ${STATIC_DIR}CACHE/
+chown -R ${APP_USER}  ${APP_DIR}whoosh/
+chmod -R u+rwX ${STATIC_DIR}CACHE/
+#find /srv/var/lib/django/pentasubmitter/templates/static/page_shots -type f -exec chown ${APP_USER} {} \;
+
+
+cp $APP_DIR${APP}/settings.py-save $APP_DIR${APP}/settings.py 
+
+#/etc/init.d/apache2 restart
+
+supervisorctl restart ${APP}
+
+exit 0

install/requirements.txt

-Django==1.4
+Django>=1.4
 PIL==1.1.7
-South==0.7.5
-django-compressor==1.1.2
-django-imagekit==2.0.1
-django-taggit==0.9.3
-pyScss==1.1.3
-pytz==2012b
+South
+django-compressor
+django-imagekit
+django-taggit
+pyScss
+pytz
 milkman
 easy_thumbnails
 git+http://github.com/justinlilly/django-gencal.git#egg=django_gencal
+django-floppyforms
+gunicorn
+django-floppyforms
+django-haystack
+django-smart-selects
+django-tinymce
+django-widget-tweaks
+easy-thumbnails

install/supervisor-myhost.sh

+#!/bin/bash
+
+APP_USER=myhost
+export DJANGO_SETTINGS_MODULE=djweb.settings
+#PYTHON_PATH=/srv/var/lib/django/:$PYTHON_PATH
+source /srv/var/lib/virtualenvs/myhost/bin/activate
+cd /srv/var/lib/django/myhost
+exec python manage.py run_gunicorn 127.0.0.1:8004
+
+Tools:
+
+tastypie-queryset-client

src/booker/models.py

 
 
 class HostSchedule(models.Model):
-    """schedule host per user"""
+    """schedule Host per MyHostUser"""
 
     PRIORITIES = (
         ('1', 'highest'),
 
     )
 
+    user = models.ForeignKey(
+        MyHostUser,
+        verbose_name=_("Host User"),
+        help_text=_(u"User that books the host.")
+    )
+
+
     host = models.ForeignKey(
         Host,
         verbose_name=_("Host"),
         help_text=_(u"Host to schedule.")
     )
 
-    user = models.ForeignKey(
-        MyHostUser,
-        verbose_name=_("Host User"),
-        help_text=_(u"User that books the host.")
-    )
-
     comment = models.TextField(
         verbose_name=_("Comment"),
         help_text=_(u"Why is the host scheduled?")

src/djweb/__init__.py

Empty file removed.

src/djweb/search_sites.py

-# -*- coding: utf-8 -*-
-
-"""Search using Haystack"""
-
-import haystack
-from haystack import site, indexes
-import datetime
-
-__author__ = 'a8'
-
-haystack.autodiscover()
-

src/djweb/settings.py

-# Django settings for djweb project.
-import os
-import pytz
-
-DEBUG = True
-TEMPLATE_DEBUG = DEBUG
-
-gettext = lambda s: s
-
-BASEDIR = os.path.sep.join(os.path.abspath(
-    os.path.dirname(__file__)).split(os.path.sep)[:-1])
-
-ADMINS = (
-    ('Frank Becker', 'fb@alien8.de')
-)
-
-MANAGERS = ADMINS
-
-DATABASES = {
-    'default': {
-        'ENGINE': 'django.db.backends.sqlite3',  # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
-        'NAME': 'database/db.sqlite3',           # Or path to database file if using sqlite3.
-        'USER': '',                      # Not used with sqlite3.
-        'PASSWORD': '',                  # Not used with sqlite3.
-        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
-        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
-    }
-}
-
-# Local time zone for this installation. Choices can be found here:
-# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
-# although not all choices may be available on all operating systems.
-# On Unix systems, a value of None will cause Django to use the same
-# timezone as the operating system.
-# If running in a Windows environment this must be set to the same as your
-# system time zone.
-TIME_ZONE = 'Europe/Berlin'
-
-# Language code for this installation. All choices can be found here:
-# http://www.i18nguy.com/unicode/language-identifiers.html
-LANGUAGE_CODE = 'en-us'
-
-SITE_ID = 1
-
-# If you set this to False, Django will make some optimizations so as not
-# to load the internationalization machinery.
-USE_I18N = True
-
-# If you set this to False, Django will not format dates, numbers and
-# calendars according to the current locale.
-USE_L10N = True
-
-# If you set this to False, Django will not use timezone-aware datetimes.
-USE_TZ = True
-
-# Absolute filesystem path to the directory that will hold user-uploaded files.
-# Example: "/home/media/media.lawrence.com/media/"
-MEDIA_ROOT = os.path.join(BASEDIR, "media/")
-
-# URL that handles the media served from MEDIA_ROOT. Make sure to use a
-# trailing slash.
-# Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
-MEDIA_URL = '/media/'
-
-FIXTURE_DIRS = [os.path.join(BASEDIR, 'fixtures')]
-
-# Absolute path to the directory static files should be collected to.
-# Don't put anything in this directory yourself; store your static files
-# in apps' "static/" subdirectories and in STATICFILES_DIRS.
-# Example: "/home/media/media.lawrence.com/static/"
-STATIC_ROOT = os.path.join(BASEDIR, "sitestatic/")
-
-# URL prefix for static files.
-# Example: "http://media.lawrence.com/static/"
-STATIC_URL = '/static/'
-
-# Additional locations of static files
-STATICFILES_DIRS = (
-    #os.path.join(BASEDIR, "static/"),
-    ('css', BASEDIR + '/templates/css'),
-    ('js', BASEDIR + '/templates/js'),
-    ('images', BASEDIR + '/templates/images'),
-    ('img', BASEDIR + '/templates/img'),
-    ('ico', BASEDIR + '/templates/ico'),
-    # Put strings here, like "/home/html/static" or "C:/www/django/static".
-    # Always use forward slashes, even on Windows.
-    # Don't forget to use absolute paths, not relative paths.
-)
-
-# List of finder classes that know how to find static files in
-# various locations.
-STATICFILES_FINDERS = (
-    'django.contrib.staticfiles.finders.FileSystemFinder',
-    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
-    'compressor.finders.CompressorFinder',
-#    'django.contrib.staticfiles.finders.DefaultStorageFinder',
-)
-
-# Make this unique, and don't share it with anybody.
-SECRET_KEY = '#1af*)$i4)9fm&amp;p&amp;ng*8xsn50g9fjcv51m(od&amp;m9_t9^f@#nv4'
-
-# List of callables that know how to import templates from various sources.
-TEMPLATE_LOADERS = (
-    'django.template.loaders.filesystem.Loader',
-    'django.template.loaders.app_directories.Loader',
-#     'django.template.loaders.eggs.Loader',
-)
-
-TEMPLATE_CONTEXT_PROCESSORS = (
-    'django.contrib.auth.context_processors.auth',
-    'django.core.context_processors.i18n',
-    'django.core.context_processors.request',
-    'django.core.context_processors.media',
-    'django.core.context_processors.static',
-)
-MIDDLEWARE_CLASSES = (
-    'django.middleware.common.CommonMiddleware',
-    'django.contrib.sessions.middleware.SessionMiddleware',
-    'django.middleware.csrf.CsrfViewMiddleware',
-    'django.contrib.auth.middleware.AuthenticationMiddleware',
-    'django.contrib.messages.middleware.MessageMiddleware',
-    # Uncomment the next line for simple clickjacking protection:
-    # 'django.middleware.clickjacking.XFrameOptionsMiddleware',
-)
-
-ROOT_URLCONF = 'djweb.urls'
-
-# Python dotted path to the WSGI application used by Django's runserver.
-WSGI_APPLICATION = 'djweb.wsgi.application'
-
-TEMPLATE_DIRS = (
-    os.path.join(BASEDIR, "templates/"),
-    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
-    # Always use forward slashes, even on Windows.
-    # Don't forget to use absolute paths, not relative paths.
-)
-
-INSTALLED_APPS = (
-    'django.contrib.auth',
-    'django.contrib.contenttypes',
-    'django.contrib.sessions',
-    'django.contrib.sites',
-    'django.contrib.messages',
-    'django.contrib.staticfiles',
-    # Uncomment the next line to enable the admin:
-    'django.contrib.admin',
-    # Uncomment the next line to enable admin documentation:
-    'django.contrib.admindocs',
-    'south',
-    'compressor',
-    'haystack',
-    'widget_tweaks',
-    'easy_thumbnails',
-    'gencal',
-    'taggit',
-    # myhosts modules
-    'hosts',
-    'booker',
-)
-
-# A sample logging configuration. The only tangible logging
-# performed by this configuration is to send an email to
-# the site admins on every HTTP 500 error when DEBUG=False.
-# See http://docs.djangoproject.com/en/dev/topics/logging for
-# more details on how to customize your logging configuration.
-LOGGING = {
-    'version': 1,
-    'disable_existing_loggers': False,
-    'filters': {
-        'require_debug_false': {
-            '()': 'django.utils.log.RequireDebugFalse'
-        }
-    },
-    'handlers': {
-        'mail_admins': {
-            'level': 'ERROR',
-            'filters': ['require_debug_false'],
-            'class': 'django.utils.log.AdminEmailHandler'
-        }
-    },
-    'loggers': {
-        'django.request': {
-            'handlers': ['mail_admins'],
-            'level': 'ERROR',
-            'propagate': True,
-        },
-    }
-}
-
-# compressor
-COMPRESS_PRECOMPILERS = (
-        ('text/x-scss', '/Users/a8/.virtualenvs/importthis/bin/pyscss -C -o {outfile} {infile}'),
-        )
-
-COMPRESS_CSS_FILTERS = ['compressor.filters.csstidy.CSSTidyFilter', ]
-HAYSTACK_INCLUDE_SPELLING = True
-HAYSTACK_SITECONF = 'djweb.search_sites'
-HAYSTACK_SEARCH_ENGINE = 'whoosh'
-HAYSTACK_WHOOSH_PATH = BASEDIR + '/whoosh'
-HAYSTACK_SEARCH_RESULTS_PER_PAGE = 8
-
-THUMBNAIL_ALIASES = {
-    '': {
-        'avatar': {'size': (50, 50), 'crop': True},
-    },
-}
-
-DEFAULT_TIMEZONE = pytz.timezone('Europe/Berlin')

src/djweb/urls.py

-# -*- coding: utf-8 -*-
-
-"""models of myhost app, holds the Django models
-"""
-from django.conf.urls import patterns, include, url
-
-# Uncomment the next two lines to enable the admin:
-from django.contrib import admin
-from django.conf import settings
-from django.conf.urls import include, patterns, url
-from django.conf.urls.static import static
-from django.contrib.staticfiles.urls import staticfiles_urlpatterns
-
-from djweb.views import StartView, home
-
-admin.autodiscover()
-urlpatterns = static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
-urlpatterns += staticfiles_urlpatterns()
-
-urlpatterns += patterns('',
-    # Examples:
-    #url(r'^$', StartView.as_view(), name='home'),
-    url(r'^$', home, name='home'),
-    # url(r'^djweb/', include('djweb.foo.urls')),
-
-    # Uncomment the admin/doc line below to enable admin documentation:
-    url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
-
-    # Uncomment the next line to enable the admin:
-    url(r'^admin/', include(admin.site.urls)),
-
-    url(r'^hosts/', include('hosts.urls')),
-)
-
-if settings.DEBUG:
-    pass
-    #urlpatterns += patterns('',
-        #(r'^' + settings.MEDIA_URL.lstrip('/'), include('appmedia.urls')),
-    #) + urlpatterns

src/djweb/views.py

-# -*- coding: utf-8 -*-
-
-"""views"""
-from django.core.urlresolvers import reverse
-from django.http import HttpResponseRedirect
-from django.template.context import RequestContext
-from django.shortcuts import render_to_response, get_object_or_404
-from django.utils import timezone
-from hosts.models import Host
-
-__author__ = 'a8'
-
-from django.views.generic import TemplateView
-from hosts.forms import SearchForm
-
-class StartView(TemplateView):
-    template_name = "start.html"
-
-    def get_context_data(self, **kwargs):
-        context = super(StartView, self).get_context_data(**kwargs)
-        context['now'] = timezone.now()
-        context['hosts'] = [e[0] for e in Host.objects.all().values_list('name')]
-        return context
-
-def home(request):
-    """look up the main search form
-
-        for the moment only hosts are looked up. More to come
-    """
-    host_found = True   # flag if host was found
-    form = SearchForm(request.POST or None)
-    if form.is_valid():
-        name = form.cleaned_data['search_string']
-        try:
-            host = Host.objects.get(name=name)
-        except Host.DoesNotExist:
-            host_found = False
-
-        if host_found:
-            return HttpResponseRedirect(
-                reverse('host-detail', kwargs={'slug': name}))
-
-    hosts = [e[0] for e in Host.objects.all().values_list('name')]
-
-    return render_to_response(
-        'start.html',
-        locals(),
-        context_instance=RequestContext(request),
-        )
-

src/djweb/wsgi.py

-"""
-WSGI config for djweb project.
-
-This module contains the WSGI application used by Django's development server
-and any production WSGI deployments. It should expose a module-level variable
-named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
-this application via the ``WSGI_APPLICATION`` setting.
-
-Usually you will have the standard Django WSGI application here, but it also
-might make sense to replace the whole Django WSGI application with a custom one
-that later delegates to the Django one. For example, you could introduce WSGI
-middleware here, or combine a Django application with an application of another
-framework.
-
-"""
-import os
-
-os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djweb.settings")
-
-# This application object is used by any WSGI server configured to use this
-# file. This includes Django's development server, if the WSGI_APPLICATION
-# setting points here.
-from django.core.wsgi import get_wsgi_application
-application = get_wsgi_application()
-
-# Apply WSGI middleware here.
-# from helloworld.wsgi import HelloWorldApplication
-# application = HelloWorldApplication(application)

src/gencal/tests.py

+from templatetags.gencal import ListCalendar
+import unittest
+import datetime
+
+class GencalBasicTest(unittest.TestCase):
+    def setUp(self):
+        obj_list = [{'date':datetime.date.today()},
+                    {'date':datetime.date.today() + datetime.timedelta(days=1)},
+                    {'date':datetime.date.today() + datetime.timedelta(days=2)}]
+
+        def get_link(self, dt):
+            if self.month_dict[dt] != []:
+                return "/home/"
+            return None
+        ListCalendar.get_link = get_link
+        
+        self.list_cal = ListCalendar(obj_list)
+
+    def test_it(self):
+        today = datetime.date.today()
+        calendar = ''.join(self.list_cal.formatmonth(today.year, today.month))
+        self.assertEqual(3, calendar.count("/home/"))
+        
+
+
+if __name__ == "__main__":
+    unittest.main()

src/hosts/forms.py

+# -*- coding: utf-8 -*-
+
+"""
+Python source code - replace this with a description of the code and write
+the code below this text.
+"""
+
+__author__ = "Frank Becker <fb@alien8.de>"
+__version__ = "Revision: 0.1"
+__date__ = "Date: 2012-07-25"
+__copyright__ = "Copyright (c) 2012 Frank Becker"
+__license__ = "Python"
+
+from django import forms
+
+class SearchForm(forms.Form):
+    search_string = forms.CharField(max_length=150)

src/hosts/migrations/0001_initial.py

+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding model 'Vendor'
+        db.create_table('hosts_vendor', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=100)),
+            ('logo', self.gf('django.db.models.fields.files.ImageField')(max_length=100, null=True, blank=True)),
+        ))
+        db.send_create_signal('hosts', ['Vendor'])
+
+        # Adding model 'CPU'
+        db.create_table('hosts_cpu', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=100)),
+            ('vendor', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['hosts.Vendor'], null=True, blank=True)),
+            ('type', self.gf('django.db.models.fields.CharField')(max_length=1)),
+        ))
+        db.send_create_signal('hosts', ['CPU'])
+
+        # Adding model 'RAM'
+        db.create_table('hosts_ram', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=100)),
+            ('vendor', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['hosts.Vendor'], null=True)),
+            ('size', self.gf('django.db.models.fields.PositiveIntegerField')(null=True)),
+            ('type', self.gf('django.db.models.fields.CharField')(max_length=3, null=True, blank=True)),
+        ))
+        db.send_create_signal('hosts', ['RAM'])
+
+        # Adding model 'MyHostUser'
+        db.create_table('hosts_myhostuser', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
+            ('ldap', self.gf('django.db.models.fields.CharField')(unique=True, max_length=25)),
+            ('avatar', self.gf('django.db.models.fields.files.ImageField')(max_length=100, null=True, blank=True)),
+            ('date_created', self.gf('django.db.models.fields.DateTimeField')()),
+            ('date_last_update', self.gf('django.db.models.fields.DateTimeField')()),
+        ))
+        db.send_create_signal('hosts', ['MyHostUser'])
+
+        # Adding model 'Tapper'
+        db.create_table('hosts_tapper', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('tapper_id', self.gf('django.db.models.fields.PositiveIntegerField')(unique=True, max_length=5)),
+            ('host_status', self.gf('django.db.models.fields.CharField')(max_length=1, null=True, blank=True)),
+            ('tapper_status', self.gf('django.db.models.fields.CharField')(max_length=12, null=True, blank=True)),
+            ('running_since', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+            ('comment', self.gf('django.db.models.fields.CharField')(max_length=100, null=True, blank=True)),
+            ('queues', self.gf('django.db.models.fields.CharField')(max_length=100, null=True, blank=True)),
+        ))
+        db.send_create_signal('hosts', ['Tapper'])
+
+        # Adding model 'Host'
+        db.create_table('hosts_host', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=100)),
+            ('hardware_db_id', self.gf('django.db.models.fields.IntegerField')()),
+            ('owner', self.gf('django.db.models.fields.related.ForeignKey')(related_name='host_owner', to=orm['hosts.MyHostUser'])),
+            ('user', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='current_host_user', null=True, to=orm['hosts.MyHostUser'])),
+            ('purpose', self.gf('django.db.models.fields.CharField')(max_length=2, null=True, blank=True)),
+            ('in_tapper', self.gf('django.db.models.fields.BooleanField')(default=False)),
+            ('tapper', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='current_tapper_data', null=True, to=orm['hosts.Tapper'])),
+            ('description', self.gf('django.db.models.fields.TextField')(blank=True)),
+            ('cpu_type', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='cpu_type', null=True, to=orm['hosts.CPU'])),
+            ('cpu_number', self.gf('django.db.models.fields.PositiveIntegerField')(null=True, blank=True)),
+            ('ram_size', self.gf('django.db.models.fields.PositiveIntegerField')(null=True, blank=True)),
+            ('harddisk_size', self.gf('django.db.models.fields.PositiveIntegerField')(null=True, blank=True)),
+            ('location', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True)),
+            ('kvm', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True)),
+            ('couch_id', self.gf('django.db.models.fields.CharField')(max_length=50, blank=True)),
+            ('time_added', self.gf('django.db.models.fields.DateTimeField')()),
+            ('time_change', self.gf('django.db.models.fields.DateTimeField')()),
+            ('time_last_ping', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+            ('time_last_tapper_update', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+        ))
+        db.send_create_signal('hosts', ['Host'])
+
+        # Adding M2M table for field ram_types on 'Host'
+        db.create_table('hosts_host_ram_types', (
+            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+            ('host', models.ForeignKey(orm['hosts.host'], null=False)),
+            ('ram', models.ForeignKey(orm['hosts.ram'], null=False))
+        ))
+        db.create_unique('hosts_host_ram_types', ['host_id', 'ram_id'])
+
+        # Adding model 'NetworkCard'
+        db.create_table('hosts_networkcard', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=100)),
+            ('mac', self.gf('django.db.models.fields.CharField')(max_length=12, null=True, blank=True)),
+            ('vendor', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['hosts.Vendor'], null=True)),
+        ))
+        db.send_create_signal('hosts', ['NetworkCard'])
+
+        # Adding model 'GraphicBoard'
+        db.create_table('hosts_graphicboard', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=100)),
+            ('vendor', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['hosts.Vendor'], null=True)),
+            ('type', self.gf('django.db.models.fields.CharField')(max_length=1)),
+        ))
+        db.send_create_signal('hosts', ['GraphicBoard'])
+
+        # Adding model 'BIOS'
+        db.create_table('hosts_bios', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('vendor', self.gf('django.db.models.fields.CharField')(max_length=100)),
+            ('version', self.gf('django.db.models.fields.CharField')(max_length=100)),
+        ))
+        db.send_create_signal('hosts', ['BIOS'])
+
+        # Adding model 'SocketType'
+        db.create_table('hosts_sockettype', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=50)),
+        ))
+        db.send_create_signal('hosts', ['SocketType'])
+
+        # Adding model 'NorthBridge'
+        db.create_table('hosts_northbridge', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=50)),
+        ))
+        db.send_create_signal('hosts', ['NorthBridge'])
+
+        # Adding model 'SouthBridge'
+        db.create_table('hosts_southbridge', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(max_length=50)),
+        ))
+        db.send_create_signal('hosts', ['SouthBridge'])
+
+
+    def backwards(self, orm):
+        # Deleting model 'Vendor'
+        db.delete_table('hosts_vendor')
+
+        # Deleting model 'CPU'
+        db.delete_table('hosts_cpu')
+
+        # Deleting model 'RAM'
+        db.delete_table('hosts_ram')
+
+        # Deleting model 'MyHostUser'
+        db.delete_table('hosts_myhostuser')
+
+        # Deleting model 'Tapper'
+        db.delete_table('hosts_tapper')
+
+        # Deleting model 'Host'
+        db.delete_table('hosts_host')
+
+        # Removing M2M table for field ram_types on 'Host'
+        db.delete_table('hosts_host_ram_types')
+
+        # Deleting model 'NetworkCard'
+        db.delete_table('hosts_networkcard')
+
+        # Deleting model 'GraphicBoard'
+        db.delete_table('hosts_graphicboard')
+
+        # Deleting model 'BIOS'
+        db.delete_table('hosts_bios')
+
+        # Deleting model 'SocketType'
+        db.delete_table('hosts_sockettype')
+
+        # Deleting model 'NorthBridge'
+        db.delete_table('hosts_northbridge')
+
+        # Deleting model 'SouthBridge'
+        db.delete_table('hosts_southbridge')
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'hosts.bios': {
+            'Meta': {'object_name': 'BIOS'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'vendor': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'version': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'hosts.cpu': {
+            'Meta': {'object_name': 'CPU'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+            'vendor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['hosts.Vendor']", 'null': 'True', 'blank': 'True'})
+        },
+        'hosts.graphicboard': {
+            'Meta': {'object_name': 'GraphicBoard'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+            'vendor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['hosts.Vendor']", 'null': 'True'})
+        },
+        'hosts.host': {
+            'Meta': {'ordering': "('name',)", 'object_name': 'Host'},
+            'couch_id': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
+            'cpu_number': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'cpu_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'cpu_type'", 'null': 'True', 'to': "orm['hosts.CPU']"}),
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'harddisk_size': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'hardware_db_id': ('django.db.models.fields.IntegerField', [], {}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'in_tapper': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'kvm': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
+            'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'host_owner'", 'to': "orm['hosts.MyHostUser']"}),
+            'purpose': ('django.db.models.fields.CharField', [], {'max_length': '2', 'null': 'True', 'blank': 'True'}),
+            'ram_size': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'ram_types': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'ram_types'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['hosts.RAM']"}),
+            'tapper': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'current_tapper_data'", 'null': 'True', 'to': "orm['hosts.Tapper']"}),
+            'time_added': ('django.db.models.fields.DateTimeField', [], {}),
+            'time_change': ('django.db.models.fields.DateTimeField', [], {}),
+            'time_last_ping': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'time_last_tapper_update': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'current_host_user'", 'null': 'True', 'to': "orm['hosts.MyHostUser']"})
+        },
+        'hosts.myhostuser': {
+            'Meta': {'object_name': 'MyHostUser'},
+            'avatar': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+            'date_created': ('django.db.models.fields.DateTimeField', [], {}),
+            'date_last_update': ('django.db.models.fields.DateTimeField', [], {}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ldap': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '25'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'hosts.networkcard': {
+            'Meta': {'object_name': 'NetworkCard'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'mac': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'vendor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['hosts.Vendor']", 'null': 'True'})
+        },
+        'hosts.northbridge': {
+            'Meta': {'object_name': 'NorthBridge'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'hosts.ram': {
+            'Meta': {'object_name': 'RAM'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'size': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True', 'blank': 'True'}),
+            'vendor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['hosts.Vendor']", 'null': 'True'})
+        },
+        'hosts.sockettype': {
+            'Meta': {'object_name': 'SocketType'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'hosts.southbridge': {
+            'Meta': {'object_name': 'SouthBridge'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'hosts.tapper': {
+            'Meta': {'object_name': 'Tapper'},
+            'comment': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+            'host_status': ('django.db.models.fields.CharField', [], {'max_length': '1', 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'queues': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+            'running_since': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'tapper_id': ('django.db.models.fields.PositiveIntegerField', [], {'unique': 'True', 'max_length': '5'}),
+            'tapper_status': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'})
+        },
+        'hosts.vendor': {
+            'Meta': {'object_name': 'Vendor'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'logo': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'taggit.tag': {
+            'Meta': {'object_name': 'Tag'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100'})
+        },
+        'taggit.taggeditem': {
+            'Meta': {'object_name': 'TaggedItem'},
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+            'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"})
+        }
+    }
+
+    complete_apps = ['hosts']

src/hosts/migrations/0002_auto__add_field_tapper_time_last_update__del_field_host_time_last_tapp.py

+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding field 'Tapper.time_last_update'
+        db.add_column('hosts_tapper', 'time_last_update',
+                      self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True),
+                      keep_default=False)
+
+        # Deleting field 'Host.time_last_tapper_update'
+        db.delete_column('hosts_host', 'time_last_tapper_update')
+
+
+    def backwards(self, orm):
+        # Deleting field 'Tapper.time_last_update'
+        db.delete_column('hosts_tapper', 'time_last_update')
+
+        # Adding field 'Host.time_last_tapper_update'
+        db.add_column('hosts_host', 'time_last_tapper_update',
+                      self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True),
+                      keep_default=False)
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'hosts.bios': {
+            'Meta': {'object_name': 'BIOS'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'vendor': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'version': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'hosts.cpu': {
+            'Meta': {'object_name': 'CPU'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+            'vendor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['hosts.Vendor']", 'null': 'True', 'blank': 'True'})
+        },
+        'hosts.graphicboard': {
+            'Meta': {'object_name': 'GraphicBoard'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+            'vendor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['hosts.Vendor']", 'null': 'True'})
+        },
+        'hosts.host': {
+            'Meta': {'ordering': "('name',)", 'object_name': 'Host'},
+            'couch_id': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
+            'cpu_number': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'cpu_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'cpu_type'", 'null': 'True', 'to': "orm['hosts.CPU']"}),
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'harddisk_size': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'hardware_db_id': ('django.db.models.fields.IntegerField', [], {}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'in_tapper': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'kvm': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
+            'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'host_owner'", 'to': "orm['hosts.MyHostUser']"}),
+            'purpose': ('django.db.models.fields.CharField', [], {'max_length': '2', 'null': 'True', 'blank': 'True'}),
+            'ram_size': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'ram_types': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'ram_types'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['hosts.RAM']"}),
+            'tapper': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'current_tapper_data'", 'null': 'True', 'to': "orm['hosts.Tapper']"}),
+            'time_added': ('django.db.models.fields.DateTimeField', [], {}),
+            'time_change': ('django.db.models.fields.DateTimeField', [], {}),
+            'time_last_ping': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'current_host_user'", 'null': 'True', 'to': "orm['hosts.MyHostUser']"})
+        },
+        'hosts.myhostuser': {
+            'Meta': {'object_name': 'MyHostUser'},
+            'avatar': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+            'date_created': ('django.db.models.fields.DateTimeField', [], {}),
+            'date_last_update': ('django.db.models.fields.DateTimeField', [], {}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ldap': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '25'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        'hosts.networkcard': {
+            'Meta': {'object_name': 'NetworkCard'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'mac': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'vendor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['hosts.Vendor']", 'null': 'True'})
+        },
+        'hosts.northbridge': {
+            'Meta': {'object_name': 'NorthBridge'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'hosts.ram': {
+            'Meta': {'object_name': 'RAM'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'size': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True', 'blank': 'True'}),
+            'vendor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['hosts.Vendor']", 'null': 'True'})
+        },
+        'hosts.sockettype': {
+            'Meta': {'object_name': 'SocketType'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'hosts.southbridge': {
+            'Meta': {'object_name': 'SouthBridge'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'hosts.tapper': {
+            'Meta': {'object_name': 'Tapper'},
+            'comment': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+            'host_status': ('django.db.models.fields.CharField', [], {'max_length': '1', 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'queues': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+            'running_since': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'tapper_id': ('django.db.models.fields.PositiveIntegerField', [], {'unique': 'True', 'max_length': '5'}),
+            'tapper_status': ('django.db.models.fields.CharField', [], {'max_length': '12', 'null': 'True', 'blank': 'True'}),
+            'time_last_update': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'hosts.vendor': {
+            'Meta': {'object_name': 'Vendor'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'logo': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'taggit.tag': {
+            'Meta': {'object_name': 'Tag'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100'})
+        },
+        'taggit.taggeditem': {
+            'Meta': {'object_name': 'TaggedItem'},
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+            'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"})
+        }
+    }
+
+    complete_apps = ['hosts']

src/hosts/migrations/__init__.py

Empty file added.

src/hosts/models.py

 from django.utils.translation import ugettext_lazy as _
 from django.contrib.auth.models import User
 from taggit.managers import TaggableManager
+#from booker.models import HostSchedule
 
 
 class Vendor(models.Model):
         null=True,
     )
 
+    running_since = models.DateTimeField(
+        verbose_name=u'running since',
+        editable=False,
+        blank=True,
+        null=True,
+    )
+
     comment = models.CharField(
         verbose_name=_(u"Comment"),
         help_text=_(u"max. 100 char comment"),
         null=True,
         )
 
+    time_last_update = models.DateTimeField(
+        verbose_name=_(u"Timestamp of last tapper update"),
+        editable=False,
+        blank=True,
+        null=True,
+    )
+
+    def __unicode__(self):
+        return u"tapper id: {0}".format(self.tapper_id,)
+
+    def save(self, **kwargs):
+        self.time_last_update = datetime.now(tz=settings.DEFAULT_TIMEZONE)
+        super(Tapper, self).save(**kwargs)
+
 
 class Host(models.Model):
     """The Host itself. Kinda the main model."""
         null=True,
     )
 
-    time_last_tapper_update = models.DateTimeField(
-        verbose_name=_(u"Timestamp of last ping"),
-        editable=False,
-        blank=True,
-        null=True,
-        )
-
     class Meta:
         ordering = ("name",)
 

src/hosts/tasks.py

+# -*- coding: utf-8 -*-
+
+"""
+run the maintenance scripts periodically
+"""
+
+from celery.task.schedules import crontab
+from celery.decorators import periodic_task
+import tools.tapper_scraper
+import tools.hwdb_scraper
+import tools.ping_hosts
+
+# this will run every minute, see http://celeryproject.org/docs/reference/celery.task.schedules.html#celery.task.schedules.crontab
+@periodic_task(run_every=crontab(hour="*", minute="5", day_of_week="*"))
+def update_from_hardwaredb():
+    print "update from hardwaredb"
+    tools.hwdb_scraper.main()
+
+@periodic_task(run_every=crontab(hour="*", minute="5", day_of_week="*"))
+def update_from_tapper():
+    print "update from tapper"
+    tools.tapper_scraper.main()
+
+@periodic_task(run_every=crontab(hour="*", minute="15", day_of_week="*"))
+def update_ping():
+    print "update from tapper"
+    tools.ping_hosts.main()
+
+def main():
+    tools.hwdb_scraper.main()
+    tools.tapper_scraper.main()
+    tools.ping_hosts.main()
+
+if __name__ == "__main__":
+    main()

src/hosts/templates/hosts/host_detail.html

+{% extends "base.html" %}
+{% load i18n %}
+{% load widget_tweaks %}
+{% load thumbnail %}
+{% load gencal %}
+
+
+{% block main_content %}
+        <div class="span3">
+            <div class="well sidebar-nav">
+            </div>
+        </div>
+        <div class="span9">
+            <div class="hero-unit">
+                <div class="span4">
+                    <h1>{{ object.name }}</h1>
+                </div>
+                <div class="span5">
+                    {% if host.user.avatar %}
+                    <img src="{{ host.user.avatar|thumbnail_url:'avatar' }}" alt="">
+                    {% endif %}
+                    {{ host.user.user.first_name }}
+                    {{ host.user.user.last_name }}
+                    - {{ host.user.ldap }}
+                </div>
+                <button class="btn btn-large btn-success">Book host now</button>
+                </button>
+            </div>
+        <div class="row-fluid frame">
+            <div class="span4">
+                {% gencal queryset 2012 06 %}
+            </div>
+            <div class="span4">
+                {% gencal queryset 2012 07 %}
+            </div>
+            <div class="span4">
+                {% gencal queryset 2012 08 %}
+            </div>
+        </div>
+    <div class="row-fluid frame">
+        <h1>Host Details</h1>
+        <p></p>
+        <ul>
+            <li>
+                last update: {{ host.time_change }}
+            </li>
+            <li>
+                Hardwaredb ID:
+                <a href="http://quecksilber.amd.com/hardwaredb/index.php?head=systems&rev_id={{ host.hardware_db_id }}">
+                 {{ host.hardware_db_id }}
+                </a>
+            </li>
+            <li>
+                CPUs: {{ host.cpu_number }}x {{ host.cpu_type.name }}
+            </li>
+            <li>
+                location: {{ host.location }}
+            </li>
+            <li>
+                Last ping: {{ host.time_last_ping|date:"r" }}
+            </li>
+        </ul>
+    </div>
+            <div class="row-fluid frame">
+                <h1>Tapper Details</h1>
+                <p></p>
+                {% if host.in_tapper %}
+                    {% with host.tapper as t %}
+                <ul>
+                    <li>
+                         Tapper ID: <a href="http://tapper/tapper/testruns/host/{{ host.name }}">{{ t.tapper_id }}</a>
+                    </li>
+                    <li>
+                        {% if t.tapper_status != 'free' %}
+                        Current testrun id: <a href="http://tapper/tapper/testruns/id/{{ t.tapper_status }}">{{ t.tapper_status }}</a>
+                        {% else %}
+                            Host is not running any tapper testrun right now
+                        {% endif %}
+                    </li>
+                    <li>
+                        Comment: {{ t.comment }}
+                    </li>
+                    <li>
+                        Queues: {{ t.queues }}
+                    </li>
+                    <li>
+                        last update: {{ t.time_last_update|date:"jS F Y H:i" }}
+                    </li>
+                </ul>
+                    {% endwith %}
+                {% else %}
+                    This host is not registered in Tapper.
+                {% endif %}
+            </div>
+        </div>
+{% endblock %}

src/hwdb_scraper.py

-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-"""models of myhost app, holds the Django models
-"""
-
-import urllib
-import urllib2
-import string
-
-from pprint import pprint
-#from django.db import models
-from datetime import datetime
-#from django.utils.translation import ugettext_lazy as _
-import pytz
-from django.contrib.auth.models import User
-from hosts.models import Host, MyHostUser, Vendor, CPU, RAM
-from BeautifulSoup import BeautifulSoup
-import re
-
-
-class System(object):
-
-    cpus_test = re.compile('\d+x(\s+|\S+)\S')
-    number_match = re.compile('^\d+')
-    unit_match = re.compile('\D+$')
-    def __init__(self, system):
-        (self.hardwaredb_id,
-        self.name,
-        self.cpus,
-        self.ram,
-        self.harddrive,
-        self.location,
-        self.purpose,
-        self.keyword,
-        self.owner) = system
-        self.__split_cpus()
-        self.ram = self.__convert_to_bytes(self.ram)
-        self.harddrive = self.__convert_to_bytes(self.harddrive)
-
-    def __split_cpus(self):
-        """
-        split cpus into type and number
-        """
-        if self.cpus_test.match(self.cpus):
-            (number, type) = self.cpus.split('x', 1)
-        else:
-            (number, type) = ('0', 'error in http://hardwaredb')
-        assert isinstance(int(number.strip()), int)
-        self.cpus = (number.strip(), type.strip())
-
-    def __convert_to_bytes(self, value_and_unit):
-        def get_covertion_rate(unit):
-            return {
-                'null': 0,
-                'KB': 2**10,
-                'MB': 2**20,
-                'GB': 2**30,
-                'TB': 2**40,
-            }.get(unit, 'null')
-
-        value = self.number_match.findall(value_and_unit)
-        if value:
-            value = value[0].strip()
-        else:
-            return 0
-
-        unit = self.unit_match.findall(value_and_unit)
-        if unit:
-            unit = unit[0].strip()
-        else:
-            return 0
-
-        return get_covertion_rate(unit.upper()) * int(value)
-
-
-def check_default_values():
-    """
-
-    Args:
-
-
-    Returns:
-
-    """
-    user = User.objects.get(username='admin')
-    if not user.first_name:
-        user.first_name = 'Frank'
-    if not user.last_name:
-        user.last_name = 'Becker'
-    user.save()
-    if not MyHostUser.objects.filter(ldap='fbecker'):
-        user = MyHostUser(user=User.objects.get(username='admin'), ldap='fbecker')
-        user.save()
-
-    if not Vendor.objects.filter(name='AMD'):
-        vendor = Vendor(name='AMD')
-        vendor.save()
-
-    if not Vendor.objects.filter(name='unknown'):
-        vendor = Vendor(name='unknown')
-        vendor.save()
-
-def get_or_create_owner(ldap_name, user='admin'):
-    """
-    create or return owner as given as owner_name.
-    If owner does not exist and user is not given, the Django user 'admin'
-    will be used.
-    """
-
-    try:
-        owner = MyHostUser.objects.get(ldap=ldap_name)
-    except MyHostUser.DoesNotExist:
-        owner = MyHostUser(user=User.objects.get(username='admin'),
-                           ldap=ldap_name)
-        owner.save()
-
-    return owner
-
-def get_or_create_cpu(name, vendor='AMD'):
-    """
-    create or return cpu as by name.
-    If owner does not exist and user is not given, the Django user 'admin'
-    will be used.
-    """
-
-    try:
-        cpu = CPU.objects.get(name=name)
-    except CPU.DoesNotExist:
-        cpu = CPU(name=name)
-        cpu.save()
-
-    return cpu
-
-def get_or_create_ram(size, vendor='unknown'):
-    """
-    return the size of RAM in bytes.
-    Later it will figure out the module type etc. However, that is
-    not registered in the http://hostdatabase.
-    """
-
-    return ram
-
-def update_host_instance(host, system):
-    """
-    Update a given host instance
-    """
-    assert isinstance(host, Host)
-    host.hardware_db_id = system.hardwaredb_id
-    host.location = system.location
-    host.owner = get_or_create_owner('fbecker', user='admin')
-    host.user = get_or_create_owner(system.owner, user='admin')
-    host.cpu_type = get_or_create_cpu(system.cpus[1], vendor='AMD')
-    host.cpu_number = int(system.cpus[0])
-    host.ram_size = system.ram
-    host.harddisk_size = system.harddrive
-    host.purpose = system.purpose
-    host.hardware_db_id=system.hardwaredb_id
-    host.tags.clear()
-    if system.keyword:
-        host.tags.add(system.keyword,)
-
-    return host
-
-
-def update_system(system):
-   """update system in myhost db"""
-   admin = User.objects.get(username='admin')
-   user = MyHostUser.objects.get(id=1)
-   host = Host.objects.get(name=system.name)
-
-   host = update_host_instance(host, system)
-   host.save()
-
-def create_system(system):
-    """create system in myhost db
-
-    Args:
-        system
-
-    Returns:
-
-    """
-    user = MyHostUser.objects.get(id=1)
-    host = Host(
-        hardware_db_id=system.hardwaredb_id,
-        name=system.name,
-        user=user,
-        owner=MyHostUser.objects.get(ldap="fbecker"),
-    )
-    host.save()
-    host = update_host_instance(host, system)
-    host.save()
-
-def get_systems_from_hardwaredb():
-    user_agent = 'Mozilla/5 (Linux 3.5) MyHost'
-    headers = {'User-Agent': user_agent}
-
-    request = urllib2.Request(
-        "http://quecksilber.amd.com/hardwaredb/", "", headers)
-    hardwaredb = urllib2.urlopen(request)
-
-    #hardwaredb = open('hardwaredb.html').read()
-    soup = BeautifulSoup(''.join(hardwaredb))
-
-    # find 3rd table
-    systems_table = soup('table')[2]
-
-    header = systems_table.find('tr').findAll('td')
-    header = [e.text for e in systems_table.findAll('tr')[2].findAll('td')]
-    system_rows = systems_table.findAll('tr')[3:]
-    systems = []
-    systems_id = []
-    id_pattern = re.compile('rev_id=\d.*')
-    for row in system_rows:
-        hwdb_link = row.find('td').find('a').get('href')
-        hwdb_id = id_pattern.search(hwdb_link)
-        if hwdb_id.group():
-            hwdb_id = hwdb_id.group().rsplit('=')[1]
-        else:
-            hwdb_id = None
-        system = [hwdb_id] + [e.text for e in row.findAll('td')]
-        systems.append(system)
-
-    #pprint(systems)
-    return systems
-
-
-def main():
-    """main()"""
-    check_default_values()
-    systems_hwdb = get_systems_from_hardwaredb()
-    systems_myhost = [e[0] for e in Host.objects.all().values_list('name')]
-
-    for system in systems_hwdb:
-        print "Updating: {0}".format(system[1])
-        if system[1] in systems_myhost:
-            update_system(System(system))
-        else:
-            create_system(System(system))
-
-    return True
-
-
-
-
-if __name__ == "__main__":
-    main()
 import sys
 
 if __name__ == "__main__":
-    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djweb.settings")
+    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings")
 
     from django.core.management import execute_from_command_line
 

src/project/__init__.py

Empty file added.

src/project/search_sites.py

+# -*- coding: utf-8 -*-
+
+"""Search using Haystack"""
+
+import haystack
+from haystack import site, indexes
+import datetime
+
+__author__ = 'a8'
+
+haystack.autodiscover()
+

src/project/settings.py

+# Django settings for djweb project.
+import os
+import pytz
+
+DEBUG = True
+TEMPLATE_DEBUG = DEBUG
+
+gettext = lambda s: s
+
+BASEDIR = os.path.sep.join(os.path.abspath(
+    os.path.dirname(__file__)).split(os.path.sep)[:-1])
+
+ADMINS = (
+    ('Frank Becker', 'fb@alien8.de')
+)
+
+MANAGERS = ADMINS
+
+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.sqlite3',  # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
+        'NAME': 'database/db.sqlite3',           # Or path to database file if using sqlite3.
+        'USER': '',                      # Not used with sqlite3.
+        'PASSWORD': '',                  # Not used with sqlite3.
+        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
+        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
+    }
+}
+
+# Local time zone for this installation. Choices can be found here:
+# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
+# although not all choices may be available on all operating systems.
+# On Unix systems, a value of None will cause Django to use the same
+# timezone as the operating system.
+# If running in a Windows environment this must be set to the same as your
+# system time zone.
+TIME_ZONE = 'Europe/Berlin'
+
+# Language code for this installation. All choices can be found here:
+# http://www.i18nguy.com/unicode/language-identifiers.html
+LANGUAGE_CODE = 'en-us'
+
+SITE_ID = 1
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+
+# If you set this to False, Django will not format dates, numbers and
+# calendars according to the current locale.
+USE_L10N = True
+
+# If you set this to False, Django will not use timezone-aware datetimes.
+USE_TZ = True
+
+# Absolute filesystem path to the directory that will hold user-uploaded files.
+# Example: "/home/media/media.lawrence.com/media/"
+MEDIA_ROOT = os.path.join(BASEDIR, "media/")
+
+# URL that handles the media served from MEDIA_ROOT. Make sure to use a
+# trailing slash.
+# Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
+MEDIA_URL = '/media/'
+
+FIXTURE_DIRS = [os.path.join(BASEDIR, 'fixtures')]
+
+# Absolute path to the directory static files should be collected to.
+# Don't put anything in this directory yourself; store your static files
+# in apps' "static/" subdirectories and in STATICFILES_DIRS.
+# Example: "/home/media/media.lawrence.com/static/"
+STATIC_ROOT = os.path.join(BASEDIR, "sitestatic/")
+
+# URL prefix for static files.
+# Example: "http://media.lawrence.com/static/"
+STATIC_URL = '/static/'
+
+# Additional locations of static files
+STATICFILES_DIRS = (
+    #os.path.join(BASEDIR, "static/"),
+    ('css', BASEDIR + '/templates/css'),
+    ('js', BASEDIR + '/templates/js'),
+    ('images', BASEDIR + '/templates/images'),
+    ('img', BASEDIR + '/templates/img'),
+    ('ico', BASEDIR + '/templates/ico'),
+    # Put strings here, like "/home/html/static" or "C:/www/django/static".
+    # Always use forward slashes, even on Windows.
+    # Don't forget to use absolute paths, not relative paths.
+)
+
+# List of finder classes that know how to find static files in
+# various locations.
+STATICFILES_FINDERS = (
+    'django.contrib.staticfiles.finders.FileSystemFinder',
+    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
+    'compressor.finders.CompressorFinder',
+#    'django.contrib.staticfiles.finders.DefaultStorageFinder',
+)
+
+# Make this unique, and don't share it with anybody.
+SECRET_KEY = '#1af*)$i4)9fm&amp;p&amp;ng*8xsn50g9fjcv51m(od&amp;m9_t9^f@#nv4'
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+    'django.template.loaders.filesystem.Loader',
+    'django.template.loaders.app_directories.Loader',
+#     'django.template.loaders.eggs.Loader',
+)
+
+TEMPLATE_CONTEXT_PROCESSORS = (
+    'django.contrib.auth.context_processors.auth',
+    'django.core.context_processors.i18n',
+    'django.core.context_processors.request',
+    'django.core.context_processors.media',
+    'django.core.context_processors.static',
+)
+MIDDLEWARE_CLASSES = (
+    'django.middleware.common.CommonMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.middleware.csrf.CsrfViewMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+    # Uncomment the next line for simple clickjacking protection:
+    # 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+)
+
+ROOT_URLCONF = 'project.urls'
+
+# Python dotted path to the WSGI application used by Django's runserver.
+WSGI_APPLICATION = 'project.wsgi.application'
+
+TEMPLATE_DIRS = (
+    os.path.join(BASEDIR, "templates/"),
+    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
+    # Always use forward slashes, even on Windows.
+    # Don't forget to use absolute paths, not relative paths.
+)
+
+INSTALLED_APPS = (
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.sites',
+    'django.contrib.messages',
+    'django.contrib.staticfiles',
+    # Uncomment the next line to enable the admin:
+    'django.contrib.admin',
+    # Uncomment the next line to enable admin documentation:
+    'django.contrib.admindocs',
+    'south',
+    'compressor',
+    'haystack',
+    'widget_tweaks',
+    'easy_thumbnails',
+    'gencal',
+    'taggit',
+    'floppyforms',
+    #'crispyforms',
+    # myhosts modules
+    'hosts',
+    'booker',
+)
+
+# A sample logging configuration. The only tangible logging
+# performed by this configuration is to send an email to
+# the site admins on every HTTP 500 error when DEBUG=False.
+# See http://docs.djangoproject.com/en/dev/topics/logging for
+# more details on how to customize your logging configuration.
+LOGGING = {
+    'version': 1,
+    'disable_existing_loggers': False,
+    'filters': {
+        'require_debug_false': {
+            '()': 'django.utils.log.RequireDebugFalse'
+        }
+    },
+    'handlers': {
+        'mail_admins': {
+            'level': 'ERROR',
+            'filters': ['require_debug_false'],
+            'class': 'django.utils.log.AdminEmailHandler'
+        }
+    },
+    'loggers': {
+        'django.request': {
+            'handlers': ['mail_admins'],
+            'level': 'ERROR',
+            'propagate': True,
+        },
+    }
+}
+
+# compressor
+COMPRESS_PRECOMPILERS = (
+        ('text/x-scss', '/Users/a8/.virtualenvs/importthis/bin/pyscss -C -o {outfile} {infile}'),
+        )
+
+COMPRESS_CSS_FILTERS = ['compressor.filters.csstidy.CSSTidyFilter', ]
+HAYSTACK_INCLUDE_SPELLING = True
+HAYSTACK_SITECONF = 'project.search_sites'
+HAYSTACK_SEARCH_ENGINE = 'whoosh'
+HAYSTACK_WHOOSH_PATH = BASEDIR + '/whoosh'
+HAYSTACK_SEARCH_RESULTS_PER_PAGE = 8
+
+THUMBNAIL_ALIASES = {
+    '': {
+        'avatar': {'size': (50, 50), 'crop': True},
+    },
+}
+
+DEFAULT_TIMEZONE = pytz.timezone('Europe/Berlin')

src/project/urls.py

+# -*- coding: utf-8 -*-
+
+"""models of myhost app, holds the Django models
+"""
+from django.conf.urls import patterns, include, url
+
+# Uncomment the next two lines to enable the admin:
+from django.contrib import admin
+from django.conf import settings
+from django.conf.urls import include, patterns, url
+from django.conf.urls.static import static
+from django.contrib.staticfiles.urls import staticfiles_urlpatterns
+
+from project.views import StartView, home
+
+admin.autodiscover()
+urlpatterns = static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
+urlpatterns += staticfiles_urlpatterns()
+
+urlpatterns += patterns('',
+    # Examples:
+    #url(r'^$', StartView.as_view(), name='home'),
+    url(r'^$', home, name='home'),
+    # url(r'^djweb/', include('djweb.foo.urls')),
+
+    # Uncomment the admin/doc line below to enable admin documentation:
+    url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
+
+    # Uncomment the next line to enable the admin:
+    url(r'^admin/', include(admin.site.urls)),
+
+    url(r'^hosts/', include('hosts.urls')),
+)
+
+if settings.DEBUG:
+    pass
+    #urlpatterns += patterns('',
+        #(r'^' + settings.MEDIA_URL.lstrip('/'), include('appmedia.urls')),
+    #) + urlpatterns

src/project/views.py

+# -*- coding: utf-8 -*-
+
+"""views"""
+from django.core.urlresolvers import reverse
+from django.http import HttpResponseRedirect
+from django.template.context import RequestContext
+from django.shortcuts import render_to_response, get_object_or_404
+from django.utils import timezone
+from hosts.models import Host
+
+__author__ = 'a8'
+
+from django.views.generic import TemplateView
+from hosts.forms import SearchForm
+
+class StartView(TemplateView):
+    template_name = "start.html"
+
+    def get_context_data(self, **kwargs):
+        context = super(StartView, self).get_context_data(**kwargs)
+        context['now'] = timezone.now()
+        context['hosts'] = [e[0] for e in Host.objects.all().values_list('name')]
+        return context
+
+def home(request):
+    """look up the main search form
+
+        for the moment only hosts are looked up. More to come
+    """
+    host_found = True   # flag if host was found
+    form = SearchForm(request.POST or None)
+    if form.is_valid():
+        name = form.cleaned_data['search_string']
+        try:
+            host = Host.objects.get(name=name)
+        except Host.DoesNotExist:
+            host_found = False
+
+        if host_found:
+            return HttpResponseRedirect(
+                reverse('host-detail', kwargs={'slug': name}))
+
+    hosts = [e[0] for e in Host.objects.all().values_list('name')]
+
+    return render_to_response(
+        'start.html',
+        locals(),
+        context_instance=RequestContext(request),
+        )
+

src/project/wsgi.py

+"""
+WSGI config for djweb project.
+
+This module contains the WSGI application used by Django's development server
+and any production WSGI deployments. It should expose a module-level variable
+named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
+this application via the ``WSGI_APPLICATION`` setting.
+
+Usually you will have the standard Django WSGI application here, but it also
+might make sense to replace the whole Django WSGI application with a custom one
+that later delegates to the Django one. For example, you could introduce WSGI
+middleware here, or combine a Django application with an application of another
+framework.
+
+"""
+import os
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djweb.settings")
+
+# This application object is used by any WSGI server configured to use this
+# file. This includes Django's development server, if the WSGI_APPLICATION
+# setting points here.
+from django.core.wsgi import get_wsgi_application
+application = get_wsgi_application()
+
+# Apply WSGI middleware here.
+# from helloworld.wsgi import HelloWorldApplication
+# application = HelloWorldApplication(application)

src/tapper_scraper.py

-# -*- coding: utf-8 -*-
-
-"""
-Python source code - replace this with a description of the code and write
-the code below this text.
-"""
-from StringIO import StringIO
-import os
-import paramiko
-import pickle
-from hosts.models import Host, Tapper
-
-__author__ = "Frank Becker <fb@alien8.de>"
-__version__ = ": 0.0 $"
-__date__ = ": YDATE $"
-__copyright__ = "Copyright (c) 2012 Frank Becker"
-__license__ = "Python"
-
-
-
-def get_tapper_hosts():
-    """
-
-    Args:
-
-
-    Returns:
-
-    """
-    cmd = '/bin/bash -l -c "tapper-testrun listhost -v"'
-    host = 'tapper'
-    user = 'tapper'
-    passwd = 'tapper'
-    ssh = paramiko.SSHClient()
-
-    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
-    ssh.load_host_keys(os.path.expanduser(os.path.join("~", ".ssh", "known_hosts")))
-    ssh.connect(host, username=user, password=passwd)
-    stdin, stdout, stderr = ssh.exec_command(cmd)
-    stdin.flush()
-    error = stderr.read()
-    #Reading the error stream of the executed command
-    if len(error):
-        print "Error: {0}".format(error,)
-    ssh.close()
-    return stdout
-
-def update_host(hostname, data):
-    """
-
-    Args:
-        hostname
-
-    Returns:
-
-    """
-    host = Host.objects.get(name=hostname)
-    try:
-        tapper = Tapper.objects.get(tapper_id=data[0])