Skip to content

Commit

Permalink
Initial commit for bitbucket, moving out of my private repo (history …
Browse files Browse the repository at this point in the history
…is stripped on purpose)
  • Loading branch information
apollo13 committed Jun 30, 2010
0 parents commit 52b50b3
Show file tree
Hide file tree
Showing 33 changed files with 932 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .hgignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
\.py[oc]$
dev.db$
~$
340 changes: 340 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

Empty file added example/__init__.py
Empty file.
13 changes: 13 additions & 0 deletions example/manage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env python
import sys, os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from django.core.management import execute_manager
try:
import settings # Assumed to be in the same directory.
except ImportError:
import sys
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
sys.exit(1)

if __name__ == "__main__":
execute_manager(settings)
93 changes: 93 additions & 0 deletions example/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Django settings for example project.

import os
PATH = os.path.dirname(os.path.abspath(__file__))

#DEBUG = False
DEBUG = True
TEMPLATE_DEBUG = DEBUG

ADMINS = (
# ('Your Name', '[email protected]'),
('apollo13', '[email protected]'),
)

MANAGERS = ADMINS

DATABASE_ENGINE = 'sqlite3' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
DATABASE_NAME = 'dev.db' # Or path to database file if using sqlite3.
DATABASE_USER = '' # Not used with sqlite3.
DATABASE_PASSWORD = '' # Not used with sqlite3.
DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3.
DATABASE_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.
# If running in a Windows environment this must be set to the same as your
# system time zone.
TIME_ZONE = 'America/Chicago'

# 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

# Absolute path to the directory that holds media.
# Example: "/home/media/media.lawrence.com/"
MEDIA_ROOT = ''

# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash if there is a path component (optional in other cases).
# Examples: "http://media.lawrence.com", "http://example.com/media/"
MEDIA_URL = ''

# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
# trailing slash.
# Examples: "http://foo.com/media/", "/media/".
ADMIN_MEDIA_PREFIX = '/media/'

# Make this unique, and don't share it with anybody.
SECRET_KEY = 'm)mt5+(7xw-iltqmeu)mxt&e4#wu4svxf)l$d4zo*bu_$8=^=a'

# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.load_template_source',
'django.template.loaders.app_directories.load_template_source',
# 'django.template.loaders.eggs.load_template_source',
)

MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
)

ROOT_URLCONF = 'example.urls'

TEMPLATE_DIRS = (
# 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.
PATH + '/templates',
)

INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.admin',
'django.contrib.admindocs',
'mountain.core',
'mountain.monitor',
'mountain.packages',
)

EMAIL_HOST='localhost'
EMAIL_PORT=1025
1 change: 1 addition & 0 deletions example/templates/404.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
404 - Not Found
1 change: 1 addition & 0 deletions example/templates/500.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Server Error
17 changes: 17 additions & 0 deletions example/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from django.conf.urls.defaults import *

# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()

import mountain.core.urls

urlpatterns = patterns('',
# Example:
# (r'^example/', include('example.foo.urls')),

(r'^admin/doc/', include('django.contrib.admindocs.urls')),
(r'^admin/', include(admin.site.urls)),

(r'^', include(mountain.core.urls))
)
Empty file added mountain/__init__.py
Empty file.
2 changes: 2 additions & 0 deletions mountain/core/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from mountain.core import registration

36 changes: 36 additions & 0 deletions mountain/core/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from django.contrib import admin
from django.utils.simplejson import dumps

from mountain.core.models import *
from mountain.core.utils import hash_types

def force_resync(modeladmin, request, queryset):
for comp in queryset:
Message.objects.create(computer=comp, message=dumps({
# TODO: Fix operation-id
'type': 'resynchronize', 'operation-id': 1}))

def confirm_computer(modeladmin, request, queryset):
queryset.update(confirmed=True)
for comp in queryset:
plugins = comp.company.activated_plugins.values_list('identifier', flat=True).order_by('identifier')
Message.objects.create(computer=comp, message=dumps({
'type': 'registration-done'}))

class ComputerAdmin(admin.ModelAdmin):
actions = [force_resync, confirm_computer]
exclude = ('confirmed', 'client_accepted_types', 'client_accepted_types_hash')

class CompanyAdmin(admin.ModelAdmin):
exclude = ('activated_plugins_hash',)

def save_model(self, request, obj, form, change):
plugins = [i.identifier for i in form.cleaned_data['activated_plugins']]
plugins.sort()
obj.activated_plugins_hash = hash_types(plugins).encode('hex')
obj.save()


admin.site.register(Computer, ComputerAdmin)
admin.site.register(Company, CompanyAdmin)
admin.site.register(Message)
3 changes: 3 additions & 0 deletions mountain/core/management.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from mountain.core.utils import register_messagetype

register_messagetype('register')
64 changes: 64 additions & 0 deletions mountain/core/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from django.db import models
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _

class AcceptedTypes(models.Model):
"""This models holds the message types which are supported by this server.
They are selectable through the company model, so each customer can
suppport different types (eg. not everyone needs a hardware inventory)
"""
identifier = models.CharField(max_length=255)

def __unicode__(self):
return self.identifier

class Company(models.Model):
"""Company is used to define administrator and the activated plugins.
"""
verbose_name = models.CharField(_("verbose name"), max_length=255)
account_name = models.CharField(_("account name"), max_length=255, unique=True)
registration_password = models.CharField(_("registration password"), max_length=255)
administratos = models.ManyToManyField(User, verbose_name=_("administrators"))
activated_plugins = models.ManyToManyField(AcceptedTypes, verbose_name=_("activated plugins"))
activated_plugins_hash = models.CharField(max_length=255)

def __unicode__(self):
return self.verbose_name

class Meta:
verbose_name = _("Company")
verbose_name_plural = _("Companies")

class Computer(models.Model):
"""Represents one Computer, information is pulled from the client
registration request. Secure id is used to identify the computer,
if someone can sniff this id, he __will__ be able to connect as
this computer.
"""
company = models.ForeignKey(Company, verbose_name=_("company"))
computer_title = models.CharField(_("computer title"), max_length=255)
hostname = models.CharField(_("hostname"), max_length=255, unique=True)
# TODO: uniqness of secureid
secure_id = models.TextField(_("secure id"))
insecure_id = models.CharField(_("insecure id"), max_length=36, unique=True)
client_accepted_types = models.TextField()
client_accepted_types_hash = models.CharField(max_length=255)
confirmed = models.BooleanField(_("confirmed"), default=False)

def __unicode__(self):
return self.computer_title

class Meta:
verbose_name = _("Computer")
verbose_name_plural = _("Computers")

class Message(models.Model):
"""Message which is queued and should get send to the client"""
computer = models.ForeignKey(Computer, verbose_name=_("computer"))
message = models.TextField(_("message"), help_text=_("JSON encoded"))

class Meta:
verbose_name = _("Message")
verbose_name_plural = _("Messages")


35 changes: 35 additions & 0 deletions mountain/core/registration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import uuid, string
from random import choice

from django.utils.simplejson import dumps

from mountain.core.models import Computer, Company
from mountain.core.signals import message_available
from mountain.core.utils import MessageType, hash_types

CHARS = string.ascii_letters + string.digits + string.punctuation

def handle_registration(sender, computer, request_data, msg_data, **kwargs):
try:
company = Company.objects.get(account_name=msg_data['account_name'])
except Company.DoesNotExist:
return [{'type':'registration', 'info':'unknown-account'}]

if msg_data['registration_password'] != company.registration_password:
return [{'type':'registration', 'info':'unknown-account'}]

comp,created = Computer.objects.get_or_create(
hostname=msg_data['hostname'], company=company)
comp.computer_title=msg_data['computer_title']
comp.secure_id= ''.join([choice(CHARS) for i in range(1600)])
comp.insecure_id=str(uuid.uuid4())
# TODO: this is only set on registration, but we need to check if the client changed them
comp.client_accepted_types = dumps(request_data['client-accepted-types'])
comp.client_accepted_types_hash = hash_types(request_data['client-accepted-types']).encode('hex')
comp.save()


return [{'type':'set-id', 'id':comp.secure_id,
'insecure-id':comp.insecure_id}]

message_available.connect(handle_registration, sender=MessageType("register"))
2 changes: 2 additions & 0 deletions mountain/core/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# generate one with uuid.uuid1() and move into global settings.py
SERVER_UUID = 'ecd4be8b-2de9-11de-9cd3-0018f3cfc4db'
3 changes: 3 additions & 0 deletions mountain/core/signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import django.dispatch

message_available = django.dispatch.Signal(providing_args=['computer', 'request_data', 'msg_data'])
8 changes: 8 additions & 0 deletions mountain/core/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.conf.urls.defaults import *

from views import message_system, ping

urlpatterns = patterns('',
(r'message-system/?$', message_system),
(r'ping/?$', ping),
)
45 changes: 45 additions & 0 deletions mountain/core/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from hashlib import md5

from landscape.lib.bpickle import dumps

from django.http import HttpResponse

from mountain.core.settings import SERVER_UUID
from mountain.core.models import AcceptedTypes

def render_messages(messages, computer=None, append_uuid=True):
"""Updates the answer with the server-uuid, pickles it and returns the
HttpReponse.
"""
ret = {'messages': messages}
if append_uuid:
ret.update({'server-uuid': SERVER_UUID})
if computer:
ret.update({'client-accepted-types-hash': computer.client_accepted_types_hash.decode('hex')})
return HttpResponse(dumps(ret))

def MessageType(type, __instance_cache={}):
"""Use this to register your receiver function to a string.
"""
instance = __instance_cache.setdefault(type, object())
return instance

def hash_types(types):
"""The client only sends the hashed server types, we do the same,
compare them and only send new types if the types difer.
"""
m = md5()
m.update(";".join(types))
return m.digest()

def register_messagetype(type):
from django.db.models.signals import post_syncdb
from mountain.core import models

def install_type(sender, app, created_models, verbosity=0, **kwargs):
if verbosity>=1:
print "Installing message type %s" % type
AcceptedTypes.objects.get_or_create(identifier=type)

post_syncdb.connect(install_type, sender=models, weak=False)

Loading

0 comments on commit 52b50b3

Please sign in to comment.