docs: def validate_resource_attributes(resource, attributes, template, check_unit=True, exact_match=False,**kwargs):
      """
          Validate that the resource provided matches the template.
          Only passes if the resource contains ONLY the attributes specified
          in the template.
  
          The template should take the form of a dictionary, as should the
          resources.
  
          *check_unit*: Makes sure that if a unit is specified in the template, it
                        is the same in the data
          *exact_match*: Ensures that all the attributes in the template are in
                         the data also. By default this is false, meaning a subset
                         of the template attributes may be specified in the data.
                         An attribute specified in the data *must* be defined in
                         the template.
  
          @returns a list of error messages. An empty list indicates no
          errors were found.
      """
      errors = []
      #is it a node or link?
      res_type = 'GROUP'
      if resource.get('x') is not None:
          res_type = 'NODE'
      elif resource.get('node_1_id') is not None:
          res_type = 'LINK'
      elif resource.get('nodes') is not None:
          res_type = 'NETWORK'
  
      #Find all the node/link/network definitions in the template
      tmpl_res = template['resources'][res_type]
  
      #the user specified type of the resource
      res_user_type = resource.get('type')
  
      #Check the user specified type is in the template
      if res_user_type is None:
          errors.append("No type specified on resource %s"%(resource['name']))
  
      elif tmpl_res.get(res_user_type) is None:
          errors.append("Resource %s is defined as having type %s but "
                        "this type is not specified in the template."%
                        (resource['name'], res_user_type))
  
      #It is in the template. Now check all the attributes are correct.
      tmpl_attrs = tmpl_res.get(res_user_type)['attributes']
  
      attrs = {}
      for a in attributes.values():
          attrs[a['id']] = a
  
      for a in tmpl_attrs.values():
          if a.get('id') is not None:
              attrs[a['id']] = {'name':a['name'], 'unit':a.get('unit'), 'dimen':a.get('dimension')}
  
      if exact_match is True:
          #Check that all the attributes in the template are in the data.
          #get all the attribute names from the template
          tmpl_attr_names = set(tmpl_attrs.keys())
          #get all the attribute names from the data for this resource
          resource_attr_names = []
          for ra in resource['attributes']:
              attr_name = attrs[ra['attr_id']]['name']
              resource_attr_names.append(attr_name)
          resource_attr_names = set(resource_attr_names)
  
          #Compare the two lists to ensure they are the same (using sets is easier)
          in_tmpl_not_in_resource = tmpl_attr_names - resource_attr_names
          in_resource_not_in_tmpl = resource_attr_names - tmpl_attr_names
  
          if len(in_tmpl_not_in_resource) > 0:
              errors.append("Template has defined attributes %s for type %s but they are not"
                              " specified in the Data."%(','.join(in_tmpl_not_in_resource),
                                                      res_user_type ))
  
          if len(in_resource_not_in_tmpl) > 0:
              errors.append("Resource %s (type %s) has defined attributes %s but this is not"
                              " specified in the Template."%(resource['name'],
                                                          res_user_type,
                                                          ','.join(in_resource_not_in_tmpl)))
  
      #Check that each of the attributes specified on the resource are valid.
      for res_attr in resource['attributes']:
  
          attr = attrs.get(res_attr['attr_id'])
  
          if attr is None:
              errors.append("An attribute mismatch has occurred. Attr %s is not "
                            "defined in the data but is present on resource %s"
                            %(res_attr['attr_id'], resource['name']))
              continue
  
          #If an attribute is not specified in the template, then throw an error
          if tmpl_attrs.get(attr['name']) is None:
              errors.append("Resource %s has defined attribute %s but this is not"
                            " specified in the Template."%(resource['name'], attr['name']))
          else:
              #If the dimensions or units don't match, throw an error
  
              tmpl_attr = tmpl_attrs[attr['name']]
  
              if tmpl_attr.get('data_type') is not None:
                  if res_attr.get('type') is not None:
                      if tmpl_attr.get('data_type') != res_attr.get('type'):
                          errors.append("Error in data. Template says that %s on %s is a %s, but data suggests it is a %s"%
                              (attr['name'], resource['name'], tmpl_attr.get('data_type'), res_attr.get('type')))
  
              attr_dimension = 'dimensionless' if attr.get('dimension') is None else attr.get('dimension')
              tmpl_attr_dimension = 'dimensionless' if tmpl_attr.get('dimension') is None else tmpl_attr.get('dimension')
  
              if attr_dimension.lower() != tmpl_attr_dimension.lower():
                  errors.append("Dimension mismatch on resource %s for attribute %s"
                                " (template says %s on type %s, data says %s)"%
                                (resource['name'], attr.get('name'),
                                 tmpl_attr.get('dimension'), res_user_type, attr_dimension))
  
              if check_unit is True:
                  if tmpl_attr.get('unit') is not None:
                      if attr.get('unit') != tmpl_attr.get('unit'):
                          errors.append("Unit mismatch for resource %s with unit %s "
                                        "(template says %s for type %s)"
                                        %(resource['name'], attr.get('unit'),
                                          tmpl_attr.get('unit'), res_user_type))
  
      return errors