gecos-team/gecoscc-ui

View on GitHub
gecoscc/commands/check_node_policies.py

Summary

Maintainability
F
2 wks
Test Coverage
#
# Copyright 2017, Junta de Andalucia
# http://www.juntadeandalucia.es/
#
# Authors:
#   Abraham Macias <amacias@solutia-it.es>
#
# All rights reserved - EUPL License V 1.1
# https://joinup.ec.europa.eu/software/page/eupl/licence-eupl
#

from builtins import str
import json
import requests
import re
from copy import deepcopy

from chef.exceptions import ChefServerNotFoundError
from chef import Node as ChefNode
from optparse import make_option
from distutils.version import LooseVersion

from gecoscc.management import BaseCommand
from gecoscc.utils import (_get_chef_api, toChefUsername, 
                           trace_inheritance, order_groups_by_depth,
                           check_unique_node_name_by_type_at_domain,
                           is_domain, get_domain, is_root)
from gecoscc.rules import get_username_chef_format
from bson.objectid import ObjectId
from gecoscc.models import Policy

import logging
logger = logging.getLogger()    
    
class Command(BaseCommand):
    description = """
       Check the policies data for all the workstations in the database. 
       This script must be executed on major policies updates when the changes 
       in the policies structure may
       cause problems if the Chef nodes aren't properly updated.
       
       So, the curse of action is:
       1) Import the new policies with the knife command
       2) Run the "import_policies" command.
       3) Run this "check_node_policies" command.
    """

    usage = ("usage: %prog config_uri check_node_policies --administrator user "
             "--key file.pem")

    option_list = [
        make_option(
            '-a', '--administrator',
            dest='chef_username',
            action='store',
            help='An existing chef super administrator username (like '\
            '"pivotal" user)'
        ),
        make_option(
            '-k', '--key',
            dest='chef_pem',
            action='store',
            help='The pem file that contains the chef administrator private key'
        ),
        make_option(
            '-i', '--inheritance',
            dest='inheritance',
            action='store_true',
            default=False,
            help='Check inheritance field'
        ),        
        make_option(
            '-c', '--clean-inheritance',
            dest='clean_inheritance',
            action='store_true',
            default=False,
            help='Clean inheritance field (must be used with -i)'
        ),          
        make_option(
            '--clean-variables',
            dest='clean_variables',
            action='store_true',
            default=False,
            help='Clean variables information from Chef nodes'
        ),         
    ]

    required_options = (
        'chef_username',
        'chef_pem',
    )
    
    def get_url(self, url):
        r = requests.get(url, verify=False, timeout=30)
        if r.ok:
            if hasattr(r,'text'):
                return r.text
            else:  
                return r.content                
            
        return None     
    
    def get_chef_url(self, url):
        url = url[url.index('://')+3:]
        url = url[url.index('/'):]
            
        print("url=", url)
        data = None
        try:
            data = self.api[url]
        except ChefServerNotFoundError:
            pass              
        
        return data
    
    def get_default_data(self, dotted_keys):
        # Get gecos_ws_mgmt cookbook version
        data = None
        try:
            data = self.api['/organizations/default/cookbooks/gecos_ws_mgmt']
        except ChefServerNotFoundError:
            pass              
        
        if (data is None) or (not "gecos_ws_mgmt" in data) or (
            not "versions" in data["gecos_ws_mgmt"]):
            logger.error('Can\'t get version for gecos_ws_mgmt cookbook!')
            return None
            
        last_version_number = ''
        last_version_url = ''
        for ver in data["gecos_ws_mgmt"]["versions"]:
            if last_version_number == '':
                last_version_number = ver['version']
                last_version_url = ver['url']
            elif  LooseVersion(last_version_number) < LooseVersion(
                ver['version']):
                last_version_number = ver['version']
                last_version_url = ver['url']
            
        if last_version_number == '':
            logger.error('Can\'t find last version number!')
            return None
            
        logger.info("Cookbook version: %s"%(last_version_number))
        data = self.get_chef_url(last_version_url)
        if data is None:
            logger.error('Can\'t get data for gecos_ws_mgmt cookbook!')
            return None

        if not "attributes" in data:
            logger.error('gecos_ws_mgmt cookbook data doesn\'t contain '
                         'attributes!')
            return None

        default_attr_url = ''
        for attr in data["attributes"]:
            if (attr["name"] == "default.rb" and
                attr["path"] == "attributes/default.rb"):
                default_attr_url = attr["url"] 
                
        if default_attr_url == '':
            logger.error('Can\'t find default attributes file!')
            return None
            
        data = self.get_url(default_attr_url)
        if data is None:
            logger.error('Can\'t download default attributes file!')
            return None

        # Join splited lines        
        previous_line = ''
        new_data = ''
        for line in data.split('\n'):
            if line.strip() != '' and not line.strip().startswith('#'):
                if line.startswith('  '):
                    previous_line = str(previous_line) + str(line.strip())
                else:
                    new_data += previous_line + "\n"
                    previous_line = line.strip()        
        
        data = new_data
        
        # Convert to python
        data = re.sub('\[:(?P<name>[a-zA-Z0-9_]+)\]', '["\g<name>"]', data)
        data = data.replace('true', 'True').replace('false', 'False')
        data = data.replace('true', 'True').replace('.freeze', '')
        
        # Create dictionaries
        created = []
        header = ''
        for line in data.split('\n'):
            # Line example: 
            #   default["gecos_ws_mgmt"]["misc_mgmt"]["chef_conf_res"]
            #     ["support_os"] = 
            #       ["GECOS V3", "GECOS V2", "Gecos V2 Lite", "GECOS V3 Lite"]
            if line.strip() != '':
                asignation = line.split('=')
                # Left example:
                #   default["gecos_ws_mgmt"]["misc_mgmt"]["chef_conf_res"]
                #     ["support_os"]
                left = asignation[0].strip()
                right = asignation[1].strip()
                
                # Save as dotted_key => value
                dotted_key = left.replace('"', '').replace('default[', ''
                    ).replace('][', '.').replace(']','')
                dotted_keys[dotted_key] = eval(right)
                
                # Create empty dictionaries code
                begining = 0
                position = 0
                if '[' in left:
                    position = left.index('[', begining)
                    while position > 0:
                        variable = left[0:position]
                        begining = position + 1
                        try:
                            position = left.index('[', begining)
                        except ValueError:
                            # String not found
                            position = -1
                            
                        if ((not variable in created) and 
                            len(variable) != len(left)):
                            created.append(variable)
                            header += variable+' = {}\n'
                else:
                    code = compile('%s = %s'%(left, right), '<string>', 'exec')
                    exec(code)                    
                
        data = header + data
        code = compile(data, '<string>', 'exec')
        loc = locals()
        exec(code, globals(), loc)
        default = loc['default']
        return default
    
    
    def command(self):
        # Initialization
        self.api = _get_chef_api(self.settings.get('chef.url'),
                            toChefUsername(self.options.chef_username),
                            self.options.chef_pem, False,
                            self.settings.get('chef.version'))

        self.db = self.pyramid.db
        self.referenced_data_type = {}
        self.referenced_data_type['storage_can_view'] = 'storage'
        self.referenced_data_type['repository_can_view'] = 'repository'
        self.referenced_data_type['printer_can_view'] = 'printer'
        
        # Get gecos_ws_mgmt cookbook default data structure
        default_data_dotted_keys = {}
        default_data = self.get_default_data(default_data_dotted_keys)
        if default_data is None:
            logger.error("Can't find default data!")
            return
        
        # Get all the policies structures
        logger.info('Getting all the policies structures from database...')
        dbpolicies = self.db.policies.find()
        self.policiesdata = {}
        self.slug_check = {}
        for policy in dbpolicies:
            logger.debug('Adding to dictionary: %s => %s'%(policy['_id'],
                json.dumps(policy['schema'])))
            self.policiesdata[str(policy['_id'])] = policy
            
            # Check policy slug field (must be unique)
            if policy['slug'] in self.slug_check:
                logger.error("There are more than one policy with '%s' slug!"%(
                    policy['slug']))
            else:
                slug = policy['slug']
                # The slug of the emitter policies is different from the others
                if slug == 'printer_can_view':
                    slug = 'printers_res'
                elif slug == 'storage_can_view':
                    slug = 'user_shared_folders_res'
                elif slug == 'repository_can_view':
                    slug = 'software_sources_res'
                
                self.slug_check[slug] = policy
                
            # Check policy serialization
            try:
                logger.debug('Serialized policy: %s'%(
                    json.dumps(Policy().serialize(policy))))
            except Exception as err:
                logger.error('Policy %s with slug %s can\'t be serialized: %s'%(
                    policy['_id'], policy['slug'], str(err)))
                logger.warn('Possible cause: New fields in models (Colander) '
                    ' but the import_policies command has not yet been executed'
                    ' to update schema.')
                
        if self.options.clean_inheritance:
            logger.info('Cleaning inheritance field...')
            self.db.nodes.update({"inheritance": { '$exists': True }}, {
                 '$unset': { "inheritance": {'$exist': True } }}, multi=True)
            
        if self.options.clean_variables:
            logger.info('Cleaning variables data from Chef nodes')
            for node_id in ChefNode.list():
                node = ChefNode(node_id, self.api)
                if node.normal.has_dotted('gecos_info'):
                    del node.normal['gecos_info']
                    node.save()                    
            
        
        logger.info('Checking tree...')
        # Look for the root of the nodes tree
        root_nodes = self.db.nodes.find({"path" : "root"})    
        for root in root_nodes:        
            self.check_node_and_subnodes(root)
        
        logger.info('Checking nodes that are outside the tree (missing OUs in'
                    ' the PATH)...')
        # Check node path
        nodes = self.db.nodes.find({})    
        for node in nodes:
            if not 'path' in node:
                logger.error('Node with ID: %s has no "path" attribute!'%(
                    str(node['_id'])))                
                continue

            if not 'name' in node:
                logger.error('Node with ID: %s has no "name" attribute!'%(
                    str(node['_id'])))                
                continue

            if not 'type' in node:
                logger.error('Node with ID: %s has no "type" attribute!'%(
                    str(node['_id'])))                
                continue

                
            for ou_id in node['path'].split(','):
                if ou_id == 'root':
                    continue
                    
                ou = self.db.nodes.find_one({ "_id" : ObjectId(ou_id) })    
                if not ou:
                    logger.error('Can\'t find OU %s that belongs to node path '
                                 '(node ID: %s NAME: %s)'%(str(ou_id),
                                    str(node['_id']), node['name']))                
                    continue        
        
        logger.info('Checking chef node references...')
        # Check the references to Chef nodes
        computers = self.db.nodes.find({"type" : "computer"})    
        for computer in computers:  
            if "node_chef_id" in computer:
                # Check Chef node
                computer_node = ChefNode(computer['node_chef_id'], self.api)
                logger.info("Computer: %s Chef ID: %s"%(computer['name'],
                    computer['node_chef_id']))
                if not computer_node.exists:
                    logger.error("No Chef node with ID %s!"%(
                        computer['node_chef_id']))
                
            else:
                logger.error("No Chef ID in '%s' computer!"%(computer['name']))

                
        logger.info('Checking MongoDB computer references and deprecated '
                    'policies...')
        # Check the references to computer nodes
        for node_id in ChefNode.list():
            found = False
            computers = self.db.nodes.find({"node_chef_id" : node_id})
            node_path = None
            for computer in computers:   
                found = True
                node_path = computer['path']
                
            computer_node = ChefNode(node_id, self.api)
            if not found:
                pclabel = "(No OHAI-GECOS data in the node)"
                try:
                    pclabel = "(pclabel = %s)"%(
                        computer_node.attributes.get_dotted(
                            'ohai_gecos.pclabel'))
                except KeyError:
                    pass
                        
                logger.error("No computer node for Chef ID: '%s' %s!"%(node_id,
                                                                       pclabel))
                logger.warn("Possible cause: The node has been deleted in Gecos"
                    " Control Center but not in Chef server, either because it"
                    " was in use at that time or for another unknown reason.")
        
            # Check default data for chef node
            if (not computer_node.default.to_dict() or 
                not computer_node.attributes.has_dotted('gecos_ws_mgmt')):
                logger.info("FIXED: For an unknown reason Chef node: ",
                            "%s has no default attributes."%(node_id))
                computer_node.default = default_data
                computer_node.save()
                
            # Check "updated_by" field
            attributes = computer_node.normal.to_dict()
            updated, updated_attributes = self.check_updated_by_field(node_id,
                None, attributes)
            if updated:
                computer_node.normal = updated_attributes
                computer_node.save()
            
            updated, updated_attributes = self.check_chef_node_policies(node_id,
                None, attributes)
            if updated:
                computer_node.normal = updated_attributes
                computer_node.save()
            
            if node_path is not None:
                # Check "gecos_path_ids" field
                if (not computer_node.attributes.has_dotted('gecos_path_ids')
                    or computer_node.attributes.get_dotted(
                        'gecos_path_ids') != node_path):
                    logger.info("FIXED: gecos_path_ids attribute in ",
                                "node: %s."%(node_id))
                    computer_node.attributes.set_dotted('gecos_path_ids',
                                                        node_path) 
                    computer_node.save()
    
                # Check "gecos_path_names" field
                node_path_names = 'root'
                for elm in node_path.split(','):
                    if elm == 'root':
                        continue
                    ou = self.db.nodes.find_one({'_id': ObjectId(elm)})
                    node_path_names += ',' + ou['name'] 
                
                if (not computer_node.attributes.has_dotted('gecos_path_names')
                    or computer_node.attributes.get_dotted(
                        'gecos_path_names') != node_path_names):
                    logger.info("FIXED: gecos_path_names attribute in "
                                "node: %s."%(node_id))
                    computer_node.attributes.set_dotted('gecos_path_names',
                                                        node_path_names) 
                    computer_node.save()
            
        
        logger.info('END ;)')
        
    def check_updated_by_field(self, node_id, key, attributes):
        updated = False
        if isinstance(attributes, dict):
            for attribute in attributes:
                if attribute == 'updated_by':
                    if 'group' in attributes['updated_by']:
                        # Sort groups
                        sorted_groups = order_groups_by_depth(self.db,
                            attributes['updated_by']['group'])
                        if attributes['updated_by']['group'] != sorted_groups:
                            logger.info("Sorting updated_by field for node ",
                                        "{0} - {1}!".format(node_id, key)) 
                            attributes['updated_by']['group'] = sorted_groups
                            updated = True
                else:
                    if key is None:
                        k = attribute
                    else:
                        k = key+'.'+attribute
                        
                    up, attributes[attribute] = self.check_updated_by_field(
                        node_id, k, attributes[attribute])
                    updated = (updated or up)
        
        return updated, attributes
        
    def check_chef_node_policies(self, node_id, key, attributes):
        updated = False
        if 'gecos_ws_mgmt' in attributes:
            to_delete = []
            for group in attributes['gecos_ws_mgmt']:
                for policy_slug in attributes['gecos_ws_mgmt'][group]:
                    if policy_slug not in self.slug_check:
                        logger.warn("Unknown policy slug %s found in node %s."%(
                            policy_slug, node_id))
                        logger.info("FIXED: Remove %s policy information from"\
                                    " node %s."%(policy_slug, node_id))
                        to_delete.append('%s.%s'%(group, policy_slug))
                        
            for policy in to_delete:
                group, slug = policy.split('.')
                del attributes['gecos_ws_mgmt'][group][slug]
                updated = True
        
        return updated, attributes
        
    def check_node_and_subnodes(self, node):
        '''
        Check the policies applied to a node and its subnodes
        '''        
        self.check_node(node)
        
        if node['type'] == 'ou':
            subnodes = self.db.nodes.find({"path" : "%s,%s"%(node['path'],
                                                             node['_id'])}) 
            for subnode in subnodes:
                self.check_node_and_subnodes(subnode)
        
        
    def check_node(self, node):
        '''
        Check the policies applied to a node
        '''        
        logger.info('Checking node: "%s" type:%s path: %s'%(node['name'],
            node['type'], node['path']))
        
        # Check for name duplicates
        if not check_unique_node_name_by_type_at_domain(self.db.nodes, node):
            logger.error('Duplicates found for node: "%s" type:%s path: %s'%(
                node['name'], node['type'], node['path']))
        elif not is_domain(node) and not is_root(node):
            # Check that the node name is not the same as the domain name
            domain = get_domain(node, self.db.nodes)
            if domain['name'].lower() == node['name'].lower():            
                logger.error('The node has the same name as the domain: "%s" '\
                             'type:%s path: %s'%(node['name'], node['type'],
                                                 node['path']))
        
        if self.options.inheritance:
            inheritance_node = deepcopy(node)      
        
        # Check policies
        if 'policies' in node:
            to_remove = []
            # Check the policies data
            for policy in node['policies']:
                logger.debug('Checking policy with ID: %s'%(policy))
                if not str(policy) in self.policiesdata:
                    logger.warn("Can't find %s policy data in the database! "\
                                "(probably deprecated)"%(policy))
                    to_remove.append(str(policy))
                    
                else:
                    policydata = self.policiesdata[str(policy)]
                    nodedata = node['policies'][str(policy)]
                    
                    # Emiters policies have a "name" field in the data
                    # Non emiters policies have a "title" field in the data
                    namefield = 'name'
                    if not (namefield in policydata):
                        namefield = 'title'
                    
                    if not ('name' in policydata):
                        logger.critical('Policy with ID: %s doesn\'t have a'\
                                        ' name nor title!'%(str(policy)))
                        continue;
                        
                      
                    logger.info('Checking node: "%s" Checking policy: "%s"'%(
                        node['name'], policydata[namefield]))
                    if 'DEPRECATED' in policydata[namefield]:
                        logger.warning('Using deprecated policy: %s'%(
                            policydata[namefield]))
                        
                    
                    logger.debug('Node policy data: %s'%(json.dumps(
                        node['policies'][str(policy)])))
                    
                    is_emitter_policy = False
                    emitter_policy_slug = None
                    if "is_emitter_policy" in policydata:
                        is_emitter_policy = policydata["is_emitter_policy"]
                        emitter_policy_slug = policydata["slug"]
                    
                    # Check object
                    self.check_object_property(policydata['schema'], nodedata,
                        None, is_emitter_policy, emitter_policy_slug)
                    
                    if self.options.inheritance:
                        # Check inheritance field
                        trace_inheritance(logger, self.db, 'change',
                            inheritance_node, deepcopy(policydata))

            changed = False
            for policy in to_remove:
                logger.info("FIXED: Remove %s policy information"%(policy))
                del node['policies'][str(policy)]
                changed = True
                
            if changed:
                self.db.nodes.update({'_id': ObjectId(node['_id'])},
                                     {'$set': {'policies': node['policies']}})

                            
        else:
            logger.debug('No policies in this node.')
        
        if (self.options.inheritance and ('inheritance' in inheritance_node)
            and ((not 'inheritance' in node) 
                 or (inheritance_node['inheritance'] != node['inheritance']))):
            
            # Save inheritance field
            logger.info('FIXED: updating inheritance field!')
            self.db.nodes.update({'_id': ObjectId(node['_id'])},
                {'$set': {'inheritance': inheritance_node['inheritance']}})
        
        # Check referenced nodes
        if node['type'] == 'user':
            # Check computers
            new_id_list = self.check_referenced_nodes(node['computers'],
                ['computer'], 'computers')
            difference = set(node['computers']).difference(set(new_id_list))
            if len(difference) > 0:
                logger.info('FIXED: remove %s references'%(difference))
                self.db.nodes.update({'_id': ObjectId(node['_id'])},
                    {'$set': {'computers': new_id_list}})
            
            # Check user data
            self.check_user_data(node)
            
            # Check memberof
            new_id_list = self.check_referenced_nodes(node['memberof'],
                ['group'], 'memberof')
            difference = set(node['memberof']).difference(set(new_id_list))
            if len(difference) > 0:
                logger.info('FIXED: remove %s references'%(difference))
                self.db.nodes.update({'_id': ObjectId(node['_id'])},
                    {'$set': {'memberof': new_id_list}})
            
            
        if node['type'] == 'computer':
            # Check memberof
            new_id_list = self.check_referenced_nodes(node['memberof'],
                ['group'], 'memberof')
            difference = set(node['memberof']).difference(set(new_id_list))
            if len(difference) > 0:
                logger.info('FIXED: remove %s references'%(difference))
                self.db.nodes.update({'_id': ObjectId(node['_id'])},
                    {'$set': {'memberof': new_id_list}})

            
        if node['type'] == 'group':
            # Check memberof
            new_id_list = self.check_referenced_nodes(node['memberof'],
                ['group'], 'memberof')
            difference = set(node['memberof']).difference(set(new_id_list))
            if len(difference) > 0:
                logger.info('FIXED: remove %s references'%(difference))
                self.db.nodes.update({'_id': ObjectId(node['_id'])},
                    {'$set': {'memberof': new_id_list}})
                
            
            # Check members
            new_id_list = self.check_referenced_nodes(node['members'],
                ['user', 'computer', 'group'], 'members')
            difference = set(node['members']).difference(set(new_id_list))
            if len(difference) > 0:
                logger.info('FIXED: remove %s references'%(difference))
                self.db.nodes.update({'_id': ObjectId(node['_id'])},
                    {'$set': {'members': new_id_list}})
        
    def check_user_data(self, user):
        if user['type'] != 'user':
            raise ValueError('user must be an user')
        
        if ((not 'email' in user or user['email']=='') and
            (not 'first_name' in user or user['first_name']=='') and
            (not 'last_name' in user or user['last_name']=='')):
            
            # Nothing to do
            return
        
        
        computers = self.db.nodes.find_one({ "_id" : ObjectId(user['_id']) })[
            'computers']
        for computer_id in computers:
            computer = self.db.nodes.find_one({ "_id" : ObjectId(computer_id) })
            if "node_chef_id" in computer:
                # Check Chef node
                node = ChefNode(computer['node_chef_id'], self.api)
                logger.info("Computer: %s Chef ID: %s"%(computer['name'],
                    computer['node_chef_id']))
                if not node.exists:
                    logger.error("No Chef node with ID %s!"%(
                        computer['node_chef_id']))
                else:
                    if not node.normal.has_dotted('gecos_info'):
                        node.normal.set_dotted('gecos_info', {})
                        
                    if not node.normal.has_dotted('gecos_info.users'):
                        node.normal.set_dotted('gecos_info.users', {})
                                                
                    username = get_username_chef_format(user)
                    if (not node.normal.has_dotted('gecos_info.users.%s'%(
                        username))):
                        node.normal.set_dotted('gecos_info.users.%s'%(
                            username), {})
                        
                    updated = False
                    if (not node.normal.has_dotted('gecos_info.users.%s.email'%(
                        username)) or node.normal.get_dotted(
                            'gecos_info.users.%s.email'%(
                                username)) != user['email']):
                        node.normal.set_dotted('gecos_info.users.%s.email'%(
                            username), user['email'])
                        updated = True
                        
                    if (not node.normal.has_dotted(
                        'gecos_info.users.%s.firstName'%(username)) or 
                        node.normal.get_dotted('gecos_info.users.%s.firstName'%(
                            username)) != user['first_name']):
                        node.normal.set_dotted('gecos_info.users.%s.firstName'%(
                            username), user['first_name'])
                        updated = True

                    if (not node.normal.has_dotted(
                        'gecos_info.users.%s.lastName'%(username)) or
                        node.normal.get_dotted('gecos_info.users.%s.lastName'%(
                            username)) != user['last_name']):
                        node.normal.set_dotted('gecos_info.users.%s.lastName'%(
                            username), user['last_name'])
                        updated = True
                        
                    if updated:
                        logger.info("Updating user %s data in computer: %s "\
                            "Chef ID: %s"%(user['name'], computer['name'],
                                           computer['node_chef_id']))
                        node.save()         
                
            else:
                logger.error("No Chef ID in '%s' computer!"%(computer['name']))
        
    def check_referenced_nodes(self, id_list, possible_types, property_name):
        '''
        Check if the nodes with ID in the id_list exists in the database
        and its types belong to the possible_types list
        '''           
        
        new_id_list = []
        for node_id in id_list:
            ref_nodes = self.db.nodes.find({ "_id" : node_id })    
            found = False
            for ref_nodes in ref_nodes:        
                found = True
                logger.debug('Referenced node %s for property %s is a %s node'%(
                    node_id, property_name, ref_nodes["type"]))
                if not (ref_nodes["type"] in possible_types):
                    logger.error('Bad data type in referenced node %s for '\
                        'property %s (%s not in %s)'%(node_id, property_name,
                            ref_nodes["type"], possible_types))
                
            if not found:
                logger.error('Can\'t find referenced node %s for property %s'%(
                    node_id, property_name))                
                logger.warn('Possible cause: Unknown. Node references '\
                            'non-existent node in MongoDB.')                
                continue
                
            new_id_list.append(node_id)
        
        return new_id_list
        
        
    def check_boolean_property(self, schema, nodedata, property_name):
        if schema is None:
            raise ValueError('Schema is None!')
            
        if nodedata is None:
            raise ValueError('Nodedata is None!')            
            
        if schema['type'] != 'boolean':
            raise ValueError('Schema doesn\'t represent a boolean!')
            
        if nodedata not in ['true', 'false', 'True', 'False', True, False]:
            logger.error('Bad property value: %s (not a boolean) for '\
                         'property %s'%(nodedata, property_name))
            
            
    def check_number_property(self, schema, nodedata, property_name):
        if schema is None:
            raise ValueError('Schema is None!')
            
        if nodedata is None:
            raise ValueError('Nodedata is None!')            
            
        if (schema['type'] != 'number') and (schema['type'] != 'integer'):
            raise ValueError('Schema doesn\'t represent a number!')
            
        if not isinstance( nodedata, ( int, int ) ) and not nodedata.isdigit():
            logger.error('Bad property value: %s (not a number) for property '\
                         '%s'%(nodedata, property_name))
            
            
    def check_string_property(self, schema, nodedata, property_name, is_emitter,
             emitter_policy_slug):
        if schema is None:
            raise ValueError('Schema is None!')
            
        if nodedata is None:
            raise ValueError('Nodedata is None!')            
            
        if schema['type'] != 'string':
            raise ValueError('Schema doesn\'t represent a number!')
            
        if not isinstance(nodedata, (str, str)):
            logger.error('Bad property value: %s (not a string) for property '\
                         '%s'%(nodedata, property_name))
            return
            
        if 'enum' in schema:
            if (len(schema['enum']) > 0) and  not (nodedata in schema['enum']):
                logger.error('Bad property value: %s (not in enumeration %s) '\
                    'for property %s'%(nodedata, schema['enum'], property_name))
                
        if is_emitter:
            # Check if referenced node exists in database
            ref_nodes = self.db.nodes.find({"_id" : ObjectId(nodedata)})    
            found = False
            for ref_nodes in ref_nodes:        
                found = True
                logger.debug('Referenced node %s for property %s is a %s node'%(
                    nodedata, property_name, ref_nodes["type"]))
                if ref_nodes["type"] != self.referenced_data_type[
                    emitter_policy_slug]:
                    logger.error('Bad data type in referenced node %s for '\
                        'property %s (%s != %s)'%(nodedata, property_name,
                            ref_nodes["type"], self.referenced_data_type[
                                emitter_policy_slug]))
                
            if not found:
                logger.error('Can\'t find referenced node %s for '\
                             'property %s'%(nodedata, property_name))
            
    def check_object_property(self, schema, nodedata, propertyname, is_emitter,
            emitter_policy_slug):
        if schema is None:
            raise ValueError('Schema is None!')
            
        if nodedata is None:
            raise ValueError('Nodedata is None!')            
            
        if schema['type'] != 'object':
            raise ValueError('Schema doesn\'t represent a object!')
            
        # Check required properties
        if 'required' in schema:
            for req_property in schema['required']:
                name = str(req_property)
                if propertyname is not None:
                    name = "%s.%s"%(propertyname, req_property)
                logger.debug('\tChecking required property: %s'%(name))
                if str(req_property) in nodedata:
                    logger.debug('\tRequired property: %s exists in the node'\
                                 ' data.'%(name))
                else:
                    logger.error('\tRequired property: %s doesn\'t exists in '\
                                 'the node data!'%(name))

                    
        # Compare the policy schema and the node data
        for prop in list(schema['properties'].keys()):
            prop_type = schema['properties'][str(prop)]['type']
            name = str(prop)
            if propertyname is not None:
                name = "%s.%s"%(propertyname, prop)
            logger.debug('\tChecking property: %s (%s)'%(name, prop_type))
            if not str(prop) in nodedata:
                logger.debug('\tNon required property missing: %s'%(name))
                continue;
            
            if prop_type == 'array':
                self.check_array_property(schema['properties'][str(prop)],
                    nodedata[str(prop)], name, is_emitter, emitter_policy_slug)
                
            elif prop_type == 'string':
                self.check_string_property(schema['properties'][str(prop)],
                    nodedata[str(prop)], name, is_emitter, emitter_policy_slug)
            
            elif prop_type == 'object':
                self.check_object_property(schema['properties'][str(prop)],
                    nodedata[str(prop)], name, is_emitter, emitter_policy_slug)
        
            elif (prop_type == 'number') or (prop_type == 'integer'):
                self.check_number_property(schema['properties'][str(prop)],
                    nodedata[str(prop)], name)

            elif prop_type == 'boolean':
                self.check_boolean_property(schema['properties'][str(prop)],
                    nodedata[str(prop)], name)

            else:
                logger.error('Unknown property type found: %s'%(prop_type))
            
        # Reverse check
        if isinstance(nodedata, dict):
            for prop in list(nodedata.keys()):
                name = str(prop)
                if propertyname is not None:
                    name = "%s.%s"%(propertyname, prop)
                    
                if not str(prop) in list(schema['properties'].keys()):
                    logger.warning('\tProperty in database that doesn\'t exist'\
                                   ' in schema anymore: %s'%(name))
        else:
            logger.error('\tProperty in database that isn\'t an object: %s'%(
                name))
        
        
            

    def check_array_property(self, schema, nodedata, propertyname, is_emitter,
                             emitter_policy_slug):
        if schema is None:
            raise ValueError('Schema is None!')
            
        if nodedata is None:
            raise ValueError('Nodedata is None!')            
            
        if schema['type'] != 'array':
            raise ValueError('Schema doesn\'t represent an array!')

        if not isinstance(nodedata, list):
            logger.error('Bad property value: %s (not an array) for '\
                         'property %s'%(nodedata, propertyname))
            return

        if 'minItems' in schema:
            if len(nodedata) < schema['minItems']:
                logger.error('Bad property value: %s (under min items) for '\
                             'property %s'%(nodedata, propertyname))
                return

        item_type = schema['items']['type']
        count = 0
        for value in nodedata:
            name = '%s[%s]'%(propertyname, count)
            if item_type == 'array':
                self.check_array_property(schema['items'], value, name,
                    is_emitter, emitter_policy_slug)
                
            elif item_type == 'string':
                self.check_string_property(schema['items'], value, name,
                    is_emitter, emitter_policy_slug)
            
            elif item_type == 'object':
                self.check_object_property(schema['items'], value, name,
                    is_emitter, emitter_policy_slug)
        
            elif (item_type == 'number') or (item_type == 'integer'):
                self.check_number_property(schema['items'], value, name)

            elif item_type == 'boolean':
                self.check_boolean_property(schema['items'], value, name)

            else:
                logger.error('Unknown property type found: %s'%(item_type))
                
            count += 1