diff --git a/releasenotes/notes/list-servers-all-projects-349e6dc665ba2e8d.yaml b/releasenotes/notes/list-servers-all-projects-349e6dc665ba2e8d.yaml new file mode 100644 index 000000000..c993d2d81 --- /dev/null +++ b/releasenotes/notes/list-servers-all-projects-349e6dc665ba2e8d.yaml @@ -0,0 +1,6 @@ +--- +features: + - Add 'all_projects' parameter to list_servers and + search_servers which will tell Nova to return servers for all projects + rather than just for the current project. This is only available to + cloud admins. diff --git a/shade/openstackcloud.py b/shade/openstackcloud.py index 168a912f3..19714c476 100644 --- a/shade/openstackcloud.py +++ b/shade/openstackcloud.py @@ -1574,8 +1574,11 @@ class OpenStackCloud(_normalize.Normalizer): ) return _utils._filter_list(groups, name_or_id, filters) - def search_servers(self, name_or_id=None, filters=None, detailed=False): - servers = self.list_servers(detailed=detailed) + def search_servers( + self, name_or_id=None, filters=None, detailed=False, + all_projects=False): + servers = self.list_servers( + detailed=detailed, all_projects=all_projects) return _utils._filter_list(servers, name_or_id, filters) def search_server_groups(self, name_or_id=None, filters=None): @@ -1829,7 +1832,7 @@ class OpenStackCloud(_normalize.Normalizer): _tasks.NovaSecurityGroupList(search_opts=filters)) return self._normalize_secgroups(groups) - def list_servers(self, detailed=False): + def list_servers(self, detailed=False, all_projects=False): """List all available servers. :returns: A list of server ``munch.Munch``. @@ -1847,19 +1850,24 @@ class OpenStackCloud(_normalize.Normalizer): if self._servers_lock.acquire(first_run): try: if not (first_run and self._servers is not None): - self._servers = self._list_servers(detailed=detailed) + self._servers = self._list_servers( + detailed=detailed, + all_projects=all_projects) self._servers_time = time.time() finally: self._servers_lock.release() return self._servers - def _list_servers(self, detailed=False): + def _list_servers(self, detailed=False, all_projects=False): with _utils.shade_exceptions( "Error fetching server list on {cloud}:{region}:".format( cloud=self.name, region=self.region_name)): + kwargs = {} + if all_projects: + kwargs['search_opts'] = {'all_tenants': True} servers = self._normalize_servers( - self.manager.submit_task(_tasks.ServerList())) + self.manager.submit_task(_tasks.ServerList(**kwargs))) if detailed: return [ diff --git a/shade/tests/functional/test_compute.py b/shade/tests/functional/test_compute.py index a06c23376..399b37eb4 100644 --- a/shade/tests/functional/test_compute.py +++ b/shade/tests/functional/test_compute.py @@ -68,6 +68,28 @@ class TestCompute(base.BaseFunctionalTestCase): self.demo_cloud.delete_server(self.server_name, wait=True)) self.assertIsNone(self.demo_cloud.get_server(self.server_name)) + def test_list_all_servers(self): + self.addCleanup(self._cleanup_servers_and_volumes, self.server_name) + server = self.demo_cloud.create_server( + name=self.server_name, + image=self.image, + flavor=self.flavor, + wait=True) + # We're going to get servers from other tests, but that's ok, as long + # as we get the server we created with the demo user. + found_server = False + for s in self.operator_cloud.list_servers(all_projects=True): + if s.name == server.name: + found_server = True + self.assertTrue(found_server) + + def test_list_all_servers_bad_permissions(self): + # Normal users are not allowed to pass all_projects=True + self.assertRaises( + exc.OpenStackCloudException, + self.demo_cloud.list_servers, + all_projects=True) + def test_create_server_image_flavor_dict(self): self.addCleanup(self._cleanup_servers_and_volumes, self.server_name) server = self.demo_cloud.create_server( diff --git a/shade/tests/unit/test_shade.py b/shade/tests/unit/test_shade.py index 69992731c..568a52907 100644 --- a/shade/tests/unit/test_shade.py +++ b/shade/tests/unit/test_shade.py @@ -585,6 +585,20 @@ class TestShade(base.TestCase): self.assertEqual('server1', r[0]['name']) self.assertEqual('server2', r[1]['name']) + @mock.patch.object(shade.OpenStackCloud, 'nova_client') + def test_list_servers_all_projects(self, mock_nova_client): + '''This test verifies that when list_servers is called with + `all_projects=True` that it passes `all_tenants=1` to novaclient.''' + mock_nova_client.servers.list.return_value = [ + fakes.FakeServer('server1', '', 'ACTIVE'), + fakes.FakeServer('server2', '', 'ACTIVE'), + ] + + self.cloud.list_servers(all_projects=True) + + mock_nova_client.servers.list.assert_called_with( + search_opts={'all_tenants': True}) + def test_iterate_timeout_bad_wait(self): with testtools.ExpectedException( exc.OpenStackCloudException,