compute: Add 'server * --all-projects' option
Add an '--all-projects' option to a number of commands: - server delete - server start - server stop This is in addition to 'server list', which already supports this option. This option allows users to request the corresponding action on one or more servers using the server names when that server exists in another project. This is admin-only by default. As part of this work, we also introduce a 'boolenv' helper function that allows us to parse the environment variable as a boolean using 'bool_from_string' helper provided by oslo.utils. This could probably be clever and it has the unfortunate side effect of modifying the help text in environments where this is configured, but it's good enough for now. It also appears to add a new dependency, in the form of oslo.utils, but that dependency was already required by osc-lib and probably more. Change-Id: I4811f8f66dcb14ed99cc1cfb80b00e2d77afe45f Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
This commit is contained in:
parent
bfa032cb18
commit
1a6df700be
@ -31,6 +31,7 @@ from osc_lib.cli import parseractions
|
||||
from osc_lib.command import command
|
||||
from osc_lib import exceptions
|
||||
from osc_lib import utils
|
||||
from oslo_utils import strutils
|
||||
|
||||
from openstackclient.i18n import _
|
||||
from openstackclient.identity import common as identity_common
|
||||
@ -193,6 +194,24 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True):
|
||||
return info
|
||||
|
||||
|
||||
def boolenv(*vars, default=False):
|
||||
"""Search for the first defined of possibly many bool-like env vars.
|
||||
|
||||
Returns the first environment variable defined in vars, or returns the
|
||||
default.
|
||||
|
||||
:param vars: Arbitrary strings to search for. Case sensitive.
|
||||
:param default: The default to return if no value found.
|
||||
:returns: A boolean corresponding to the value found, else the default if
|
||||
no value found.
|
||||
"""
|
||||
for v in vars:
|
||||
value = os.environ.get(v, None)
|
||||
if value:
|
||||
return strutils.bool_from_string(value)
|
||||
return default
|
||||
|
||||
|
||||
class AddFixedIP(command.Command):
|
||||
_description = _("Add fixed IP address to server")
|
||||
|
||||
@ -1322,6 +1341,15 @@ class DeleteServer(command.Command):
|
||||
action='store_true',
|
||||
help=_('Force delete server(s)'),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--all-projects',
|
||||
action='store_true',
|
||||
default=boolenv('ALL_PROJECTS'),
|
||||
help=_(
|
||||
'Delete server(s) in another project by name (admin only)'
|
||||
'(can be specified using the ALL_PROJECTS envvar)'
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--wait',
|
||||
action='store_true',
|
||||
@ -1339,7 +1367,8 @@ class DeleteServer(command.Command):
|
||||
compute_client = self.app.client_manager.compute
|
||||
for server in parsed_args.server:
|
||||
server_obj = utils.find_resource(
|
||||
compute_client.servers, server)
|
||||
compute_client.servers, server,
|
||||
all_tenants=parsed_args.all_projects)
|
||||
|
||||
if parsed_args.force:
|
||||
compute_client.servers.force_delete(server_obj.id)
|
||||
@ -1347,11 +1376,13 @@ class DeleteServer(command.Command):
|
||||
compute_client.servers.delete(server_obj.id)
|
||||
|
||||
if parsed_args.wait:
|
||||
if not utils.wait_for_delete(compute_client.servers,
|
||||
server_obj.id,
|
||||
callback=_show_progress):
|
||||
LOG.error(_('Error deleting server: %s'),
|
||||
server_obj.id)
|
||||
if not utils.wait_for_delete(
|
||||
compute_client.servers,
|
||||
server_obj.id,
|
||||
callback=_show_progress,
|
||||
):
|
||||
msg = _('Error deleting server: %s')
|
||||
LOG.error(msg, server_obj.id)
|
||||
self.app.stdout.write(_('Error deleting server\n'))
|
||||
raise SystemExit
|
||||
|
||||
@ -1446,8 +1477,11 @@ class ListServer(command.Lister):
|
||||
parser.add_argument(
|
||||
'--all-projects',
|
||||
action='store_true',
|
||||
default=bool(int(os.environ.get("ALL_PROJECTS", 0))),
|
||||
help=_('Include all projects (admin only)'),
|
||||
default=boolenv('ALL_PROJECTS'),
|
||||
help=_(
|
||||
'Include all projects (admin only) '
|
||||
'(can be specified using the ALL_PROJECTS envvar)'
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--project',
|
||||
@ -3939,6 +3973,15 @@ class StartServer(command.Command):
|
||||
nargs="+",
|
||||
help=_('Server(s) to start (name or ID)'),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--all-projects',
|
||||
action='store_true',
|
||||
default=boolenv('ALL_PROJECTS'),
|
||||
help=_(
|
||||
'Start server(s) in another project by name (admin only)'
|
||||
'(can be specified using the ALL_PROJECTS envvar)'
|
||||
),
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
@ -3947,6 +3990,7 @@ class StartServer(command.Command):
|
||||
utils.find_resource(
|
||||
compute_client.servers,
|
||||
server,
|
||||
all_tenants=parsed_args.all_projects,
|
||||
).start()
|
||||
|
||||
|
||||
@ -3961,6 +4005,15 @@ class StopServer(command.Command):
|
||||
nargs="+",
|
||||
help=_('Server(s) to stop (name or ID)'),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--all-projects',
|
||||
action='store_true',
|
||||
default=boolenv('ALL_PROJECTS'),
|
||||
help=_(
|
||||
'Stop server(s) in another project by name (admin only)'
|
||||
'(can be specified using the ALL_PROJECTS envvar)'
|
||||
),
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
@ -3969,6 +4022,7 @@ class StopServer(command.Command):
|
||||
utils.find_resource(
|
||||
compute_client.servers,
|
||||
server,
|
||||
all_tenants=parsed_args.all_projects,
|
||||
).stop()
|
||||
|
||||
|
||||
|
@ -2913,6 +2913,28 @@ class TestServerDelete(TestServer):
|
||||
self.servers_mock.delete.assert_has_calls(calls)
|
||||
self.assertIsNone(result)
|
||||
|
||||
@mock.patch.object(common_utils, 'find_resource')
|
||||
def test_server_delete_with_all_projects(self, mock_find_resource):
|
||||
servers = self.setup_servers_mock(count=1)
|
||||
mock_find_resource.side_effect = compute_fakes.FakeServer.get_servers(
|
||||
servers, 0,
|
||||
)
|
||||
|
||||
arglist = [
|
||||
servers[0].id,
|
||||
'--all-projects',
|
||||
]
|
||||
verifylist = [
|
||||
('server', [servers[0].id]),
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
mock_find_resource.assert_called_once_with(
|
||||
mock.ANY, servers[0].id, all_tenants=True,
|
||||
)
|
||||
|
||||
@mock.patch.object(common_utils, 'wait_for_delete', return_value=True)
|
||||
def test_server_delete_wait_ok(self, mock_wait_for_delete):
|
||||
servers = self.setup_servers_mock(count=1)
|
||||
@ -6781,6 +6803,28 @@ class TestServerStart(TestServer):
|
||||
def test_server_start_multi_servers(self):
|
||||
self.run_method_with_servers('start', 3)
|
||||
|
||||
@mock.patch.object(common_utils, 'find_resource')
|
||||
def test_server_start_with_all_projects(self, mock_find_resource):
|
||||
servers = self.setup_servers_mock(count=1)
|
||||
mock_find_resource.side_effect = compute_fakes.FakeServer.get_servers(
|
||||
servers, 0,
|
||||
)
|
||||
|
||||
arglist = [
|
||||
servers[0].id,
|
||||
'--all-projects',
|
||||
]
|
||||
verifylist = [
|
||||
('server', [servers[0].id]),
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
mock_find_resource.assert_called_once_with(
|
||||
mock.ANY, servers[0].id, all_tenants=True,
|
||||
)
|
||||
|
||||
|
||||
class TestServerStop(TestServer):
|
||||
|
||||
@ -6801,6 +6845,28 @@ class TestServerStop(TestServer):
|
||||
def test_server_stop_multi_servers(self):
|
||||
self.run_method_with_servers('stop', 3)
|
||||
|
||||
@mock.patch.object(common_utils, 'find_resource')
|
||||
def test_server_start_with_all_projects(self, mock_find_resource):
|
||||
servers = self.setup_servers_mock(count=1)
|
||||
mock_find_resource.side_effect = compute_fakes.FakeServer.get_servers(
|
||||
servers, 0,
|
||||
)
|
||||
|
||||
arglist = [
|
||||
servers[0].id,
|
||||
'--all-projects',
|
||||
]
|
||||
verifylist = [
|
||||
('server', [servers[0].id]),
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
mock_find_resource.assert_called_once_with(
|
||||
mock.ANY, servers[0].id, all_tenants=True,
|
||||
)
|
||||
|
||||
|
||||
class TestServerSuspend(TestServer):
|
||||
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
The ``server delete``, ``server start`` and ``server stop`` commands now
|
||||
support the ``--all-projects`` option. This allows you to perform the
|
||||
specified action on a server in another project using the server name.
|
||||
This is an admin-only action by default.
|
@ -8,6 +8,7 @@ iso8601>=0.1.11 # MIT
|
||||
openstacksdk>=0.52.0 # Apache-2.0
|
||||
osc-lib>=2.3.0 # Apache-2.0
|
||||
oslo.i18n>=3.15.3 # Apache-2.0
|
||||
oslo.utils>=3.33.0 # Apache-2.0
|
||||
python-keystoneclient>=3.22.0 # Apache-2.0
|
||||
python-novaclient>=17.0.0 # Apache-2.0
|
||||
python-cinderclient>=3.3.0 # Apache-2.0
|
||||
|
Loading…
Reference in New Issue
Block a user