def retag_node_for_hardware_by_modalias(node, modaliases, parent_tag_name, hardware_descriptors): """Adds or removes tags on a node based on its modaliases. Returns the Tag model objects added and removed, respectively. :param node: The node whose tags to modify. :param modaliases: The modaliases discovered on the node. :param parent_tag_name: The tag name for the hardware type given in the `hardware_descriptors` list. For example, if switch ASICs are being discovered, the string "switch" might be appropriate. Then, if switch hardware is found, the node will be tagged with the matching descriptors' tag(s), *and* with the more general "switch" tag. :param hardware_descriptors: A list of hardware descriptor dictionaries. :returns: tuple of (tags_added, tags_removed) """ # Don't unconditionally create the tag. Check for it with a filter first. parent_tag = get_one(Tag.objects.filter(name=parent_tag_name)) tags_added = set() tags_removed = set() discovered_hardware, ruled_out_hardware = determine_hardware_matches( modaliases, hardware_descriptors) if len(discovered_hardware) > 0: if parent_tag is None: # Create the tag "just in time" if we found matching hardware, and # we hadn't created the tag yet. parent_tag = Tag(name=parent_tag_name) parent_tag.save() node.tags.add(parent_tag) tags_added.add(parent_tag) logger.info("%s: Added tag '%s' for detected hardware type." % (node.hostname, parent_tag_name)) for descriptor in discovered_hardware: tag = descriptor["tag"] comment = descriptor["comment"] matches = descriptor["matches"] hw_tag, _ = Tag.objects.get_or_create( name=tag, defaults={"comment": comment}) node.tags.add(hw_tag) tags_added.add(hw_tag) logger.info("%s: Added tag '%s' for detected hardware: %s " "(Matched: %s)." % (node.hostname, tag, comment, matches)) else: if parent_tag is not None: node.tags.remove(parent_tag) tags_removed.add(parent_tag) logger.info( "%s: Removed tag '%s'; machine does not match hardware " "description." % (node.hostname, parent_tag_name)) for descriptor in ruled_out_hardware: tag_name = descriptor["tag"] existing_tag = get_one(node.tags.filter(name=tag_name)) if existing_tag is not None: node.tags.remove(existing_tag) tags_removed.add(existing_tag) logger.info("%s: Removed tag '%s'; hardware is missing." % (node.hostname, tag_name)) return tags_added, tags_removed
def test__clears_node_set_before_populating(self): tag = Tag(name=factory.make_name("tag"), definition="//foo") tag.save(populate=False) nodes = [factory.make_Node() for _ in range(3)] tag.node_set.add(*nodes) tag._populate_nodes_now() self.assertItemsEqual([], tag.node_set.all())
def test__does_nothing_if_tag_is_not_defined(self): post_commit_do = self.patch(tag_module, "post_commit_do") tag = Tag(name=factory.make_name("tag"), definition="") tag.save(populate=False) self.assertFalse(tag.is_defined) self.assertThat(post_commit_do, MockNotCalled()) tag._populate_nodes_later() self.assertThat(post_commit_do, MockNotCalled())
def test__does_nothing_if_tag_is_not_defined(self): populate_multiple = self.patch_autospec( populate_tags, "populate_tag_for_multiple_nodes") tag = Tag(name=factory.make_name("tag"), definition="") tag.save(populate=False) self.assertFalse(tag.is_defined) self.assertThat(populate_multiple, MockNotCalled()) tag._populate_nodes_now() self.assertThat(populate_multiple, MockNotCalled())
def test__populates_if_tag_is_defined(self): populate_multiple = self.patch_autospec( populate_tags, "populate_tag_for_multiple_nodes") tag = Tag(name=factory.make_name("tag"), definition="//foo") tag.save(populate=False) self.assertTrue(tag.is_defined) self.assertThat(populate_multiple, MockNotCalled()) tag._populate_nodes_now() self.assertThat(populate_multiple, MockCalledOnceWith(tag, ANY))
def test__populates_if_tag_is_defined(self): post_commit_do = self.patch(tag_module, "post_commit_do") tag = Tag(name=factory.make_name("tag"), definition="//foo") tag.save(populate=False) self.assertTrue(tag.is_defined) self.assertThat(post_commit_do, MockNotCalled()) tag._populate_nodes_later() self.assertThat( post_commit_do, MockCalledOnceWith(reactor.callLater, 0, deferToDatabase, populate_tags.populate_tags, tag))
def test__does_not_clear_node_set_before_populating(self): post_commit_do = self.patch(tag_module, "post_commit_do") tag = Tag(name=factory.make_name("tag"), definition="//foo") tag.save(populate=False) nodes = [factory.make_Node() for _ in range(3)] tag.node_set.add(*nodes) tag._populate_nodes_later() self.assertItemsEqual(nodes, tag.node_set.all()) self.assertThat( post_commit_do, MockCalledOnceWith(reactor.callLater, 0, deferToDatabase, populate_tags.populate_tags, tag))
def test_applies_tags_to_nodes_on_save(self): populate_nodes = self.patch_autospec(Tag, "populate_nodes") tag = Tag(name=factory.make_name("tag"), definition="//node/child") self.assertThat(populate_nodes, MockNotCalled()) tag.save() self.assertThat(populate_nodes, MockCalledOnceWith(tag))
def test__later_is_the_default(self): tag = Tag(name=factory.make_name("tag")) self.patch(tag, "_populate_nodes_later") self.assertThat(tag._populate_nodes_later, MockNotCalled()) tag.save() self.assertThat(tag._populate_nodes_later, MockCalledOnceWith())