Remove deprecated commands
There are several commands and options which have been deprecated, it's now time to clean those up. flavor-access-list and aggregate-update had options deprecated in newton, but that's still too new to remove those yet. Here is the table of affected commands and the deprecation commit and initial release with the deprecation. Command Commit Release ------- ------ ------- rename1d1e43957
3.3.0 root-password3bf6f8fe9
2.27.0 add-floating-ip4f92f7ba0
2.16.0 remove-floating-ip4f92f7ba0
2.16.0 absolute-limitsbf6fbdb8d
2.25.0 rate-limitsbf6fbdb8d
2.25.0 aggregate-detailsbf68a0cf1
3.4.0 endpoints1f11840dd
3.1.0 credentials1f11840dd
3.1.0 Note that network resource quota deprecations are not part of this change as those are impacted by the 2.36 microversion and will be cleaned up later separately. Change-Id: Id649d16ec2cdeb04bbaf2239a5e813abcca9c65d
This commit is contained in:
parent
ef3856804d
commit
fdc5aa5d5c
@ -32,10 +32,6 @@ class SimpleReadOnlyNovaClientTest(base.ClientTestBase):
|
|||||||
|
|
||||||
# NOTE(jogo): Commands in order listed in 'nova help'
|
# NOTE(jogo): Commands in order listed in 'nova help'
|
||||||
|
|
||||||
def test_admin_absolute_limites(self):
|
|
||||||
self.nova('absolute-limits')
|
|
||||||
self.nova('absolute-limits', params='--reserved')
|
|
||||||
|
|
||||||
def test_admin_aggregate_list(self):
|
def test_admin_aggregate_list(self):
|
||||||
self.nova('aggregate-list')
|
self.nova('aggregate-list')
|
||||||
|
|
||||||
@ -45,9 +41,6 @@ class SimpleReadOnlyNovaClientTest(base.ClientTestBase):
|
|||||||
def test_admin_cloudpipe_list(self):
|
def test_admin_cloudpipe_list(self):
|
||||||
self.nova('cloudpipe-list')
|
self.nova('cloudpipe-list')
|
||||||
|
|
||||||
def test_admin_credentials(self):
|
|
||||||
self.nova('credentials')
|
|
||||||
|
|
||||||
# "Neutron does not provide this feature"
|
# "Neutron does not provide this feature"
|
||||||
def test_admin_dns_domains(self):
|
def test_admin_dns_domains(self):
|
||||||
self.skip_if_neutron()
|
self.skip_if_neutron()
|
||||||
@ -58,9 +51,6 @@ class SimpleReadOnlyNovaClientTest(base.ClientTestBase):
|
|||||||
self.skip_if_neutron()
|
self.skip_if_neutron()
|
||||||
self.nova('dns-list')
|
self.nova('dns-list')
|
||||||
|
|
||||||
def test_admin_endpoints(self):
|
|
||||||
self.nova('endpoints')
|
|
||||||
|
|
||||||
def test_admin_flavor_acces_list(self):
|
def test_admin_flavor_acces_list(self):
|
||||||
self.assertRaises(exceptions.CommandFailed,
|
self.assertRaises(exceptions.CommandFailed,
|
||||||
self.nova,
|
self.nova,
|
||||||
@ -112,9 +102,6 @@ class SimpleReadOnlyNovaClientTest(base.ClientTestBase):
|
|||||||
def test_admin_network_list(self):
|
def test_admin_network_list(self):
|
||||||
self.nova('network-list')
|
self.nova('network-list')
|
||||||
|
|
||||||
def test_admin_rate_limits(self):
|
|
||||||
self.nova('rate-limits')
|
|
||||||
|
|
||||||
def test_admin_secgroup_list(self):
|
def test_admin_secgroup_list(self):
|
||||||
self.nova('secgroup-list')
|
self.nova('secgroup-list')
|
||||||
|
|
||||||
|
@ -1466,11 +1466,6 @@ class ShellTest(utils.TestCase):
|
|||||||
self.run_command('migrate sample-server')
|
self.run_command('migrate sample-server')
|
||||||
self.assert_called('POST', '/servers/1234/action', {'migrate': None})
|
self.assert_called('POST', '/servers/1234/action', {'migrate': None})
|
||||||
|
|
||||||
def test_rename(self):
|
|
||||||
self.run_command('rename sample-server newname')
|
|
||||||
self.assert_called('PUT', '/servers/1234',
|
|
||||||
{'server': {'name': 'newname'}})
|
|
||||||
|
|
||||||
def test_resize(self):
|
def test_resize(self):
|
||||||
self.run_command('resize sample-server 1')
|
self.run_command('resize sample-server 1')
|
||||||
self.assert_called('POST', '/servers/1234/action',
|
self.assert_called('POST', '/servers/1234/action',
|
||||||
@ -1492,14 +1487,6 @@ class ShellTest(utils.TestCase):
|
|||||||
self.assert_called('POST', '/servers/1234/action',
|
self.assert_called('POST', '/servers/1234/action',
|
||||||
{'changePassword': {'adminPass': 'p'}})
|
{'changePassword': {'adminPass': 'p'}})
|
||||||
|
|
||||||
# root-password is deprecated, keeping this arond until it's removed
|
|
||||||
# entirely - penick
|
|
||||||
@mock.patch('getpass.getpass', mock.Mock(return_value='p'))
|
|
||||||
def test_root_password(self):
|
|
||||||
self.run_command('root-password sample-server')
|
|
||||||
self.assert_called('POST', '/servers/1234/action',
|
|
||||||
{'changePassword': {'adminPass': 'p'}})
|
|
||||||
|
|
||||||
def test_scrub(self):
|
def test_scrub(self):
|
||||||
self.run_command('scrub 4ffc664c198e435e9853f2538fbcd7a7')
|
self.run_command('scrub 4ffc664c198e435e9853f2538fbcd7a7')
|
||||||
self.assert_called('GET', '/os-networks', pos=-4)
|
self.assert_called('GET', '/os-networks', pos=-4)
|
||||||
@ -1752,16 +1739,6 @@ class ShellTest(utils.TestCase):
|
|||||||
self.assert_called('PUT', '/os-floating-ips-bulk/delete',
|
self.assert_called('PUT', '/os-floating-ips-bulk/delete',
|
||||||
{'ip_range': '10.0.0.1/24'})
|
{'ip_range': '10.0.0.1/24'})
|
||||||
|
|
||||||
def test_server_floating_ip_add(self):
|
|
||||||
self.run_command('add-floating-ip sample-server 11.0.0.1')
|
|
||||||
self.assert_called('POST', '/servers/1234/action',
|
|
||||||
{'addFloatingIp': {'address': '11.0.0.1'}})
|
|
||||||
|
|
||||||
def test_server_floating_ip_remove(self):
|
|
||||||
self.run_command('remove-floating-ip sample-server 11.0.0.1')
|
|
||||||
self.assert_called('POST', '/servers/1234/action',
|
|
||||||
{'removeFloatingIp': {'address': '11.0.0.1'}})
|
|
||||||
|
|
||||||
def test_server_floating_ip_associate(self):
|
def test_server_floating_ip_associate(self):
|
||||||
self.run_command('floating-ip-associate sample-server 11.0.0.1')
|
self.run_command('floating-ip-associate sample-server 11.0.0.1')
|
||||||
self.assert_called('POST', '/servers/1234/action',
|
self.assert_called('POST', '/servers/1234/action',
|
||||||
@ -1935,14 +1912,6 @@ class ShellTest(utils.TestCase):
|
|||||||
self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2)
|
self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2)
|
||||||
self.assert_called('GET', '/os-aggregates/1', pos=-1)
|
self.assert_called('GET', '/os-aggregates/1', pos=-1)
|
||||||
|
|
||||||
def test_aggregate_details_by_id(self):
|
|
||||||
self.run_command('aggregate-details 1')
|
|
||||||
self.assert_called('GET', '/os-aggregates/1')
|
|
||||||
|
|
||||||
def test_aggregate_details_by_name(self):
|
|
||||||
self.run_command('aggregate-details test')
|
|
||||||
self.assert_called('GET', '/os-aggregates')
|
|
||||||
|
|
||||||
def test_aggregate_show_by_id(self):
|
def test_aggregate_show_by_id(self):
|
||||||
self.run_command('aggregate-show 1')
|
self.run_command('aggregate-show 1')
|
||||||
self.assert_called('GET', '/os-aggregates/1')
|
self.assert_called('GET', '/os-aggregates/1')
|
||||||
@ -2662,16 +2631,6 @@ class ShellTest(utils.TestCase):
|
|||||||
'backup_type': 'daily',
|
'backup_type': 'daily',
|
||||||
'rotation': '1'}})
|
'rotation': '1'}})
|
||||||
|
|
||||||
def test_absolute_limits(self):
|
|
||||||
self.run_command('absolute-limits')
|
|
||||||
self.assert_called('GET', '/limits')
|
|
||||||
|
|
||||||
self.run_command('absolute-limits --reserved')
|
|
||||||
self.assert_called('GET', '/limits?reserved=1')
|
|
||||||
|
|
||||||
self.run_command('absolute-limits --tenant 1234')
|
|
||||||
self.assert_called('GET', '/limits?tenant_id=1234')
|
|
||||||
|
|
||||||
def test_limits(self):
|
def test_limits(self):
|
||||||
self.run_command('limits')
|
self.run_command('limits')
|
||||||
self.assert_called('GET', '/limits')
|
self.assert_called('GET', '/limits')
|
||||||
@ -3239,44 +3198,6 @@ class GetSecgroupTest(utils.TestCase):
|
|||||||
'group_one')
|
'group_one')
|
||||||
|
|
||||||
|
|
||||||
class GetFirstEndpointTest(utils.TestCase):
|
|
||||||
def test_only_one_endpoint(self):
|
|
||||||
# If there is only one endpoint, it is returned.
|
|
||||||
endpoint = {"url": "test"}
|
|
||||||
result = novaclient.v2.shell._get_first_endpoint([endpoint], "XYZ")
|
|
||||||
self.assertEqual(endpoint, result)
|
|
||||||
|
|
||||||
def test_multiple_endpoints(self):
|
|
||||||
# If there are multiple endpoints, the first one of the appropriate
|
|
||||||
# region is returned.
|
|
||||||
endpoints = [
|
|
||||||
{"region": "XYZ"},
|
|
||||||
{"region": "ORD", "number": 1},
|
|
||||||
{"region": "ORD", "number": 2}
|
|
||||||
]
|
|
||||||
result = novaclient.v2.shell._get_first_endpoint(endpoints, "ORD")
|
|
||||||
self.assertEqual(endpoints[1], result)
|
|
||||||
|
|
||||||
def test_multiple_endpoints_but_none_suitable(self):
|
|
||||||
# If there are multiple endpoints but none of them are suitable, an
|
|
||||||
# exception is raised.
|
|
||||||
|
|
||||||
endpoints = [
|
|
||||||
{"region": "XYZ"},
|
|
||||||
{"region": "PQR"},
|
|
||||||
{"region": "STU"}
|
|
||||||
]
|
|
||||||
self.assertRaises(LookupError,
|
|
||||||
novaclient.v2.shell._get_first_endpoint,
|
|
||||||
endpoints, "ORD")
|
|
||||||
|
|
||||||
def test_no_endpoints(self):
|
|
||||||
# If there are no endpoints available, an exception is raised.
|
|
||||||
self.assertRaises(LookupError,
|
|
||||||
novaclient.v2.shell._get_first_endpoint,
|
|
||||||
[], "ORD")
|
|
||||||
|
|
||||||
|
|
||||||
class PollForStatusTestCase(utils.TestCase):
|
class PollForStatusTestCase(utils.TestCase):
|
||||||
@mock.patch("novaclient.v2.shell.time")
|
@mock.patch("novaclient.v2.shell.time")
|
||||||
def test_simple_usage(self, mock_time):
|
def test_simple_usage(self, mock_time):
|
||||||
|
@ -28,7 +28,6 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import warnings
|
|
||||||
|
|
||||||
from oslo_utils import encodeutils
|
from oslo_utils import encodeutils
|
||||||
from oslo_utils import netutils
|
from oslo_utils import netutils
|
||||||
@ -1888,15 +1887,6 @@ def do_rebuild(cs, args):
|
|||||||
_poll_for_status(cs.servers.get, server.id, 'rebuilding', ['active'])
|
_poll_for_status(cs.servers.get, server.id, 'rebuilding', ['active'])
|
||||||
|
|
||||||
|
|
||||||
@utils.arg(
|
|
||||||
'server', metavar='<server>',
|
|
||||||
help=_('Name (old name) or ID of server.'))
|
|
||||||
@utils.arg('name', metavar='<name>', help=_('New name for the server.'))
|
|
||||||
def do_rename(cs, args):
|
|
||||||
"""DEPRECATED, use update instead."""
|
|
||||||
do_update(cs, args)
|
|
||||||
|
|
||||||
|
|
||||||
@utils.arg(
|
@utils.arg(
|
||||||
'server', metavar='<server>',
|
'server', metavar='<server>',
|
||||||
help=_('Name (old name) or ID of server.'))
|
help=_('Name (old name) or ID of server.'))
|
||||||
@ -2123,12 +2113,6 @@ def do_refresh_network(cs, args):
|
|||||||
'name': 'network-changed'}])
|
'name': 'network-changed'}])
|
||||||
|
|
||||||
|
|
||||||
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
|
|
||||||
def do_root_password(cs, args):
|
|
||||||
"""DEPRECATED, use set-password instead."""
|
|
||||||
do_set_password(cs, args)
|
|
||||||
|
|
||||||
|
|
||||||
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
|
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
|
||||||
def do_set_password(cs, args):
|
def do_set_password(cs, args):
|
||||||
"""
|
"""
|
||||||
@ -2679,18 +2663,6 @@ def do_console_log(cs, args):
|
|||||||
print(data)
|
print(data)
|
||||||
|
|
||||||
|
|
||||||
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
|
|
||||||
@utils.arg('address', metavar='<address>', help=_('IP Address.'))
|
|
||||||
@utils.arg(
|
|
||||||
'--fixed-address',
|
|
||||||
metavar='<fixed_address>',
|
|
||||||
default=None,
|
|
||||||
help=_('Fixed IP Address to associate with.'))
|
|
||||||
def do_add_floating_ip(cs, args):
|
|
||||||
"""DEPRECATED, use floating-ip-associate instead."""
|
|
||||||
_associate_floating_ip(cs, args)
|
|
||||||
|
|
||||||
|
|
||||||
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
|
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
|
||||||
@utils.arg('address', metavar='<address>', help=_('IP Address.'))
|
@utils.arg('address', metavar='<address>', help=_('IP Address.'))
|
||||||
@utils.arg(
|
@utils.arg(
|
||||||
@ -2708,13 +2680,6 @@ def _associate_floating_ip(cs, args):
|
|||||||
server.add_floating_ip(args.address, args.fixed_address)
|
server.add_floating_ip(args.address, args.fixed_address)
|
||||||
|
|
||||||
|
|
||||||
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
|
|
||||||
@utils.arg('address', metavar='<address>', help=_('IP Address.'))
|
|
||||||
def do_remove_floating_ip(cs, args):
|
|
||||||
"""DEPRECATED, use floating-ip-disassociate instead."""
|
|
||||||
_disassociate_floating_ip(cs, args)
|
|
||||||
|
|
||||||
|
|
||||||
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
|
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
|
||||||
@utils.arg('address', metavar='<address>', help=_('IP Address.'))
|
@utils.arg('address', metavar='<address>', help=_('IP Address.'))
|
||||||
def do_floating_ip_disassociate(cs, args):
|
def do_floating_ip_disassociate(cs, args):
|
||||||
@ -3369,25 +3334,6 @@ def _find_keypair(cs, keypair):
|
|||||||
return utils.find_resource(cs.keypairs, keypair)
|
return utils.find_resource(cs.keypairs, keypair)
|
||||||
|
|
||||||
|
|
||||||
@utils.arg(
|
|
||||||
'--tenant',
|
|
||||||
# nova db searches by project_id
|
|
||||||
dest='tenant',
|
|
||||||
metavar='<tenant>',
|
|
||||||
nargs='?',
|
|
||||||
help=_('Display information from single tenant (Admin only).'))
|
|
||||||
@utils.arg(
|
|
||||||
'--reserved',
|
|
||||||
dest='reserved',
|
|
||||||
action='store_true',
|
|
||||||
default=False,
|
|
||||||
help=_('Include reservations count.'))
|
|
||||||
def do_absolute_limits(cs, args):
|
|
||||||
"""DEPRECATED, use limits instead."""
|
|
||||||
limits = cs.limits.get(args.reserved, args.tenant).absolute
|
|
||||||
_print_absolute_limits(limits)
|
|
||||||
|
|
||||||
|
|
||||||
def _print_absolute_limits(limits):
|
def _print_absolute_limits(limits):
|
||||||
"""Prints absolute limits."""
|
"""Prints absolute limits."""
|
||||||
class Limit(object):
|
class Limit(object):
|
||||||
@ -3450,12 +3396,6 @@ def _print_absolute_limits(limits):
|
|||||||
utils.print_list(limit_list, columns)
|
utils.print_list(limit_list, columns)
|
||||||
|
|
||||||
|
|
||||||
def do_rate_limits(cs, args):
|
|
||||||
"""DEPRECATED, use limits instead."""
|
|
||||||
limits = cs.limits.get().rate
|
|
||||||
_print_rate_limits(limits)
|
|
||||||
|
|
||||||
|
|
||||||
def _print_rate_limits(limits):
|
def _print_rate_limits(limits):
|
||||||
"""print rate limits."""
|
"""print rate limits."""
|
||||||
columns = ['Verb', 'URI', 'Value', 'Remain', 'Unit', 'Next_Available']
|
columns = ['Verb', 'URI', 'Value', 'Remain', 'Unit', 'Next_Available']
|
||||||
@ -3837,14 +3777,6 @@ def do_aggregate_remove_host(cs, args):
|
|||||||
_print_aggregate_details(aggregate)
|
_print_aggregate_details(aggregate)
|
||||||
|
|
||||||
|
|
||||||
@utils.arg(
|
|
||||||
'aggregate', metavar='<aggregate>',
|
|
||||||
help=_('Name or ID of aggregate.'))
|
|
||||||
def do_aggregate_details(cs, args):
|
|
||||||
"""DEPRECATED, use aggregate-show instead."""
|
|
||||||
do_aggregate_show(cs, args)
|
|
||||||
|
|
||||||
|
|
||||||
@utils.arg(
|
@utils.arg(
|
||||||
'aggregate', metavar='<aggregate>',
|
'aggregate', metavar='<aggregate>',
|
||||||
help=_('Name or ID of aggregate.'))
|
help=_('Name or ID of aggregate.'))
|
||||||
@ -4296,86 +4228,6 @@ def do_hypervisor_stats(cs, args):
|
|||||||
utils.print_dict(stats._info.copy())
|
utils.print_dict(stats._info.copy())
|
||||||
|
|
||||||
|
|
||||||
def ensure_service_catalog_present(cs):
|
|
||||||
if not hasattr(cs.client, 'service_catalog'):
|
|
||||||
# Turn off token caching and re-auth
|
|
||||||
cs.client.unauthenticate()
|
|
||||||
cs.client.use_token_cache(False)
|
|
||||||
cs.client.authenticate()
|
|
||||||
|
|
||||||
|
|
||||||
def do_endpoints(cs, _args):
|
|
||||||
"""Discover endpoints that get returned from the authenticate services."""
|
|
||||||
warnings.warn(
|
|
||||||
"nova endpoints is deprecated, use openstack catalog list instead")
|
|
||||||
if isinstance(cs.client, client.SessionClient):
|
|
||||||
access = cs.client.auth.get_access(cs.client.session)
|
|
||||||
for service in access.service_catalog.catalog:
|
|
||||||
_print_endpoints(service, cs.client.region_name)
|
|
||||||
else:
|
|
||||||
ensure_service_catalog_present(cs)
|
|
||||||
|
|
||||||
catalog = cs.client.service_catalog.catalog
|
|
||||||
region = cs.client.region_name
|
|
||||||
for service in catalog['access']['serviceCatalog']:
|
|
||||||
_print_endpoints(service, region)
|
|
||||||
|
|
||||||
|
|
||||||
def _print_endpoints(service, region):
|
|
||||||
name, endpoints = service["name"], service["endpoints"]
|
|
||||||
|
|
||||||
try:
|
|
||||||
endpoint = _get_first_endpoint(endpoints, region)
|
|
||||||
utils.print_dict(endpoint, name)
|
|
||||||
except LookupError:
|
|
||||||
print(_("WARNING: %(service)s has no endpoint in %(region)s! "
|
|
||||||
"Available endpoints for this service:") %
|
|
||||||
{'service': name, 'region': region})
|
|
||||||
for other_endpoint in endpoints:
|
|
||||||
utils.print_dict(other_endpoint, name)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_first_endpoint(endpoints, region):
|
|
||||||
"""Find the first suitable endpoint in endpoints.
|
|
||||||
|
|
||||||
If there is only one endpoint, return it. If there is more than
|
|
||||||
one endpoint, return the first one with the given region. If there
|
|
||||||
are no endpoints, or there is more than one endpoint but none of
|
|
||||||
them match the given region, raise KeyError.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if len(endpoints) == 1:
|
|
||||||
return endpoints[0]
|
|
||||||
else:
|
|
||||||
for candidate_endpoint in endpoints:
|
|
||||||
if candidate_endpoint["region"] == region:
|
|
||||||
return candidate_endpoint
|
|
||||||
|
|
||||||
raise LookupError("No suitable endpoint found")
|
|
||||||
|
|
||||||
|
|
||||||
@utils.arg(
|
|
||||||
'--wrap', dest='wrap', metavar='<integer>', default=64,
|
|
||||||
help=_('Wrap PKI tokens to a specified length, or 0 to disable.'))
|
|
||||||
def do_credentials(cs, _args):
|
|
||||||
"""Show user credentials returned from auth."""
|
|
||||||
warnings.warn(
|
|
||||||
"nova credentials is deprecated, use openstack client instead")
|
|
||||||
if isinstance(cs.client, client.SessionClient):
|
|
||||||
access = cs.client.auth.get_access(cs.client.session)
|
|
||||||
utils.print_dict(access._user, 'User Credentials',
|
|
||||||
wrap=int(_args.wrap))
|
|
||||||
if hasattr(access, '_token'):
|
|
||||||
utils.print_dict(access._token, 'Token', wrap=int(_args.wrap))
|
|
||||||
else:
|
|
||||||
ensure_service_catalog_present(cs)
|
|
||||||
catalog = cs.client.service_catalog.catalog
|
|
||||||
utils.print_dict(catalog['access']['user'], "User Credentials",
|
|
||||||
wrap=int(_args.wrap))
|
|
||||||
utils.print_dict(catalog['access']['token'], "Token",
|
|
||||||
wrap=int(_args.wrap))
|
|
||||||
|
|
||||||
|
|
||||||
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
|
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
|
||||||
@utils.arg(
|
@utils.arg(
|
||||||
'--port',
|
'--port',
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
prelude: >
|
||||||
|
Several deprecated commands have been removed. See the upgrade section for
|
||||||
|
details.
|
||||||
|
upgrade:
|
||||||
|
- |
|
||||||
|
The following deprecated commands have been removed:
|
||||||
|
|
||||||
|
* absolute-limits
|
||||||
|
* add-floating-ip
|
||||||
|
* aggregate-details
|
||||||
|
* credentials
|
||||||
|
* endpoints
|
||||||
|
* rate-limits
|
||||||
|
* remove-floating-ip
|
||||||
|
* rename
|
||||||
|
* root-password
|
Loading…
Reference in New Issue
Block a user