def _value_startswith(attribute_id, search_string, limit=20): """Query attribute starting with search_string :param attribute_id: e.g. primary_ip6 :param search_string: e.g. 2a00 :param limit: e.g. limit result to n results :return: """ query_attribute = Attribute.objects.filter(attribute_id=attribute_id) if not query_attribute: return [] attribute = query_attribute.first() attribute_model = ServerAttribute.get_model(attribute.type) if attribute.type == 'reverse': # Should not be queried by client but lets be sure. return [] if attribute.type == 'relation': query = Server.objects.filter(servertype_id=attribute_id).filter( hostname__startswith=search_string).only('hostname').order_by( 'hostname') return [server.hostname for server in query[:limit]] if attribute.type == 'boolean': return ['true', 'false'] query = attribute_model.objects.filter(attribute_id=attribute_id).filter( value__startswith=search_string).only('value').distinct( 'value').order_by('value') return [attr.value for attr in query[:limit]]
def _real_condition_sql(attribute, template, related_vias): model = ServerAttribute.get_model(attribute.type) assert model is not None # If we come to this point, we must have the item for the entry existing # in the related-vias dictionary. Keep in mind that it also includes # the directly attached servertype attribute combinations. They would # have None as the key of the inner dictionary. If no servertype attribute # combinations had been possible, the caller must have returned an empty # result, before calling this module to get the SQL query. No filter # is optional in queries after all. related_vias = related_vias[attribute.attribute_id] assert related_vias # We start with the condition for the attributes the server has on # its own. Then, add the conditions for all possible relations. They # are going to be OR'ed together. relation_conditions = [] for related_via_attribute, servertype_ids in related_vias.items(): if related_via_attribute is None: # The condition for directly attached attributes relation_condition = 'server.server_id = sub.server_id' elif related_via_attribute.type == 'supernet': relation_condition = _exists_sql(Server, 'rel1', ( "rel1.servertype_id = '{0}'".format( related_via_attribute.target_servertype_id), 'rel1.intern_ip >>= server.intern_ip', 'rel1.server_id = sub.server_id', )) elif related_via_attribute.type == 'reverse': relation_condition = _exists_sql(ServerRelationAttribute, 'rel1', ( "rel1.attribute_id = '{0}'".format( related_via_attribute.reversed_attribute_id), 'rel1.value = server.server_id', 'rel1.server_id = sub.server_id', )) else: assert related_via_attribute.type == 'relation' relation_condition = _exists_sql(ServerRelationAttribute, 'rel1', ( "rel1.attribute_id = '{0}'".format( related_via_attribute.attribute_id), 'rel1.server_id = server.server_id', 'rel1.value = sub.server_id', )) relation_conditions.append((relation_condition, servertype_ids)) if len(relation_conditions) == 1: mixed_relation_condition = relation_conditions[0][0] else: mixed_relation_condition = '({0})'.format(' OR '.join( '({0} AND server.servertype_id IN ({1}))'.format( relation_condition, ', '.join("'{0}'".format(s) for s in servertype_ids)) for relation_condition, servertype_ids in relation_conditions)) return _exists_sql(model, 'sub', ( mixed_relation_condition, "sub.attribute_id = '{0}'".format(attribute.attribute_id), template.format('sub.value'), ))
def _add_related_attribute(self, attribute, servertype_attribute, servers_by_type): related_via_attribute = servertype_attribute.related_via_attribute # First, index the related servers for fast access later servers_by_related = {} for target in servers_by_type[servertype_attribute.servertype_id]: attributes = self._server_attributes[target] if related_via_attribute in attributes: if related_via_attribute.multi: for source in attributes[related_via_attribute]: servers_by_related.setdefault(source, []).append(target) else: source = attributes[related_via_attribute] servers_by_related.setdefault(source, []).append(target) # Then, query and set the related attributes for sa in ServerAttribute.get_model(attribute.type).objects.filter( server__hostname__in=servers_by_related.keys(), attribute=attribute, ).prefetch_related('server').defer( 'server__intern_ip', 'server__servertype', ): for target in servers_by_related[sa.server]: self._add_attribute_value(target, attribute, sa.get_value())
def _upsert_attributes(attribute_lookup, changed, changed_servers): for changes in changed: object_id = changes['object_id'] for attribute_id, change in changes.items(): if attribute_id in Attribute.specials: continue attribute = attribute_lookup[attribute_id] server = changed_servers[object_id] action = change['action'] if action == 'multi': for value in change['add']: server.add_attribute(attribute, value) continue if action not in ('new', 'update'): continue if change['new'] is None: continue try: server_attribute = server.get_attributes(attribute).get() except ServerAttribute.get_model(attribute.type).DoesNotExist: server.add_attribute(attribute, change['new']) else: server_attribute.save_value(change['new'])
def _add_attributes(self, servers_by_type): """Add the attributes to the results""" for key, attributes in self._attributes_by_type.items(): if key == 'supernet': for attribute in attributes: self._add_supernet_attribute( attribute, (s for st in self._servertype_ids_by_attribute[attribute] for s in servers_by_type[st])) elif key == 'domain': for attribute in attributes: self._add_domain_attribute(attribute, [ s for st in self._servertype_ids_by_attribute[attribute] for s in servers_by_type[st] ]) elif key == 'reverse': reversed_attributes = { a.reversed_attribute_id: a for a in attributes } for sa in ServerRelationAttribute.objects.filter( value_id__in=self._server_attributes.keys(), attribute_id__in=reversed_attributes.keys(), ).prefetch_related('server').defer( 'server__intern_ip', 'server__servertype', ): self._add_attribute_value( sa.value, reversed_attributes[sa.attribute_id], sa.server, ) else: attribute_lookup = {a.attribute_id: a for a in attributes} for sa in ServerAttribute.get_model(key).objects.filter( server__in=self._server_attributes.keys(), attribute__in=attributes, ).prefetch_related('server').defer( 'server__intern_ip', 'server__servertype', ): self._add_attribute_value( sa.server, attribute_lookup[sa.attribute_id], sa.get_value(), )