summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--openstackclient/compute/v2/server.py70
-rw-r--r--openstackclient/compute/v2/server_group.py38
-rw-r--r--openstackclient/network/v2/network_rbac.py22
-rw-r--r--openstackclient/tests/unit/compute/v2/test_server.py66
-rw-r--r--openstackclient/tests/unit/compute/v2/test_server_group.py47
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_rbac.py6
-rw-r--r--releasenotes/notes/add-missing-server-group-list-opts-d3c3d98b7f7a56a6.yaml5
-rw-r--r--releasenotes/notes/rbac-add-address-group-f9bb83238b5a7c1f.yaml4
-rw-r--r--releasenotes/notes/server-ops-all-projects-2ce2202cdf617184.yaml7
-rw-r--r--requirements.txt1
10 files changed, 244 insertions, 22 deletions
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index 02fc5816..15a5084d 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -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")
@@ -1323,6 +1342,15 @@ class DeleteServer(command.Command):
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',
help=_('Wait for delete to complete'),
@@ -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()
diff --git a/openstackclient/compute/v2/server_group.py b/openstackclient/compute/v2/server_group.py
index 783fdbfe..32dd1937 100644
--- a/openstackclient/compute/v2/server_group.py
+++ b/openstackclient/compute/v2/server_group.py
@@ -177,11 +177,47 @@ class ListServerGroup(command.Lister):
default=False,
help=_("List additional fields in output")
)
+ # TODO(stephenfin): This should really be a --marker option, but alas
+ # the API doesn't support that for some reason
+ parser.add_argument(
+ '--offset',
+ metavar='<offset>',
+ type=int,
+ default=None,
+ help=_(
+ 'Index from which to start listing servers. This should '
+ 'typically be a factor of --limit. Display all servers groups '
+ 'if not specified.'
+ ),
+ )
+ parser.add_argument(
+ '--limit',
+ metavar='<limit>',
+ type=int,
+ default=None,
+ help=_(
+ "Maximum number of server groups to display. "
+ "If limit is greater than 'osapi_max_limit' option of Nova "
+ "API, 'osapi_max_limit' will be used instead."
+ ),
+ )
return parser
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
- data = compute_client.server_groups.list(parsed_args.all_projects)
+
+ kwargs = {}
+
+ if parsed_args.all_projects:
+ kwargs['all_projects'] = parsed_args.all_projects
+
+ if parsed_args.offset:
+ kwargs['offset'] = parsed_args.offset
+
+ if parsed_args.limit:
+ kwargs['limit'] = parsed_args.limit
+
+ data = compute_client.server_groups.list(**kwargs)
policy_key = 'Policies'
if compute_client.api_version >= api_versions.APIVersion("2.64"):
diff --git a/openstackclient/network/v2/network_rbac.py b/openstackclient/network/v2/network_rbac.py
index b88ef019..4984e89d 100644
--- a/openstackclient/network/v2/network_rbac.py
+++ b/openstackclient/network/v2/network_rbac.py
@@ -60,6 +60,10 @@ def _get_attrs(client_manager, parsed_args):
object_id = network_client.find_subnet_pool(
parsed_args.rbac_object,
ignore_missing=False).id
+ if parsed_args.type == 'address_group':
+ object_id = network_client.find_address_group(
+ parsed_args.rbac_object,
+ ignore_missing=False).id
attrs['object_id'] = object_id
@@ -100,11 +104,12 @@ class CreateNetworkRBAC(command.ShowOne):
'--type',
metavar="<type>",
required=True,
- choices=['address_scope', 'security_group', 'subnetpool',
- 'qos_policy', 'network'],
+ choices=['address_group', 'address_scope', 'security_group',
+ 'subnetpool', 'qos_policy', 'network'],
help=_('Type of the object that RBAC policy '
- 'affects ("address_scope", "security_group", "subnetpool",'
- ' "qos_policy" or "network")')
+ 'affects ("address_group", "address_scope", '
+ '"security_group", "subnetpool", "qos_policy" or '
+ '"network")')
)
parser.add_argument(
'--action',
@@ -193,11 +198,12 @@ class ListNetworkRBAC(command.Lister):
parser.add_argument(
'--type',
metavar='<type>',
- choices=['address_scope', 'security_group', 'subnetpool',
- 'qos_policy', 'network'],
+ choices=['address_group', 'address_scope', 'security_group',
+ 'subnetpool', 'qos_policy', 'network'],
help=_('List network RBAC policies according to '
- 'given object type ("address_scope", "security_group", '
- '"subnetpool", "qos_policy" or "network")')
+ 'given object type ("address_group", "address_scope", '
+ '"security_group", "subnetpool", "qos_policy" or '
+ '"network")')
)
parser.add_argument(
'--action',
diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py
index 16885eb8..9a01758c 100644
--- a/openstackclient/tests/unit/compute/v2/test_server.py
+++ b/openstackclient/tests/unit/compute/v2/test_server.py
@@ -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):
diff --git a/openstackclient/tests/unit/compute/v2/test_server_group.py b/openstackclient/tests/unit/compute/v2/test_server_group.py
index 732c1881..3ed19e27 100644
--- a/openstackclient/tests/unit/compute/v2/test_server_group.py
+++ b/openstackclient/tests/unit/compute/v2/test_server_group.py
@@ -326,10 +326,13 @@ class TestServerGroupList(TestServerGroup):
verifylist = [
('all_projects', False),
('long', False),
+ ('limit', None),
+ ('offset', None),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.server_groups_mock.list.assert_called_once_with(False)
+
+ self.server_groups_mock.list.assert_called_once_with()
self.assertCountEqual(self.list_columns, columns)
self.assertCountEqual(self.list_data, tuple(data))
@@ -342,14 +345,49 @@ class TestServerGroupList(TestServerGroup):
verifylist = [
('all_projects', True),
('long', True),
+ ('limit', None),
+ ('offset', None),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.server_groups_mock.list.assert_called_once_with(True)
+ self.server_groups_mock.list.assert_called_once_with(
+ all_projects=True)
self.assertCountEqual(self.list_columns_long, columns)
self.assertCountEqual(self.list_data_long, tuple(data))
+ def test_server_group_list_with_limit(self):
+ arglist = [
+ '--limit', '1',
+ ]
+ verifylist = [
+ ('all_projects', False),
+ ('long', False),
+ ('limit', 1),
+ ('offset', None),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.cmd.take_action(parsed_args)
+
+ self.server_groups_mock.list.assert_called_once_with(limit=1)
+
+ def test_server_group_list_with_offset(self):
+ arglist = [
+ '--offset', '5',
+ ]
+ verifylist = [
+ ('all_projects', False),
+ ('long', False),
+ ('limit', None),
+ ('offset', 5),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.cmd.take_action(parsed_args)
+
+ self.server_groups_mock.list.assert_called_once_with(offset=5)
+
class TestServerGroupListV264(TestServerGroupV264):
@@ -400,7 +438,7 @@ class TestServerGroupListV264(TestServerGroupV264):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.server_groups_mock.list.assert_called_once_with(False)
+ self.server_groups_mock.list.assert_called_once_with()
self.assertCountEqual(self.list_columns, columns)
self.assertCountEqual(self.list_data, tuple(data))
@@ -416,7 +454,8 @@ class TestServerGroupListV264(TestServerGroupV264):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.server_groups_mock.list.assert_called_once_with(True)
+ self.server_groups_mock.list.assert_called_once_with(
+ all_projects=True)
self.assertCountEqual(self.list_columns_long, columns)
self.assertCountEqual(self.list_data_long, tuple(data))
diff --git a/openstackclient/tests/unit/network/v2/test_network_rbac.py b/openstackclient/tests/unit/network/v2/test_network_rbac.py
index d7c71ea7..08be64c5 100644
--- a/openstackclient/tests/unit/network/v2/test_network_rbac.py
+++ b/openstackclient/tests/unit/network/v2/test_network_rbac.py
@@ -42,6 +42,7 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
sg_object = network_fakes.FakeNetworkSecGroup.create_one_security_group()
as_object = network_fakes.FakeAddressScope.create_one_address_scope()
snp_object = network_fakes.FakeSubnetPool.create_one_subnet_pool()
+ ag_object = network_fakes.FakeAddressGroup.create_one_address_group()
project = identity_fakes_v3.FakeProject.create_one_project()
rbac_policy = network_fakes.FakeNetworkRBAC.create_one_network_rbac(
attrs={'tenant_id': project.id,
@@ -85,6 +86,8 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
return_value=self.as_object)
self.network.find_subnet_pool = mock.Mock(
return_value=self.snp_object)
+ self.network.find_address_group = mock.Mock(
+ return_value=self.ag_object)
self.projects_mock.get.return_value = self.project
def test_network_rbac_create_no_type(self):
@@ -236,7 +239,8 @@ class TestCreateNetworkRBAC(TestNetworkRBAC):
('qos_policy', "qos_object"),
('security_group', "sg_object"),
('subnetpool', "snp_object"),
- ('address_scope', "as_object")
+ ('address_scope', "as_object"),
+ ('address_group', "ag_object")
)
@ddt.unpack
def test_network_rbac_create_object(self, obj_type, obj_fake_attr):
diff --git a/releasenotes/notes/add-missing-server-group-list-opts-d3c3d98b7f7a56a6.yaml b/releasenotes/notes/add-missing-server-group-list-opts-d3c3d98b7f7a56a6.yaml
new file mode 100644
index 00000000..b359e77c
--- /dev/null
+++ b/releasenotes/notes/add-missing-server-group-list-opts-d3c3d98b7f7a56a6.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - |
+ Add ``--limit`` and ``--offset`` options to ``server group list`` command,
+ to configure pagination of results.
diff --git a/releasenotes/notes/rbac-add-address-group-f9bb83238b5a7c1f.yaml b/releasenotes/notes/rbac-add-address-group-f9bb83238b5a7c1f.yaml
new file mode 100644
index 00000000..d25da2f0
--- /dev/null
+++ b/releasenotes/notes/rbac-add-address-group-f9bb83238b5a7c1f.yaml
@@ -0,0 +1,4 @@
+features:
+ - |
+ Add ``address_group`` as a valid ``--type`` value for the
+ ``network rbac create`` and ``network rbac list`` commands.
diff --git a/releasenotes/notes/server-ops-all-projects-2ce2202cdf617184.yaml b/releasenotes/notes/server-ops-all-projects-2ce2202cdf617184.yaml
new file mode 100644
index 00000000..b14eb504
--- /dev/null
+++ b/releasenotes/notes/server-ops-all-projects-2ce2202cdf617184.yaml
@@ -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.
diff --git a/requirements.txt b/requirements.txt
index 5077ad77..d3a17e9a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -8,6 +8,7 @@ iso8601>=0.1.11 # MIT
openstacksdk>=0.53.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