Implement list_instances and `metalsmith list`
Change-Id: I51f83fcebf665be8b290774433fbf1da5108ec27 Story: #2003585 Task: #24891
This commit is contained in:
parent
06a19626cd
commit
d20abe3c79
|
@ -85,6 +85,11 @@ def _do_wait(api, args, formatter):
|
||||||
formatter.show(instances)
|
formatter.show(instances)
|
||||||
|
|
||||||
|
|
||||||
|
def _do_list(api, args, formatter):
|
||||||
|
instances = api.list_instances()
|
||||||
|
formatter.show(instances)
|
||||||
|
|
||||||
|
|
||||||
def _parse_args(args, config):
|
def _parse_args(args, config):
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description='Deployment and Scheduling tool for Bare Metal')
|
description='Deployment and Scheduling tool for Bare Metal')
|
||||||
|
@ -151,6 +156,9 @@ def _parse_args(args, config):
|
||||||
show.set_defaults(func=_do_show)
|
show.set_defaults(func=_do_show)
|
||||||
show.add_argument('instance', nargs='+', help='instance UUID(s)')
|
show.add_argument('instance', nargs='+', help='instance UUID(s)')
|
||||||
|
|
||||||
|
show = subparsers.add_parser('list')
|
||||||
|
show.set_defaults(func=_do_list)
|
||||||
|
|
||||||
wait = subparsers.add_parser('wait')
|
wait = subparsers.add_parser('wait')
|
||||||
wait.set_defaults(func=_do_wait)
|
wait.set_defaults(func=_do_wait)
|
||||||
wait.add_argument('instance', nargs='+', help='instance UUID(s)')
|
wait.add_argument('instance', nargs='+', help='instance UUID(s)')
|
||||||
|
|
|
@ -16,14 +16,15 @@
|
||||||
from metalsmith import _os_api
|
from metalsmith import _os_api
|
||||||
|
|
||||||
|
|
||||||
|
_PROGRESS_STATES = frozenset(['deploying', 'wait call-back',
|
||||||
|
'deploy complete'])
|
||||||
# NOTE(dtantsur): include available since there is a period of time between
|
# NOTE(dtantsur): include available since there is a period of time between
|
||||||
# claiming the instance and starting the actual provisioning via ironic.
|
# claiming the instance and starting the actual provisioning via ironic.
|
||||||
_DEPLOYING_STATES = frozenset(['available', 'deploying', 'wait call-back',
|
_DEPLOYING_STATES = _PROGRESS_STATES | {'available'}
|
||||||
'deploy complete'])
|
|
||||||
_ACTIVE_STATES = frozenset(['active'])
|
_ACTIVE_STATES = frozenset(['active'])
|
||||||
_ERROR_STATE = frozenset(['error', 'deploy failed'])
|
_ERROR_STATES = frozenset(['error', 'deploy failed'])
|
||||||
|
|
||||||
_HEALTHY_STATES = frozenset(['deploying', 'active'])
|
_HEALTHY_STATES = _PROGRESS_STATES | _ACTIVE_STATES
|
||||||
|
|
||||||
|
|
||||||
class Instance(object):
|
class Instance(object):
|
||||||
|
@ -58,6 +59,10 @@ class Instance(object):
|
||||||
"""Whether the node is deployed."""
|
"""Whether the node is deployed."""
|
||||||
return self._node.provision_state in _ACTIVE_STATES
|
return self._node.provision_state in _ACTIVE_STATES
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _is_deployed_by_metalsmith(self):
|
||||||
|
return _os_api.HOSTNAME_FIELD in self._node.instance_info
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_healthy(self):
|
def is_healthy(self):
|
||||||
"""Whether the node is not at fault or maintenance."""
|
"""Whether the node is not at fault or maintenance."""
|
||||||
|
@ -101,7 +106,7 @@ class Instance(object):
|
||||||
prov_state = self._node.provision_state
|
prov_state = self._node.provision_state
|
||||||
if prov_state in _DEPLOYING_STATES:
|
if prov_state in _DEPLOYING_STATES:
|
||||||
return 'deploying'
|
return 'deploying'
|
||||||
elif prov_state in _ERROR_STATE:
|
elif prov_state in _ERROR_STATES:
|
||||||
return 'error'
|
return 'error'
|
||||||
elif prov_state in _ACTIVE_STATES:
|
elif prov_state in _ACTIVE_STATES:
|
||||||
if self._node.maintenance:
|
if self._node.maintenance:
|
||||||
|
|
|
@ -506,3 +506,14 @@ class Provisioner(object):
|
||||||
self._api.get_node(inst, accept_hostname=True))
|
self._api.get_node(inst, accept_hostname=True))
|
||||||
for inst in instances
|
for inst in instances
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def list_instances(self):
|
||||||
|
"""List instances deployed by metalsmith.
|
||||||
|
|
||||||
|
:return: list of :py:class:`metalsmith.Instance` objects.
|
||||||
|
"""
|
||||||
|
nodes = self._api.list_nodes(provision_state=None, associated=True)
|
||||||
|
instances = [i for i in
|
||||||
|
(_instance.Instance(self._api, node) for node in nodes)
|
||||||
|
if i._is_deployed_by_metalsmith]
|
||||||
|
return instances
|
||||||
|
|
|
@ -751,6 +751,18 @@ class TestShowWait(testtools.TestCase):
|
||||||
mock_pr.return_value.show_instances.assert_called_once_with(
|
mock_pr.return_value.show_instances.assert_called_once_with(
|
||||||
['uuid1', 'hostname2'])
|
['uuid1', 'hostname2'])
|
||||||
|
|
||||||
|
def test_list(self, mock_os_conf, mock_pr):
|
||||||
|
mock_pr.return_value.list_instances.return_value = self.instances
|
||||||
|
args = ['list']
|
||||||
|
_cmd.main(args)
|
||||||
|
|
||||||
|
self.mock_print.assert_has_calls([
|
||||||
|
mock.call(mock.ANY, node='name-1 (UUID 1)', state='active'),
|
||||||
|
mock.call(mock.ANY, ips='private=1.2.3.4'),
|
||||||
|
mock.call(mock.ANY, node='name-2 (UUID 2)', state='deploying'),
|
||||||
|
])
|
||||||
|
mock_pr.return_value.list_instances.assert_called_once_with()
|
||||||
|
|
||||||
def test_wait(self, mock_os_conf, mock_pr):
|
def test_wait(self, mock_os_conf, mock_pr):
|
||||||
mock_pr.return_value.wait_for_provisioning.return_value = (
|
mock_pr.return_value.wait_for_provisioning.return_value = (
|
||||||
self.instances)
|
self.instances)
|
||||||
|
@ -790,6 +802,18 @@ class TestShowWait(testtools.TestCase):
|
||||||
{'hostname1': {'1': 'name-1'},
|
{'hostname1': {'1': 'name-1'},
|
||||||
'hostname2': {'2': 'name-2'}})
|
'hostname2': {'2': 'name-2'}})
|
||||||
|
|
||||||
|
def test_list_json(self, mock_os_conf, mock_pr):
|
||||||
|
mock_pr.return_value.list_instances.return_value = self.instances
|
||||||
|
args = ['--format', 'json', 'list']
|
||||||
|
|
||||||
|
fake_io = six.StringIO()
|
||||||
|
with mock.patch('sys.stdout', fake_io):
|
||||||
|
_cmd.main(args)
|
||||||
|
self.assertEqual(json.loads(fake_io.getvalue()),
|
||||||
|
{'hostname1': {'1': 'name-1'},
|
||||||
|
'hostname2': {'2': 'name-2'}})
|
||||||
|
mock_pr.return_value.list_instances.assert_called_once_with()
|
||||||
|
|
||||||
def test_wait_json(self, mock_os_conf, mock_pr):
|
def test_wait_json(self, mock_os_conf, mock_pr):
|
||||||
mock_pr.return_value.wait_for_provisioning.return_value = (
|
mock_pr.return_value.wait_for_provisioning.return_value = (
|
||||||
self.instances)
|
self.instances)
|
||||||
|
|
|
@ -903,3 +903,23 @@ class TestWaitForState(Base):
|
||||||
|
|
||||||
mock_sleep.assert_called_with(1)
|
mock_sleep.assert_called_with(1)
|
||||||
self.assertEqual(3, mock_sleep.call_count)
|
self.assertEqual(3, mock_sleep.call_count)
|
||||||
|
|
||||||
|
|
||||||
|
class TestListInstances(Base):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestListInstances, self).setUp()
|
||||||
|
self.nodes = [
|
||||||
|
mock.Mock(spec=_os_api.NODE_FIELDS, provision_state=state,
|
||||||
|
instance_info={'metalsmith_hostname': '1234'})
|
||||||
|
for state in ('active', 'active', 'deploying', 'wait call-back',
|
||||||
|
'deploy failed', 'available')
|
||||||
|
]
|
||||||
|
del self.nodes[-1].instance_info['metalsmith_hostname']
|
||||||
|
self.api.list_nodes.return_value = self.nodes
|
||||||
|
|
||||||
|
def test_list(self):
|
||||||
|
instances = self.pr.list_instances()
|
||||||
|
self.assertTrue(isinstance(i, _instance.Instance) for i in instances)
|
||||||
|
self.assertEqual(self.nodes[:5], [i.node for i in instances])
|
||||||
|
self.api.list_nodes.assert_called_once_with(provision_state=None,
|
||||||
|
associated=True)
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
- "{{ ssh_key_file }}"
|
- "{{ ssh_key_file }}"
|
||||||
user_name: "{{ configure_instance_user | default('') }}"
|
user_name: "{{ configure_instance_user | default('') }}"
|
||||||
|
|
||||||
- name: Get instance info via CLI
|
- name: Get instance info via CLI show
|
||||||
command: metalsmith --format=json show test
|
command: metalsmith --format=json show test
|
||||||
register: instance_info
|
register: instance_info
|
||||||
|
|
||||||
|
@ -38,6 +38,15 @@
|
||||||
instance: "{{ (instance_info.stdout | from_json).test }}"
|
instance: "{{ (instance_info.stdout | from_json).test }}"
|
||||||
failed_when: instance.state != 'active' or instance.node.provision_state != 'active'
|
failed_when: instance.state != 'active' or instance.node.provision_state != 'active'
|
||||||
|
|
||||||
|
- name: Get instance info via CLI list
|
||||||
|
command: metalsmith --format=json list
|
||||||
|
register: instance_info_via_list
|
||||||
|
|
||||||
|
- name: Verify that instance info via list is also correct
|
||||||
|
set_fact:
|
||||||
|
instance_via_list: "{{ (instance_info_via_list.stdout | from_json).test }}"
|
||||||
|
failed_when: instance_via_list.state != 'active' or instance_via_list.node.provision_state != 'active'
|
||||||
|
|
||||||
- name: Show active node information
|
- name: Show active node information
|
||||||
command: openstack baremetal node show {{ instance.node.uuid }}
|
command: openstack baremetal node show {{ instance.node.uuid }}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue