@@ -15,25 +15,52 @@

import requests

from django.utils.simplejson import loads
from django.utils.simplejson import loads, dumps
from django.core.urlresolvers import reverse
from django.core.serializers.json import DjangoJSONEncoder

from djangorestframework import status

from openrelay_resources.models import Resource, Version
from openrelay_resources.exceptions import ORInvalidResourceFile
from core.runtime import gpg
from django_gpg import (KeyFetchingError, GPGVerificationError,
GPGDecryptionError)

from server_talk.models import LocalNode, Sibling, NetworkResourceVersion
from server_talk.conf.settings import PORT, IPADDRESS
from server_talk.conf.settings import PORT, IPADDRESS, KEY_PASSPHRASE
from server_talk.exceptions import (AnnounceClientError, NoSuchNode,
HeartbeatError, InventoryHashError, ResourceListError,
NetworkResourceNotFound, NetworkResourceDownloadError, SiblingsHashError,
SiblingsListError)
SiblingsListError, NodeDataPackageError)
from server_talk.literals import NODE_STATUS_UP

logger = logging.getLogger(__name__)


def decrypt_request_data(signed_data):
try:
result = gpg.verify(signed_data, retry=True)
result = gpg.decrypt(signed_data)
except (KeyFetchingError, GPGVerificationError):
logger.error('got verify exception')
raise NodeDataPackageError('package signature failure')
except GPGDecryptionError:
logger.error('got GPGDecryptionError')
raise NodeDataPackageError('package decryption failure')
else:
try:
return result.pubkey_fingerprint, loads(result.data)
except ValueError:
logger.error('non JSON data')
logger.debug('got: %s' % result.data)
return result.pubkey_fingerprint, {}


def prepare_package(package):
return {'signed_data': gpg.sign(dumps(package, cls=DjangoJSONEncoder), key=LocalNode().public_key, passphrase=KEY_PASSPHRASE).data}


class NetworkCall(object):
def find_resource(self, uuid):
try:
@@ -76,46 +103,50 @@ def get_service_url(self, service_name, *args, **kwargs):
return urlparse.urlunparse(['http', self.get_full_ip_address(), reverse(service_name, *args, **kwargs), '', '', ''])

def get_id_package(self):
local_node = LocalNode.get()
return {
return prepare_package({
'ip_address': IPADDRESS,
'port': PORT,
'uuid': local_node.uuid,
}

})

def announce(self):
'''
Announce the local node to another OpenRelay node
'''
full_ip_address = self.get_full_ip_address()
url = self.get_service_url('service-announce')
try:
response = requests.post(url, data=self.get_id_package())
signed_data = self.get_id_package()
logger.debug('signed data: %s' % signed_data)
response = requests.post(url, data=signed_data)
except requests.ConnectionError:
logger.error('unable to connect to url: %s' % url)
raise AnnounceClientError('Unable to join network')

if response.status_code == status.OK:
node_answer = loads(response.content)
if node_answer['uuid'] == LocalNode.get().uuid:
logger.error('announce service on node with uuid: %s and url: %s, responded the same UUID as the local server' % (node_answer['uuid'], full_ip_address))
raise AnnounceClientError('Remote and local nodes identity conflict')
else:
sibling_data = {
'ip_address': node_answer['ip_address'],
'port': node_answer['port'],
'name': node_answer['name'],
'email': node_answer['email'],
'comment': node_answer['comment'],
}
sibling, created = Sibling.objects.get_or_create(uuid=node_answer['uuid'], defaults=sibling_data)
if not created:
sibling.ip_address = sibling_data['ip_address']
sibling.port = sibling_data['port']
sibling.save()
else:
logger.error('announce service on remote node responded with a non OK code')
raise AnnounceClientError('Unable to join network')
if response.status_code == status.OK:
try:
result = loads(response.content)
fingerprint, node_answer = decrypt_request_data(result['signed_data'])
except NodeDataPackageError:
raise AnnounceClientError('id package signature failure')
except ValueError:
raise AnnounceClientError('non JSON response')
else:
if fingerprint == LocalNode().uuid:
logger.error('announce service on node with uuid: %s and url: %s, responded the same UUID as the local server' % (node_answer['uuid'], full_ip_address))
raise AnnounceClientError('Remote and local nodes identity conflict')
else:
sibling_data = {
'ip_address': node_answer['ip_address'],
'port': node_answer['port'],
}
sibling, created = Sibling.objects.get_or_create(uuid=fingerprint, defaults=sibling_data)
if not created:
sibling.ip_address = sibling_data['ip_address']
sibling.port = sibling_data['port']
sibling.save()
else:
logger.error('announce service on remote node responded with a non OK code')
raise AnnounceClientError('Unable to join network')

def heartbeat(self):
'''
@@ -124,12 +155,19 @@ def heartbeat(self):
url = self.get_service_url('service-heartbeat')
try:
logger.debug('calling heartbeat service on url: %s' % url)
response = requests.post(url, data=self.get_id_package())
request = requests.post(url, data=self.get_id_package())
logger.debug('received heartbeat from url: %s' % url)
return loads(response.content)
except requests.ConnectionError:
logger.error('unable to connect to url: %s' % url)
raise HeartbeatError('Unable to query node')
else:
try:
response = loads(request.content)
return decrypt_request_data(response['signed_data'])
except ValueError:
raise HeartbeatError('non JSON data')
except NodeDataPackageError, exc:
raise HeartbeatError(exc)

def inventory_hash(self):
'''
@@ -138,12 +176,19 @@ def inventory_hash(self):
url = self.get_service_url('service-inventory_hash')
try:
logger.debug('calling inventory_hash service on url: %s' % url)
response = requests.post(url, data=self.get_id_package())
request = requests.post(url, data=self.get_id_package())
logger.debug('received inventory_hash from url: %s' % url)
return loads(response.content)
except requests.ConnectionError:
logger.error('unable to connect to url: %s' % url)
raise InventoryHashError('Unable to query node')
else:
try:
response = loads(request.content)
return decrypt_request_data(response['signed_data'])
except ValueError:
raise InventoryHashError('non JSON data')
except NodeDataPackageError, exc:
raise InventoryHashError(exc)

def siblings_hash(self):
'''
@@ -152,12 +197,19 @@ def siblings_hash(self):
url = self.get_service_url('service-siblings_hash')
try:
logger.debug('calling siblings_hash service on url: %s' % url)
response = requests.post(url, data=self.get_id_package())
request = requests.post(url, data=self.get_id_package())
logger.debug('received siblings_hash from url: %s' % url)
return loads(response.content)
except requests.ConnectionError:
logger.error('unable to connect to url: %s' % url)
raise InventoryHashError('Unable to query node')
raise SiblingsHashError('Unable to query node')
else:
try:
response = loads(request.content)
return decrypt_request_data(response['signed_data'])
except ValueError:
raise SiblingsHashError('non JSON data')
except NodeDataPackageError, exc:
raise SiblingsHashError(exc)

def resource_list(self):
'''
@@ -166,12 +218,19 @@ def resource_list(self):
url = self.get_service_url('version-root')
try:
logger.debug('calling resource_list service on url: %s' % url)
response = requests.post(url, data=self.get_id_package())
request = requests.post(url, data=self.get_id_package())
logger.debug('received resource_list from url: %s' % url)
return loads(response.content)
except requests.ConnectionError:
logger.error('unable to connect to url: %s' % url)
raise ResourceListError('Unable to query node')
else:
try:
response = loads(request.content)
return decrypt_request_data(response['signed_data'])
except ValueError:
raise ResourceListError('non JSON data')
except NodeDataPackageError, exc:
raise ResourceListError(exc)

def siblings_list(self):
'''
@@ -180,26 +239,19 @@ def siblings_list(self):
url = self.get_service_url('sibling-root')
try:
logger.debug('calling sibling-root service on url: %s' % url)
response = requests.post(url, data=self.get_id_package())
request = requests.post(url, data=self.get_id_package())
logger.debug('received sibling-root from url: %s' % url)
return loads(response.content)
except requests.ConnectionError:
logger.error('unable to connect to url: %s' % url)
raise SiblingListError('Unable to query node')

def heartbeat(self):
'''
Check a host's availability and cpu load
'''
url = self.get_service_url('service-heartbeat')
try:
logger.debug('calling heartbeat service on url: %s' % url)
response = requests.post(url, data=self.get_id_package())
logger.debug('received heartbeat from url: %s' % url)
return loads(response.content)
except requests.ConnectionError:
logger.error('unable to connect to url: %s' % url)
raise HeartbeatError('Unable to query node')
else:
try:
response = loads(request.content)
return decrypt_request_data(response['signed_data'])
except ValueError:
raise SiblingListError('non JSON data')
except NodeDataPackageError, exc:
raise SiblingListError(exc)

def download_version(self, uuid):
'''
@@ -208,9 +260,16 @@ def download_version(self, uuid):
url = self.get_service_url('version-download', args=[uuid])
try:
logger.debug('calling version download on url: %s' % url)
response = requests.post(url, data=self.get_id_package())
request = requests.post(url, data=self.get_id_package())
logger.debug('received download from url: %s' % url)
return response.content
except requests.ConnectionError:
logger.error('unable to connect to url: %s' % url)
raise NetworkResourceDownloadError('Unable to query node')
else:
try:
response = loads(request.content)
return decrypt_request_data(response['signed_data'])
except ValueError:
raise NetworkResourceDownloadError('non JSON data')
except NodeDataPackageError, exc:
raise NetworkResourceDownloadError(exc)
@@ -2,9 +2,13 @@

from django.conf import settings

from server_talk.literals import DEFAULT_PASSPHRASE


PORT = getattr(settings, 'SERVER_PORT', 8000)
IPADDRESS = getattr(settings, 'SERVER_IPADDRESS', socket.gethostbyname(socket.gethostname()))
HEARTBEAT_QUERY_INTERVAL = getattr(settings, 'SERVER_HEARTBEAT_QUERY_INTERVAL', 10)
INVENTORY_QUERY_INTERVAL = getattr(settings, 'SERVER_INVENTORY_QUERY_INTERVAL', 30)
SIBLINGS_QUERY_INTERVAL = getattr(settings, 'SERVER_SIBLINGS_QUERY_INTERVAL', 30)
HEARTBEAT_FAILURE_THRESHOLD = getattr(settings, 'SERVER_HEARTBEAT_FAILURE_THRESHOLD', 30)
KEY_PASSPHRASE = getattr(settings, 'SERVER_KEY_PASSPHRASE', DEFAULT_PASSPHRASE)
@@ -38,3 +38,11 @@ class SiblingsHashError(ServerTalkException):

class SiblingsListError(ServerTalkException):
pass


class NodeDataPackageError(ServerTalkException):
'''
Raised for verification or decryption error
if the node communication
'''
pass
@@ -6,3 +6,7 @@
(NODE_STATUS_DOWN, _(u'Down')),
(NODE_STATUS_UP, _(u'Up')),
)

# Default passphrase to be used when users leave passphrase field blank
# of when deploying nodes with the non interactive flag
DEFAULT_PASSPHRASE = u'openrelay'
@@ -13,40 +13,55 @@
from server_talk.conf.settings import PORT
from server_talk.literals import NODE_STATUS_DOWN, NODE_STATUS_CHOICES

LOCALNODE_ID = '1'


class Nodebase(models.Model):
uuid = models.CharField(max_length=48, editable=False, verbose_name=_(u'UUID'))
name = models.CharField(max_length=255, editable=False, verbose_name=_(u'name'))
email = models.CharField(max_length=255, editable=False, verbose_name=_(u'e-mail'))
comment = models.CharField(max_length=255, editable=False, verbose_name=_(u'comment'))
name = models.CharField(max_length=255, editable=False, blank=True, verbose_name=_(u'name'))
email = models.CharField(max_length=255, editable=False, blank=True, verbose_name=_(u'e-mail'))
comment = models.CharField(max_length=255, editable=False, blank=True, verbose_name=_(u'comment'))

def __unicode__(self):
return self.uuid

@property
def public_key(self):
return Key.get(gpg, self.uuid, secret=False, search_keyservers=True)

def save(self, *args, **kwargs):
if not self.pk:
key = self.public_key
self.name = key.name
self.email = key.email
self.comment = key.comment

super(Nodebase, self).save(*args, **kwargs)

class Meta:
abstract = True
verbose_name = _(u'node')
verbose_name_plural = _(u'nodes')


class LocalNodeModelManager(models.Manager):
def get(self):
node, created = self.model.objects.get_or_create(lock_id=LOCALNODE_ID)
return node


class LocalNode(Nodebase):
class LocalNodeModel(Nodebase):
'''
This class holds information of the currenty node, the "self" node,
and it is a singleton model
'''
lock_id = models.CharField(max_length=1, default='1', editable=False, verbose_name=_(u'lock field'), unique=True)
lock_id = models.CharField(max_length=1, default=LOCALNODE_ID, editable=False, verbose_name=_(u'lock field'), unique=True)

@classmethod
def get(cls):
return cls.objects.get(lock_id='1')
objects = LocalNodeModelManager()

def save(self, *args, **kwargs):
self.id = 1
super(LocalNode, self).save()
super(LocalNodeModel, self).save()

def delete(self):
pass
@@ -63,6 +78,8 @@ class Meta(Nodebase.Meta):
verbose_name = _(u'local node')
verbose_name_plural = _(u'local node')

LocalNode = LocalNodeModel.objects.get


class Sibling(Nodebase):
ip_address = models.IPAddressField(verbose_name=_(u'URL'))
@@ -6,7 +6,8 @@
from lock_manager import Lock, LockError
from openrelay_resources.literals import TIMESTAMP_SEPARATOR

from server_talk.exceptions import HeartbeatError, InventoryHashError
from server_talk.exceptions import (HeartbeatError, InventoryHashError,
SiblingsHashError, SiblingsListError, ResourceListError)
from server_talk.api import RemoteCall
from server_talk.models import LocalNode, Sibling, NetworkResourceVersion, ResourceHolder
from server_talk.literals import NODE_STATUS_DOWN, NODE_STATUS_UP
@@ -25,24 +26,27 @@ def heartbeat_check():
oldest = siblings[0]
try:
lock = Lock.acquire_lock(u''.join(['heartbeat_check', oldest.uuid]), 20)
node = RemoteCall(uuid=oldest.uuid)
oldest.last_heartbeat = datetime.datetime.now()
response = node.heartbeat()
oldest.cpuload = int(float(response['cpuload']))
oldest.status = NODE_STATUS_UP
oldest.failure_count = 0
oldest.save()
lock.release()
except LockError:
pass
except HeartbeatError:
oldest.status = NODE_STATUS_DOWN
oldest.failure_count += 1
oldest.save()
if oldest.failure_count > HEARTBEAT_FAILURE_THRESHOLD:
oldest.delete()

lock.release()
logging.debug('unable to acquire lock')
else:
node = RemoteCall(uuid=oldest.uuid)
try:
fingerprint, response = node.heartbeat()
except HeartbeatError:
oldest.status = NODE_STATUS_DOWN
oldest.failure_count += 1
oldest.last_heartbeat = datetime.datetime.now()
oldest.save()
if oldest.failure_count > HEARTBEAT_FAILURE_THRESHOLD:
oldest.delete()
else:
oldest.last_heartbeat = datetime.datetime.now()
oldest.cpuload = int(float(response['cpuload']))
oldest.status = NODE_STATUS_UP
oldest.failure_count = 0
oldest.save()
finally:
lock.release()


# TODO: move DB logic to api.py
@@ -56,35 +60,43 @@ def inventory_hash_check():
oldest = siblings[0]
try:
lock = Lock.acquire_lock(u''.join(['inventory_hash', oldest.uuid]), 20)
oldest.last_inventory_hash = datetime.datetime.now()
remote_api = RemoteCall(uuid=oldest.uuid)
response = remote_api.inventory_hash()
if oldest.inventory_hash != response['inventory_hash']:
# Delete this holder from all it's resources to catch
# later the ones it doesn't have anymore
ResourceHolder.objects.filter(node__uuid=oldest.uuid).delete()
for resource_item in remote_api.resource_list():
uuid, timestamp=resource_item['uuid'].split(TIMESTAMP_SEPARATOR)
resource, created = NetworkResourceVersion.objects.get_or_create(
uuid=uuid,
timestamp=timestamp,
defaults={
'metadata': dumps(resource_item.get('metadata')),
'signature_properties': dumps(resource_item.get('signature_properties')),
}
)
resource.resourceholder_set.get_or_create(node=oldest)

oldest.inventory_hash = response['inventory_hash']
oldest.save()
# Delete network resources that have no holder
NetworkResourceVersion.objects.filter(resourceholder=None).delete()
lock.release()
except LockError:
pass
except InventoryHashError:
lock.release()
logging.debug('unable to acquire lock')
else:
remote_api = RemoteCall(uuid=oldest.uuid)
try:
fingerprint, hash_response = remote_api.inventory_hash()
except InventoryHashError:
logger.error('got InventoryHashError')
else:
if oldest.inventory_hash != hash_response['inventory_hash']:
# Delete this holder from all it's resources to catch
# later the ones it doesn't have anymore
ResourceHolder.objects.filter(node__uuid=oldest.uuid).delete()
try:
fingerprint, response = remote_api.resource_list()
except ResourceListError:
logger.error('got ResourceListError')
else:
for remote_resource in response['version-list']:
uuid, timestamp=remote_resource['uuid'].split(TIMESTAMP_SEPARATOR)
resource, created = NetworkResourceVersion.objects.get_or_create(
uuid=uuid,
timestamp=timestamp,
defaults={
'metadata': dumps(remote_resource.get('metadata')),
'signature_properties': dumps(remote_resource.get('signature_properties')),
}
)
resource.resourceholder_set.get_or_create(node=oldest)

oldest.last_inventory_hash = datetime.datetime.now()
oldest.inventory_hash = hash_response['inventory_hash']
oldest.save()
# Delete network resources that have no holder
NetworkResourceVersion.objects.filter(resourceholder=None).delete()
finally:
lock.release()

def siblings_hash_check():
'''
@@ -96,30 +108,41 @@ def siblings_hash_check():
oldest = siblings[0]
try:
lock = Lock.acquire_lock(u''.join(['siblings_hash', oldest.uuid]), 20)
oldest.last_siblings_hash = datetime.datetime.now()
except LockError:
logging.debug('unable to acquire lock')
else:
remote_api = RemoteCall(uuid=oldest.uuid)
response = remote_api.siblings_hash()
if oldest.siblings_hash != response['siblings_hash']:
for remote_sibling in remote_api.siblings_list():
# Don't use a precomputed list, to a DB check each time
# to minimize posibility of race cond
# TODO: add locking
try:
fingerprint, hash_response = remote_api.siblings_hash()
except SiblingsHashError:
logger.error('got SiblingsHashError')
else:
if oldest.siblings_hash != hash_response['siblings_hash']:
try:
Sibling.objects.get(uuid=remote_sibling['uuid'])
except Sibling.DoesNotExist:
# Only add nodes not known
if remote_sibling['uuid'] != LocalNode.get().uuid:
# Don't add self
sibling = Sibling()
sibling.uuid=remote_sibling['uuid'])
sibling.port=remote_sibling['port'])
sibling.ip_address=remote_sibling['ip_address'])
sibling.save()
)
oldest.siblings_hash = response['siblings_hash']
oldest.save()
lock.release()
except LockError:
pass
except SiblingsHashError:
lock.release()
fingerprint, response = remote_api.siblings_list()
except SiblingsListError:
logger.error('got SiblingsListError')
else:
for remote_sibling in response['sibling_list']:
# Don't use a precomputed list, to a DB check each time
# to minimize posibility of race cond
# TODO: add locking
try:
Sibling.objects.get(uuid=remote_sibling['uuid'])
except Sibling.DoesNotExist:
# Only add nodes not known
if remote_sibling['uuid'] != LocalNode().uuid:
logger.debug("remote_sibling['uuid']: %s" % remote_sibling['uuid'])
logger.debug("LocalNode().uuid: %s" % LocalNode().uuid)
# Don't add self
sibling = Sibling()
sibling.uuid=remote_sibling['uuid']
sibling.port=remote_sibling['port']
sibling.ip_address=remote_sibling['ip_address']
sibling.save()

oldest.last_siblings_hash = datetime.datetime.now()
oldest.siblings_hash = hash_response['siblings_hash']
oldest.save()
finally:
lock.release()
@@ -8,6 +8,7 @@
from django.template import RequestContext
from django.contrib import messages
from django.core.urlresolvers import reverse
from django.utils.simplejson import loads

from djangorestframework.mixins import InstanceMixin, ReadModelMixin
from djangorestframework.views import View, ModelView
@@ -16,12 +17,14 @@

from openrelay_resources.models import Resource, Version
from openrelay_resources.literals import TIMESTAMP_SEPARATOR
from core.runtime import gpg
from django_gpg.exceptions import KeyDoesNotExist

from server_talk.models import LocalNode, Sibling, NetworkResourceVersion
from server_talk.forms import JoinForm
from server_talk.api import RemoteCall
from server_talk.conf.settings import PORT, IPADDRESS
from server_talk.exceptions import AnnounceClientError
from server_talk.api import RemoteCall, decrypt_request_data, prepare_package
from server_talk.conf.settings import PORT, IPADDRESS, KEY_PASSPHRASE
from server_talk.exceptions import AnnounceClientError, NodeDataPackageError
from server_talk.utils import CPUsage

logger = logging.getLogger(__name__)
@@ -92,18 +95,22 @@ def post(self, request, uuid):

class VersionRoot(View):
def post(self, request):
return [
return prepare_package(
{
'uuid': version.full_uuid,
'url': reverse('version', args=[version.full_uuid]),
'name': version.name,
'label': version.label,
'description': version.description,
'metadata': version.metadata,
'signature_properties': version.signature_properties,
'version-list': [
{
'uuid': version.full_uuid,
'url': reverse('version', args=[version.full_uuid]),
'name': version.name,
'label': version.label,
'description': version.description,
'metadata': version.metadata,
'signature_properties': version.signature_properties,
}
for version in Version.objects.all()
]
}
for version in Version.objects.all()
]
)


class VersionObject(View):
@@ -157,84 +164,130 @@ def post(self, request, uuid):

class Announce(View):
def post(self, request):
uuid = request.POST.get('uuid')
ip_address = request.POST.get('ip_address')
port = request.POST.get('port')
logger.info('received announce call from: %s @ %s' % (uuid, request.META['REMOTE_ADDR']))
if uuid and ip_address and port:
sibling_data = {'ip_address': ip_address, 'port': port}
# TODO: Verify node identity
sibling, created = Sibling.objects.get_or_create(uuid=uuid, defaults=sibling_data)
if not created:
sibling.ip_address = sibling_data['ip_address']
sibling.port = sibling_data['port']
sibling.save()
local_node = LocalNode.get()
local_node_info = {
'ip_address': IPADDRESS,
'port': PORT,
'uuid': local_node.uuid,
'name': local_node.name,
'email': local_node.email,
'comment': local_node.comment,
}
return local_node_info
logger.info('received announce call from: %s' % request.META['REMOTE_ADDR'])
signed_data = request.POST.get('signed_data')
try:
fingerprint, result = decrypt_request_data(signed_data)
except NodeDataPackageError:
logger.error('got NodeDataPackageError')
return Response(status.BAD_REQUEST)
else:
return Response(status.PARTIAL_CONTENT)
logger.info('remote node uuid is: %s' % fingerprint)
try:
sibling_data = {'ip_address': result['ip_address'], 'port': result['port']}
except KeyError:
return Response(status.PARTIAL_CONTENT)
else:
sibling, created = Sibling.objects.get_or_create(uuid=fingerprint, defaults=sibling_data)
if not created:
sibling.ip_address = sibling_data['ip_address']
sibling.port = sibling_data['port']
sibling.save()

# Send our info
local_node_info = {
'ip_address': IPADDRESS,
'port': PORT,
}
try:
return prepare_package(local_node_info)
except KeyDoesNotExist:
return Response(status.INTERNAL_SERVER_ERROR)


class Heartbeat(View):
def post(self, request):
uuid = request.GET.get('uuid')
# TODO: Reject call from non verified nodes
signed_data = request.POST.get('signed_data')
try:
fingerprint, result = decrypt_request_data(signed_data)
except NodeDataPackageError:
logger.error('got NodeDataPackageError')
return Response(status.BAD_REQUEST)
else:
uuid = fingerprint

logger.info('received heartbeat call from node: %s @ %s' % (uuid, request.META['REMOTE_ADDR']))
return {'cpuload': CPUsage()}
return prepare_package({'cpuload': str(CPUsage())})


class SiblingsHash(View):
def post(self, request):
uuid = request.GET.get('uuid')
# TODO: Reject call from non verified nodes
signed_data = request.POST.get('signed_data')
try:
fingerprint, result = decrypt_request_data(signed_data)
except NodeDataPackageError:
logger.error('got NodeDataPackageError')
return Response(status.BAD_REQUEST)
else:
uuid = fingerprint

logger.info('received siblings hash call from node: %s' % uuid)
return {
'siblings_hash': HASH_FUNCTION(
u''.join(
[
node.uuid for node in Sibling.objects.all().order_by('uuid')
]
return prepare_package(
{
'siblings_hash': HASH_FUNCTION(
u''.join(
[
node.uuid for node in Sibling.objects.all().order_by('uuid')
]
)
)
)
}
}
)


class SiblingList(View):
def post(self, request):
uuid = request.GET.get('uuid')
signed_data = request.POST.get('signed_data')
try:
fingerprint, result = decrypt_request_data(signed_data)
except NodeDataPackageError:
logger.error('got NodeDataPackageError')
return Response(status.BAD_REQUEST)
else:
uuid = fingerprint

logger.info('received siblings list call from node: %s' % uuid)
return [
{
'uuid': sibling.uuid,
'ip_address': sibling.ip_address,
'port': sibling.port,
'last_heartbeat': sibling.last_heartbeat,
'cpuload': sibling.cpuload,
'status': sibling.status,
'failure_count': sibling.failure_count,
'last_inventory_hash': sibling.last_inventory_hash,
'inventory_hash': sibling.inventory_hash,
'last_siblings_hash': sibling.last_siblings_hash,
'siblings_hash': sibling.siblings_hash,
}
for sibling in Sibling.objects.all()
]
return prepare_package(
{
'sibling_list': [
{
'uuid': sibling.uuid,
'ip_address': sibling.ip_address,
'port': sibling.port,
'last_heartbeat': sibling.last_heartbeat,
'cpuload': sibling.cpuload,
'status': sibling.status,
'failure_count': sibling.failure_count,
'last_inventory_hash': sibling.last_inventory_hash,
'inventory_hash': sibling.inventory_hash,
'last_siblings_hash': sibling.last_siblings_hash,
'siblings_hash': sibling.siblings_hash,
}
for sibling in Sibling.objects.all()
]
}
)


class InventoryHash(View):
def post(self, request):
uuid = request.GET.get('uuid')
# TODO: Reject call from non verified nodes
signed_data = request.POST.get('signed_data')
try:
fingerprint, result = decrypt_request_data(signed_data)
except NodeDataPackageError:
logger.error('got NodeDataPackageError')
return Response(status.BAD_REQUEST)
else:
uuid = fingerprint

logger.info('received inventory hash call from node: %s @ %s' % (uuid, request.META['REMOTE_ADDR']))
return {'inventory_hash': HASH_FUNCTION(u''.join([version.full_uuid for version in Version.objects.all().order_by('timestamp')]))}
return prepare_package(
{
'inventory_hash': HASH_FUNCTION(
u''.join([version.full_uuid for version in Version.objects.all().order_by('timestamp')])
)
}
)


# Interactive views - user
@@ -271,7 +324,7 @@ def node_list(request):

def node_info(request):
return render_to_response('node_list.html', {
'object_list': [LocalNode.get()],
'object_list': [LocalNode()],
'title': _(u'Local node information'),
}, context_instance=RequestContext(request))