def test_POST_update_nodes_refuses_unidentified_rack_controller(self): tag = factory.make_Tag() rack_controller = factory.make_RackController() node = factory.make_Node() client = make_worker_client(rack_controller) # We don't pass rack controller system_id so we get refused. response = client.post( self.get_tag_uri(tag), { "op": "update_nodes", "add": [node.system_id] }, ) self.assertEqual(http.client.FORBIDDEN, response.status_code) self.assertItemsEqual([], tag.node_set.all())
def test_PUT_updates_tag(self): self.become_admin() tag = factory.make_Tag() # Note that 'definition' is not being sent response = self.client.put( self.get_tag_uri(tag), {"name": "new-tag-name", "comment": "A random comment"}, ) self.assertEqual(http.client.OK, response.status_code) parsed_result = json.loads(response.content.decode("ascii")) self.assertEqual("new-tag-name", parsed_result["name"]) self.assertEqual("A random comment", parsed_result["comment"]) self.assertEqual(tag.definition, parsed_result["definition"]) self.assertFalse(Tag.objects.filter(name=tag.name).exists()) self.assertTrue(Tag.objects.filter(name="new-tag-name").exists())
def test_PUT_invalid_definition(self): self.become_admin() node = factory.make_Node() tag = factory.make_Tag(definition="//child") node.tags.add(tag) self.assertItemsEqual([tag.name], node.tag_names()) response = self.client.put( self.get_tag_uri(tag), {"name": "bad tag", "definition": "invalid::tag"}, ) self.assertEqual(http.client.BAD_REQUEST, response.status_code) # The tag should not be modified tag = reload_object(tag) self.assertItemsEqual([tag.name], node.tag_names()) self.assertEqual("//child", tag.definition)
def test_PUT_updates_tag(self): self.become_admin() tag = factory.make_Tag() # Note that 'definition' is not being sent response = self.client.put(self.get_tag_uri(tag), { 'name': 'new-tag-name', 'comment': 'A random comment' }) self.assertEqual(http.client.OK, response.status_code) parsed_result = json.loads(response.content.decode('ascii')) self.assertEqual('new-tag-name', parsed_result['name']) self.assertEqual('A random comment', parsed_result['comment']) self.assertEqual(tag.definition, parsed_result['definition']) self.assertFalse(Tag.objects.filter(name=tag.name).exists()) self.assertTrue(Tag.objects.filter(name='new-tag-name').exists())
def test_POST_update_nodes_refuses_non_rack_controller(self): tag = factory.make_Tag() rack_controller = factory.make_RackController() node = factory.make_Node() token = create_auth_token(rack_controller.owner) token.save() creds = convert_tuple_to_string(get_creds_tuple(token)) response = self.client.post( self.get_tag_uri(tag), { 'op': 'update_nodes', 'add': [node.system_id], 'rack_controller': rack_controller.system_id, 'credentials': creds, }) self.assertEqual(http.client.FORBIDDEN, response.status_code) self.assertItemsEqual([], tag.node_set.all())
def test_GET_machines_query_count(self): # Patch middleware so it does not affect query counting. self.patch( middleware.ExternalComponentsMiddleware, "_check_rack_controller_connectivity", ) tag = factory.make_Tag() for _ in range(3): machine = factory.make_Node_with_Interface_on_Subnet() machine.tags.add(tag) # XXX ltrager 2019-08-16 - Work around for LP:1840491 Node.objects.update(boot_disk=None) num_queries1, response1 = count_queries(self.client.get, self.get_tag_uri(tag), {"op": "machines"}) for _ in range(3): machine = factory.make_Node_with_Interface_on_Subnet() machine.tags.add(tag) # XXX ltrager 2019-08-16 - Work around for LP:1840491 Node.objects.update(boot_disk=None) num_queries2, response2 = count_queries(self.client.get, self.get_tag_uri(tag), {"op": "machines"}) # Make sure the responses are ok as it's not useful to compare the # number of queries if they are not. parsed_result_1 = json.loads( response1.content.decode(settings.DEFAULT_CHARSET)) parsed_result_2 = json.loads( response2.content.decode(settings.DEFAULT_CHARSET)) self.assertEqual( [http.client.OK, http.client.OK, 3, 6], [ response1.status_code, response2.status_code, len(extract_system_ids(parsed_result_1)), len(extract_system_ids(parsed_result_2)), ], ) # Because of fields `status_action`, `status_message`, # `default_gateways`, `health_status` and 'resource_pool', the number # of queries is not the same but it is proportional to the number of # machines. self.assertEquals(num_queries1, num_queries2 - (3 * 7))
def test_POST_update_nodes_ignores_unknown_nodes(self): tag = factory.make_Tag() self.become_admin() unknown_add_system_id = generate_node_system_id() unknown_remove_system_id = generate_node_system_id() self.assertItemsEqual([], tag.node_set.all()) response = self.client.post( self.get_tag_uri(tag), { 'op': 'update_nodes', 'add': [unknown_add_system_id], 'remove': [unknown_remove_system_id], }) self.assertEqual(http.client.OK, response.status_code) parsed_result = json.loads( response.content.decode(settings.DEFAULT_CHARSET)) self.assertItemsEqual([], tag.node_set.all()) self.assertEqual({'added': 0, 'removed': 0}, parsed_result)
def test_GET_region_controllers_query_count(self): # Patch middleware so it does not affect query counting. self.patch( middleware.ExternalComponentsMiddleware, "_check_rack_controller_connectivity", ) self.become_admin() tag = factory.make_Tag() for _ in range(3): region = factory.make_RegionController() region.tags.add(tag) num_queries1, response1 = count_queries( self.client.get, self.get_tag_uri(tag), {"op": "region_controllers"}, ) for _ in range(3): region = factory.make_RegionController() region.tags.add(tag) num_queries2, response2 = count_queries( self.client.get, self.get_tag_uri(tag), {"op": "region_controllers"}, ) # Make sure the responses are ok as it's not useful to compare the # number of queries if they are not. parsed_result_1 = json.loads( response1.content.decode(settings.DEFAULT_CHARSET) ) parsed_result_2 = json.loads( response2.content.decode(settings.DEFAULT_CHARSET) ) self.assertEqual( [http.client.OK, http.client.OK, 3, 6], [ response1.status_code, response2.status_code, len(extract_system_ids(parsed_result_1)), len(extract_system_ids(parsed_result_2)), ], ) self.assertEqual(num_queries1, num_queries2 - 6)
def test_POST_update_nodes_changes_associations(self): tag = factory.make_Tag() self.become_admin() node_first = factory.make_Node() node_second = factory.make_Node() node_first.tags.add(tag) self.assertItemsEqual([node_first], tag.node_set.all()) response = self.client.post( self.get_tag_uri(tag), { 'op': 'update_nodes', 'add': [node_second.system_id], 'remove': [node_first.system_id], }) self.assertEqual(http.client.OK, response.status_code) parsed_result = json.loads( response.content.decode(settings.DEFAULT_CHARSET)) self.assertItemsEqual([node_second], tag.node_set.all()) self.assertEqual({'added': 1, 'removed': 1}, parsed_result)
def test_POST_update_nodes_ignores_incorrect_definition(self): tag = factory.make_Tag() orig_def = tag.definition rack_controller = factory.make_RackController() node = factory.make_Node() client = make_worker_client(rack_controller) tag.definition = '//new/node/definition' tag.save(populate=False) response = client.post( self.get_tag_uri(tag), { 'op': 'update_nodes', 'add': [node.system_id], 'rack_controller': rack_controller.system_id, 'definition': orig_def, }) self.assertEqual(http.client.CONFLICT, response.status_code) self.assertItemsEqual([], tag.node_set.all()) self.assertItemsEqual([], node.tags.all())
def test_GET_nodes_query_count(self): # Patch middleware so it does not affect query counting. self.patch( middleware.ExternalComponentsMiddleware, '_check_rack_controller_connectivity') tag = factory.make_Tag() for _ in range(3): machine = factory.make_Node_with_Interface_on_Subnet() machine.tags.add(tag) for _ in range(3): device = factory.make_Device() device.tags.add(tag) num_queries1, response1 = count_queries( self.client.get, self.get_tag_uri(tag), {'op': 'nodes'}) for _ in range(3): machine = factory.make_Node_with_Interface_on_Subnet() machine.tags.add(tag) for _ in range(3): device = factory.make_Device() device.tags.add(tag) num_queries2, response2 = count_queries( self.client.get, self.get_tag_uri(tag), {'op': 'nodes'}) # Make sure the responses are ok as it's not useful to compare the # number of queries if they are not. parsed_result_1 = json.loads( response1.content.decode(settings.DEFAULT_CHARSET)) parsed_result_2 = json.loads( response2.content.decode(settings.DEFAULT_CHARSET)) self.assertEqual( [http.client.OK, http.client.OK, 3, 6], [ response1.status_code, response2.status_code, len(extract_system_ids(parsed_result_1)), len(extract_system_ids(parsed_result_2)), ]) # Because of fields `status_action`, `status_message`, # `default_gateways`, and `health_status` the number of queries is not # the same but it is proportional to the number of machines. self.assertEquals(num_queries1, num_queries2 - (3 * 4))
def test_GET_devices_returns_devices(self): tag = factory.make_Tag() machine = factory.make_Node() device = factory.make_Device() rack = factory.make_RackController() region = factory.make_RegionController() # Create a second node that isn't tagged. factory.make_Node() machine.tags.add(tag) device.tags.add(tag) rack.tags.add(tag) region.tags.add(tag) response = self.client.get(self.get_tag_uri(tag), {"op": "devices"}) self.assertEqual(http.client.OK, response.status_code) parsed_result = json.loads( response.content.decode(settings.DEFAULT_CHARSET)) self.assertItemsEqual([device.system_id], [r["system_id"] for r in parsed_result])
def test_yields_configuration_with_ubuntu(self): tag = factory.make_Tag(name='wedge100') node = factory.make_Node(osystem='ubuntu', netboot=False) node.tags.add(tag) configuration = generate_rack_controller_configuration(node) secret = '1234' Config.objects.set_config("rpc_shared_secret", secret) channel = version.get_maas_version_track_channel() maas_url = "http://%s:5240/MAAS" % get_maas_facing_server_host( node.get_boot_rack_controller()) cmd = "/bin/snap/maas init --mode rack" self.assertThat(dict(configuration), KeysEqual({ "runcmd": [ "snap install maas --devmode --channel=%s" % channel, "%s --maas-url %s --secret %s" % (cmd, maas_url, secret), ] }))
def test_GET_region_controllers_returns_no_controllers_nonadmin(self): tag = factory.make_Tag() machine = factory.make_Node() device = factory.make_Device() rack = factory.make_RackController() region = factory.make_RegionController() # Create a second node that isn't tagged. factory.make_Node() machine.tags.add(tag) device.tags.add(tag) rack.tags.add(tag) region.tags.add(tag) response = self.client.get( self.get_tag_uri(tag), {'op': 'region_controllers'}) self.assertEqual(http.client.OK, response.status_code) parsed_result = json.loads( response.content.decode(settings.DEFAULT_CHARSET)) self.assertItemsEqual([], parsed_result)
def test_POST_update_nodes_refuses_no_token(self): tag = factory.make_Tag() rack_controller = factory.make_RackController() node = factory.make_Node() # create a token for a different user token = create_auth_token(factory.make_User()) token.save() creds = convert_tuple_to_string(get_creds_tuple(token)) response = self.client.post( self.get_tag_uri(tag), { "op": "update_nodes", "add": [node.system_id], "rack_controller": rack_controller.system_id, "credentials": creds, }, ) self.assertEqual(http.client.FORBIDDEN, response.status_code) self.assertItemsEqual([], tag.node_set.all())
def test_calls_are_made_to_all_clusters(self): rpc_fixture = self.prepare_live_rpc() rack_controllers = [factory.make_RackController() for _ in range(3)] protocols = [] rack_creds = [] for rack in rack_controllers: tokens = list(get_auth_tokens(rack.owner)) if len(tokens) > 0: # Use the latest token. token = tokens[-1] else: token = create_auth_token(rack.owner) creds = convert_tuple_to_string(get_creds_tuple(token)) rack_creds.append(creds) protocol = rpc_fixture.makeCluster(rack, EvaluateTag) protocol.EvaluateTag.side_effect = always_succeed_with({}) protocols.append(protocol) tag = factory.make_Tag(populate=False) [d] = populate_tags(tag) # `d` is a testing-only convenience. We must wait for it to fire, and # we must do that from the reactor thread. wait_for_populate = asynchronous(lambda: d) wait_for_populate().wait(10) for rack, protocol, creds in zip(rack_controllers, protocols, rack_creds): self.expectThat( protocol.EvaluateTag, MockCalledOnceWith( protocol, tag_name=tag.name, tag_definition=tag.definition, system_id=rack.system_id, tag_nsmap=ANY, credentials=creds, nodes=ANY, ), )
def test_POST_update_nodes_doesnt_require_add_or_remove(self): tag = factory.make_Tag() node = factory.make_Node() self.become_admin() self.assertItemsEqual([], tag.node_set.all()) response = self.client.post(self.get_tag_uri(tag), { 'op': 'update_nodes', 'add': [node.system_id], }) self.assertEqual(http.client.OK, response.status_code) parsed_result = json.loads( response.content.decode(settings.DEFAULT_CHARSET)) self.assertEqual({'added': 1, 'removed': 0}, parsed_result) response = self.client.post(self.get_tag_uri(tag), { 'op': 'update_nodes', 'remove': [node.system_id], }) self.assertEqual(http.client.OK, response.status_code) parsed_result = json.loads( response.content.decode(settings.DEFAULT_CHARSET)) self.assertEqual({'added': 0, 'removed': 1}, parsed_result)
def test_GET_nodes_hides_invisible_nodes(self): user2 = factory.make_User() node1 = factory.make_Node() node2 = factory.make_Node(status=NODE_STATUS.ALLOCATED, owner=user2) tag = factory.make_Tag() node1.tags.add(tag) node2.tags.add(tag) response = self.client.get(self.get_tag_uri(tag), {'op': 'nodes'}) self.assertEqual(http.client.OK, response.status_code) parsed_result = json.loads( response.content.decode(settings.DEFAULT_CHARSET)) self.assertEqual([node1.system_id], [r['system_id'] for r in parsed_result]) # The other user can also see his node client2 = MAASSensibleOAuthClient(user2) response = client2.get(self.get_tag_uri(tag), {'op': 'nodes'}) self.assertEqual(http.client.OK, response.status_code) parsed_result = json.loads( response.content.decode(settings.DEFAULT_CHARSET)) self.assertItemsEqual([node1.system_id, node2.system_id], [r['system_id'] for r in parsed_result])
def test__returns_kparams_for_known_node(self): rack_controller = factory.make_RackController() local_ip = factory.make_ip_address() remote_ip = factory.make_ip_address() """ The make_node function will result in a boot resource being created with an architecture that looks like "arch-YYY/hwe-Z", with YYY being a random string and Z being the first letter of the default commissioning image. If we don't create a node with this same kernel name, the node will use an architecture name of arch-YYY/generic which means the get_config won't find the matching boot resource file with the kparams attribute. """ default_series = Config.objects.get_config( name='commissioning_distro_series') release = get_release_from_distro_info(default_series) hwe_kernel = "hwe-%s" % (release['version'].split()[0]) node = self.make_node_with_extra(status=NODE_STATUS.DEPLOYING, extra={'kparams': 'a=b'}, hwe_kernel=hwe_kernel) """ Create a tag so that we can make sure the kparams attribute got merged with the tag's kernel_opts attribute. """ tag = factory.make_Tag(kernel_opts="b=c") node.tags.add(tag) mac = node.get_boot_interface().mac_address config = get_config(rack_controller.system_id, local_ip, remote_ip, mac=mac) extra = config.get('extra_opts', None) self.assertIn('b=c', extra) self.assertIn('a=b', extra)
def create_tag(self, params=None): if params is None: params = {} return factory.make_Tag(**params)
def test_PUT_refuses_non_superuser(self): tag = factory.make_Tag() response = self.client.put( self.get_tag_uri(tag), {'comment': 'A special comment'}) self.assertEqual(http.client.FORBIDDEN, response.status_code)
def test_POST_rebuild_requires_admin(self): tag = factory.make_Tag(definition='/foo/bar') response = self.client.post( self.get_tag_uri(tag), {'op': 'rebuild'}) self.assertEqual(http.client.FORBIDDEN, response.status_code)
def test_list(self): user = factory.make_User() handler = TagHandler(user, {}) factory.make_Tag() expected_tags = [self.dehydrate_tag(tag) for tag in Tag.objects.all()] self.assertItemsEqual(expected_tags, handler.list({}))
def test_DELETE_requires_admin(self): tag = factory.make_Tag() response = self.client.delete(self.get_tag_uri(tag)) self.assertEqual(http.client.FORBIDDEN, response.status_code) self.assertItemsEqual([tag], Tag.objects.filter(id=tag.id))
def test_valid_tag_names(self): for valid in ["valid-dash", "under_score", "long" * 50]: tag = factory.make_Tag(name=valid) self.assertEqual(valid, tag.name)
def test_get(self): user = factory.make_User() handler = TagHandler(user, {}) tag = factory.make_Tag() self.assertEqual(self.dehydrate_tag(tag), handler.get({"id": tag.id}))
def test_yields_nothing_when_node_is_not_ubuntu(self): tag = factory.make_Tag(name='switch') node = factory.make_Node(osystem='centos', netboot=False) node.tags.add(tag) configuration = generate_rack_controller_configuration(node) self.assertThat(dict(configuration), Equals({}))
def test_populate_tags_fails_called_in_transaction(self): with transaction.atomic(): tag = factory.make_Tag(populate=False) self.assertRaises(transaction.TransactionManagementError, populate_tags, tag)
def test_DELETE_removes_tag(self): self.become_admin() tag = factory.make_Tag() response = self.client.delete(self.get_tag_uri(tag)) self.assertEqual(http.client.NO_CONTENT, response.status_code) self.assertFalse(Tag.objects.filter(id=tag.id).exists())
def test_will_not_save_invalid_xpath(self): tag = factory.make_Tag(definition="//node/foo") tag.definition = "invalid::tag" self.assertRaises(ValidationError, tag.save)