Support hostname in show, update and delete host operations
The Blazar CLI doesn't support show, update and delete host operations with hostname. It only accepts id because host uses a key called hypervisor_hostname, rather one called name like for leases. This patch enables the Blazar CLI to support hostname as well. Change-Id: I3a7a3307099ed518d89de37039f9366770a21ce2 Closes-Bug: #1702266
This commit is contained in:
committed by
Pierre Riteau
parent
0606188dd1
commit
60e658d667
@@ -26,6 +26,11 @@ from cliff import show
|
||||
from blazarclient import exception
|
||||
from blazarclient import utils
|
||||
|
||||
HEX_ELEM = '[0-9A-Fa-f]'
|
||||
UUID_PATTERN = '-'.join([HEX_ELEM + '{8}', HEX_ELEM + '{4}',
|
||||
HEX_ELEM + '{4}', HEX_ELEM + '{4}',
|
||||
HEX_ELEM + '{12}'])
|
||||
|
||||
|
||||
class OpenStackCommand(command.Command):
|
||||
"""Base class for OpenStack commands."""
|
||||
@@ -65,6 +70,8 @@ class BlazarCommand(OpenStackCommand):
|
||||
json_indent = None
|
||||
resource = None
|
||||
allow_names = True
|
||||
name_key = None
|
||||
id_pattern = UUID_PATTERN
|
||||
|
||||
def __init__(self, app, app_args):
|
||||
super(BlazarCommand, self).__init__(app, app_args)
|
||||
@@ -164,7 +171,9 @@ class UpdateCommand(BlazarCommand):
|
||||
if self.allow_names:
|
||||
res_id = utils.find_resource_id_by_name_or_id(blazar_client,
|
||||
self.resource,
|
||||
parsed_args.id)
|
||||
parsed_args.id,
|
||||
self.name_key,
|
||||
self.id_pattern)
|
||||
else:
|
||||
res_id = parsed_args.id
|
||||
resource_manager = getattr(blazar_client, self.resource)
|
||||
@@ -199,7 +208,9 @@ class DeleteCommand(BlazarCommand):
|
||||
if self.allow_names:
|
||||
res_id = utils.find_resource_id_by_name_or_id(blazar_client,
|
||||
self.resource,
|
||||
parsed_args.id)
|
||||
parsed_args.id,
|
||||
self.name_key,
|
||||
self.id_pattern)
|
||||
else:
|
||||
res_id = parsed_args.id
|
||||
resource_manager.delete(res_id)
|
||||
@@ -284,7 +295,9 @@ class ShowCommand(BlazarCommand, show.ShowOne):
|
||||
if self.allow_names:
|
||||
res_id = utils.find_resource_id_by_name_or_id(blazar_client,
|
||||
self.resource,
|
||||
parsed_args.id)
|
||||
parsed_args.id,
|
||||
self.name_key,
|
||||
self.id_pattern)
|
||||
else:
|
||||
res_id = parsed_args.id
|
||||
|
||||
|
||||
244
blazarclient/tests/v1/shell_commands/test_hosts.py
Normal file
244
blazarclient/tests/v1/shell_commands/test_hosts.py
Normal file
@@ -0,0 +1,244 @@
|
||||
# Copyright (c) 2018 NTT
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import argparse
|
||||
import mock
|
||||
|
||||
from blazarclient import shell
|
||||
from blazarclient import tests
|
||||
from blazarclient.v1.shell_commands import hosts
|
||||
|
||||
|
||||
class CreateHostTest(tests.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(CreateHostTest, self).setUp()
|
||||
self.create_host = hosts.CreateHost(shell.BlazarShell(), mock.Mock())
|
||||
|
||||
def test_args2body(self):
|
||||
args = argparse.Namespace(
|
||||
name='test-host',
|
||||
extra_capabilities=[
|
||||
'extra_key1=extra_value1',
|
||||
'extra_key2=extra_value2',
|
||||
]
|
||||
)
|
||||
|
||||
expected = {
|
||||
'name': 'test-host',
|
||||
'extra_key1': 'extra_value1',
|
||||
'extra_key2': 'extra_value2',
|
||||
}
|
||||
|
||||
ret = self.create_host.args2body(args)
|
||||
self.assertDictEqual(ret, expected)
|
||||
|
||||
|
||||
class UpdateHostTest(tests.TestCase):
|
||||
|
||||
def create_update_command(self, list_value):
|
||||
mock_host_manager = mock.Mock()
|
||||
mock_host_manager.list.return_value = list_value
|
||||
|
||||
mock_client = mock.Mock()
|
||||
mock_client.host = mock_host_manager
|
||||
|
||||
blazar_shell = shell.BlazarShell()
|
||||
blazar_shell.client = mock_client
|
||||
return hosts.UpdateHost(blazar_shell, mock.Mock()), mock_host_manager
|
||||
|
||||
def test_update_host(self):
|
||||
list_value = [
|
||||
{'id': '101', 'hypervisor_hostname': 'host-1'},
|
||||
{'id': '201', 'hypervisor_hostname': 'host-2'},
|
||||
]
|
||||
update_host, host_manager = self.create_update_command(list_value)
|
||||
args = argparse.Namespace(
|
||||
id='101',
|
||||
extra_capabilities=[
|
||||
'key1=value1',
|
||||
'key2=value2'
|
||||
])
|
||||
expected = {
|
||||
'values': {
|
||||
'key1': 'value1',
|
||||
'key2': 'value2'
|
||||
}
|
||||
}
|
||||
update_host.run(args)
|
||||
host_manager.update.assert_called_once_with('101', **expected)
|
||||
|
||||
def test_update_host_with_name(self):
|
||||
list_value = [
|
||||
{'id': '101', 'hypervisor_hostname': 'host-1'},
|
||||
{'id': '201', 'hypervisor_hostname': 'host-2'},
|
||||
]
|
||||
update_host, host_manager = self.create_update_command(list_value)
|
||||
args = argparse.Namespace(
|
||||
id='host-1',
|
||||
extra_capabilities=[
|
||||
'key1=value1',
|
||||
'key2=value2'
|
||||
])
|
||||
expected = {
|
||||
'values': {
|
||||
'key1': 'value1',
|
||||
'key2': 'value2'
|
||||
}
|
||||
}
|
||||
update_host.run(args)
|
||||
host_manager.update.assert_called_once_with('101', **expected)
|
||||
|
||||
def test_update_host_with_name_startwith_number(self):
|
||||
list_value = [
|
||||
{'id': '101', 'hypervisor_hostname': '1-host'},
|
||||
{'id': '201', 'hypervisor_hostname': '2-host'},
|
||||
]
|
||||
update_host, host_manager = self.create_update_command(list_value)
|
||||
args = argparse.Namespace(
|
||||
id='1-host',
|
||||
extra_capabilities=[
|
||||
'key1=value1',
|
||||
'key2=value2'
|
||||
])
|
||||
expected = {
|
||||
'values': {
|
||||
'key1': 'value1',
|
||||
'key2': 'value2'
|
||||
}
|
||||
}
|
||||
update_host.run(args)
|
||||
host_manager.update.assert_called_once_with('101', **expected)
|
||||
|
||||
|
||||
class ShowHostTest(tests.TestCase):
|
||||
|
||||
def create_show_command(self, list_value, get_value):
|
||||
mock_host_manager = mock.Mock()
|
||||
mock_host_manager.list.return_value = list_value
|
||||
mock_host_manager.get.return_value = get_value
|
||||
|
||||
mock_client = mock.Mock()
|
||||
mock_client.host = mock_host_manager
|
||||
|
||||
blazar_shell = shell.BlazarShell()
|
||||
blazar_shell.client = mock_client
|
||||
return hosts.ShowHost(blazar_shell, mock.Mock()), mock_host_manager
|
||||
|
||||
def test_show_host(self):
|
||||
list_value = [
|
||||
{'id': '101', 'hypervisor_hostname': 'host-1'},
|
||||
{'id': '201', 'hypervisor_hostname': 'host-2'},
|
||||
]
|
||||
get_value = {
|
||||
'id': '101', 'hypervisor_hostname': 'host-1'}
|
||||
|
||||
show_host, host_manager = self.create_show_command(list_value,
|
||||
get_value)
|
||||
|
||||
args = argparse.Namespace(id='101')
|
||||
expected = [('hypervisor_hostname', 'id'), ('host-1', '101')]
|
||||
|
||||
ret = show_host.get_data(args)
|
||||
self.assertEqual(ret, expected)
|
||||
|
||||
host_manager.get.assert_called_once_with('101')
|
||||
|
||||
def test_show_host_with_name(self):
|
||||
list_value = [
|
||||
{'id': '101', 'hypervisor_hostname': 'host-1'},
|
||||
{'id': '201', 'hypervisor_hostname': 'host-2'},
|
||||
]
|
||||
get_value = {
|
||||
'id': '101', 'hypervisor_hostname': 'host-1'}
|
||||
|
||||
show_host, host_manager = self.create_show_command(list_value,
|
||||
get_value)
|
||||
|
||||
args = argparse.Namespace(id='host-1')
|
||||
expected = [('hypervisor_hostname', 'id'), ('host-1', '101')]
|
||||
|
||||
ret = show_host.get_data(args)
|
||||
self.assertEqual(ret, expected)
|
||||
|
||||
host_manager.get.assert_called_once_with('101')
|
||||
|
||||
def test_show_host_with_name_startwith_number(self):
|
||||
list_value = [
|
||||
{'id': '101', 'hypervisor_hostname': '1-host'},
|
||||
{'id': '201', 'hypervisor_hostname': '2-host'},
|
||||
]
|
||||
get_value = {
|
||||
'id': '101', 'hypervisor_hostname': '1-host'}
|
||||
|
||||
show_host, host_manager = self.create_show_command(list_value,
|
||||
get_value)
|
||||
args = argparse.Namespace(id='1-host')
|
||||
expected = [('hypervisor_hostname', 'id'), ('1-host', '101')]
|
||||
|
||||
ret = show_host.get_data(args)
|
||||
self.assertEqual(ret, expected)
|
||||
|
||||
host_manager.get.assert_called_once_with('101')
|
||||
|
||||
|
||||
class DeleteHostTest(tests.TestCase):
|
||||
|
||||
def create_delete_command(self, list_value):
|
||||
mock_host_manager = mock.Mock()
|
||||
mock_host_manager.list.return_value = list_value
|
||||
|
||||
mock_client = mock.Mock()
|
||||
mock_client.host = mock_host_manager
|
||||
|
||||
blazar_shell = shell.BlazarShell()
|
||||
blazar_shell.client = mock_client
|
||||
return hosts.DeleteHost(blazar_shell, mock.Mock()), mock_host_manager
|
||||
|
||||
def test_delete_host(self):
|
||||
list_value = [
|
||||
{'id': '101', 'hypervisor_hostname': 'host-1'},
|
||||
{'id': '201', 'hypervisor_hostname': 'host-2'},
|
||||
]
|
||||
delete_host, host_manager = self.create_delete_command(list_value)
|
||||
|
||||
args = argparse.Namespace(id='101')
|
||||
delete_host.run(args)
|
||||
|
||||
host_manager.delete.assert_called_once_with('101')
|
||||
|
||||
def test_delete_host_with_name(self):
|
||||
list_value = [
|
||||
{'id': '101', 'hypervisor_hostname': 'host-1'},
|
||||
{'id': '201', 'hypervisor_hostname': 'host-2'},
|
||||
]
|
||||
delete_host, host_manager = self.create_delete_command(list_value)
|
||||
|
||||
args = argparse.Namespace(id='host-1')
|
||||
delete_host.run(args)
|
||||
|
||||
host_manager.delete.assert_called_once_with('101')
|
||||
|
||||
def test_delete_host_with_name_startwith_number(self):
|
||||
list_value = [
|
||||
{'id': '101', 'hypervisor_hostname': '1-host'},
|
||||
{'id': '201', 'hypervisor_hostname': '2-host'},
|
||||
]
|
||||
delete_host, host_manager = self.create_delete_command(list_value)
|
||||
|
||||
args = argparse.Namespace(id='1-host')
|
||||
delete_host.run(args)
|
||||
|
||||
host_manager.delete.assert_called_once_with('101')
|
||||
@@ -22,10 +22,6 @@ from oslo_serialization import jsonutils as json
|
||||
from blazarclient import exception
|
||||
from blazarclient.i18n import _
|
||||
|
||||
HEX_ELEM = '[0-9A-Fa-f]'
|
||||
UUID_PATTERN = '-'.join([HEX_ELEM + '{8}', HEX_ELEM + '{4}',
|
||||
HEX_ELEM + '{4}', HEX_ELEM + '{4}',
|
||||
HEX_ELEM + '{12}'])
|
||||
ELAPSED_TIME_REGEX = '^(\d+)([s|m|h|d])$'
|
||||
|
||||
LEASE_DATE_FORMAT = '%Y-%m-%dT%H:%M:%S.%f'
|
||||
@@ -106,9 +102,10 @@ def get_item_properties(item, fields, mixed_case_fields=None, formatters=None):
|
||||
return tuple(row)
|
||||
|
||||
|
||||
def find_resource_id_by_name_or_id(client, resource, name_or_id):
|
||||
def find_resource_id_by_name_or_id(client, resource, name_or_id,
|
||||
name_key, id_pattern):
|
||||
resource_manager = getattr(client, resource)
|
||||
is_id = re.match(UUID_PATTERN, name_or_id)
|
||||
is_id = re.match(id_pattern, name_or_id)
|
||||
if is_id:
|
||||
resources = resource_manager.list()
|
||||
for resource in resources:
|
||||
@@ -116,17 +113,18 @@ def find_resource_id_by_name_or_id(client, resource, name_or_id):
|
||||
return name_or_id
|
||||
raise exception.BlazarClientException('No resource found with ID %s' %
|
||||
name_or_id)
|
||||
return _find_resource_id_by_name(client, resource, name_or_id)
|
||||
return _find_resource_id_by_name(client, resource, name_or_id, name_key)
|
||||
|
||||
|
||||
def _find_resource_id_by_name(client, resource, name):
|
||||
def _find_resource_id_by_name(client, resource, name, name_key):
|
||||
resource_manager = getattr(client, resource)
|
||||
resources = resource_manager.list()
|
||||
|
||||
named_resources = []
|
||||
key = name_key if name_key else 'name'
|
||||
|
||||
for resource in resources:
|
||||
if resource['name'] == name:
|
||||
if resource[key] == name:
|
||||
named_resources.append(resource['id'])
|
||||
if len(named_resources) > 1:
|
||||
raise exception.NoUniqueMatch(message="There are more than one "
|
||||
|
||||
@@ -17,6 +17,8 @@ import logging
|
||||
|
||||
from blazarclient import command
|
||||
|
||||
HOST_ID_PATTERN = '^[0-9]+$'
|
||||
|
||||
|
||||
class ListHosts(command.ListCommand):
|
||||
"""Print a list of hosts."""
|
||||
@@ -39,9 +41,8 @@ class ShowHost(command.ShowCommand):
|
||||
"""Show host details."""
|
||||
resource = 'host'
|
||||
json_indent = 4
|
||||
# NOTE(sbauza): We can't find by name as there is currently no column
|
||||
# called 'name' but rather 'hypervisor_hostname'
|
||||
allow_names = False
|
||||
name_key = 'hypervisor_hostname'
|
||||
id_pattern = HOST_ID_PATTERN
|
||||
log = logging.getLogger(__name__ + '.ShowHost')
|
||||
|
||||
|
||||
@@ -85,8 +86,9 @@ class UpdateHost(command.UpdateCommand):
|
||||
"""Update attributes of a host."""
|
||||
resource = 'host'
|
||||
json_indent = 4
|
||||
allow_names = False
|
||||
log = logging.getLogger(__name__ + '.UpdateHost')
|
||||
name_key = 'hypervisor_hostname'
|
||||
id_pattern = HOST_ID_PATTERN
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(UpdateHost, self).get_parser(prog_name)
|
||||
@@ -115,7 +117,6 @@ class UpdateHost(command.UpdateCommand):
|
||||
class DeleteHost(command.DeleteCommand):
|
||||
"""Delete a host."""
|
||||
resource = 'host'
|
||||
# NOTE(sbauza): We can't find by name as there is currently no column
|
||||
# called 'name' but rather 'hypervisor_hostname'
|
||||
allow_names = False
|
||||
log = logging.getLogger(__name__ + '.DeleteHost')
|
||||
name_key = 'hypervisor_hostname'
|
||||
id_pattern = HOST_ID_PATTERN
|
||||
|
||||
Reference in New Issue
Block a user