From 7bde737ab80fff344453e9ad71ee382b82723ee4 Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Mon, 4 Sep 2017 16:38:16 +0200 Subject: [PATCH] Report node availability from "overcloud profiles list" Closes-Bug: #1714965 Change-Id: Ic40b40050e87a450f88963ac88a1e06f323d2396 --- ...oud-profiles-compute-0e12922b3db70285.yaml | 13 +++++ .../tests/v1/test_overcloud_profiles.py | 48 ++++++++++++++-- tripleoclient/v1/overcloud_profiles.py | 57 ++++++++++++++++--- 3 files changed, 104 insertions(+), 14 deletions(-) create mode 100644 releasenotes/notes/overcloud-profiles-compute-0e12922b3db70285.yaml diff --git a/releasenotes/notes/overcloud-profiles-compute-0e12922b3db70285.yaml b/releasenotes/notes/overcloud-profiles-compute-0e12922b3db70285.yaml new file mode 100644 index 000000000..583af001c --- /dev/null +++ b/releasenotes/notes/overcloud-profiles-compute-0e12922b3db70285.yaml @@ -0,0 +1,13 @@ +--- +features: + - | + Add ``--all`` argument to the ``overcloud profiles list`` command to + also display nodes that cannot be deployed on. A new ``Error`` column + is displayed when this argument is provided. +fixes: + - | + Exclude from the output of ``overcloud profiles list`` nodes that: + + * have error power state + * do not have a matching hypervisor request + * have their compute service down. diff --git a/tripleoclient/tests/v1/test_overcloud_profiles.py b/tripleoclient/tests/v1/test_overcloud_profiles.py index da954d3f5..0949630ed 100644 --- a/tripleoclient/tests/v1/test_overcloud_profiles.py +++ b/tripleoclient/tests/v1/test_overcloud_profiles.py @@ -102,23 +102,42 @@ class TestListProfiles(test_plugin.TestPluginV1): self.cmd = overcloud_profiles.ListProfiles(self.app, None) self.app.client_manager.tripleoclient = mock.Mock() self.app.client_manager.baremetal = mock.Mock() + self.app.client_manager.compute = mock.Mock() self.nodes = [ mock.Mock(uuid='uuid1', provision_state='active', - properties={}), + properties={}, maintenance=False), mock.Mock(uuid='uuid2', provision_state='enroll', - properties={'capabilities': 'profile:compute'}), + properties={'capabilities': 'profile:compute'}, + maintenance=False), mock.Mock(uuid='uuid3', provision_state='available', properties={'capabilities': 'profile:compute,' - 'compute_profile:1,control_profile:true'}), + 'compute_profile:1,control_profile:true'}, + maintenance=False), mock.Mock(uuid='uuid4', provision_state='available', properties={'capabilities': 'profile:compute,' - 'compute_profile:0'}), + 'compute_profile:0'}, maintenance=False), + mock.Mock(uuid='uuid5', provision_state='available', + properties={}, maintenance=False), + mock.Mock(uuid='uuid6', provision_state='available', + properties={}, maintenance=False), + mock.Mock(uuid='uuid7', provision_state='active', + properties={}, maintenance=True), ] + self.hypervisors = [ + mock.Mock(hypervisor_type='ironic', + hypervisor_hostname='uuid%d' % i, + status='enabled', state='up') + for i in range(1, 6) + ] + self.hypervisors[-1].status = 'disabled' self.bm_client = self.app.client_manager.baremetal self.bm_client.node.list.return_value = self.nodes + self.compute_client = self.app.client_manager.compute + self.compute_client.hypervisors.list.return_value = self.hypervisors def test_list(self): - result = self.cmd.take_action(None) + parsed_args = self.check_parser(self.cmd, [], []) + result = self.cmd.take_action(parsed_args) self.assertEqual(5, len(result[0])) self.assertEqual( [('uuid1', self.nodes[0].name, 'active', None, ''), @@ -126,3 +145,22 @@ class TestListProfiles(test_plugin.TestPluginV1): 'compute, control'), ('uuid4', self.nodes[3].name, 'available', 'compute', '')], result[1]) + + def test_all(self): + parsed_args = self.check_parser(self.cmd, ['--all'], [('all', True)]) + result = self.cmd.take_action(parsed_args) + self.assertEqual(6, len(result[0])) + self.assertEqual( + [('uuid1', self.nodes[0].name, 'active', None, '', ''), + ('uuid2', self.nodes[1].name, 'enroll', 'compute', '', + 'Provision state enroll'), + ('uuid3', self.nodes[2].name, 'available', 'compute', + 'compute, control', ''), + ('uuid4', self.nodes[3].name, 'available', 'compute', '', ''), + ('uuid5', self.nodes[4].name, 'available', None, '', + 'Compute service disabled'), + ('uuid6', self.nodes[5].name, 'available', None, '', + 'No hypervisor record'), + ('uuid7', self.nodes[6].name, 'active', None, '', + 'Maintenance')], + result[1]) diff --git a/tripleoclient/v1/overcloud_profiles.py b/tripleoclient/v1/overcloud_profiles.py index e62ab53d2..babe83c33 100644 --- a/tripleoclient/v1/overcloud_profiles.py +++ b/tripleoclient/v1/overcloud_profiles.py @@ -90,14 +90,49 @@ class ListProfiles(command.Lister): log = logging.getLogger(__name__ + ".ListProfiles") + def get_parser(self, prog_name): + parser = super(ListProfiles, self).get_parser(prog_name) + parser.add_argument( + '--all', + action='store_true', + default=False, + help=_('List all nodes, even those not available to Nova.') + ) + utils.add_deployment_plan_arguments(parser) + return parser + def take_action(self, parsed_args): self.log.debug("take_action(%s)" % parsed_args) - client = self.app.client_manager.baremetal + bm_client = self.app.client_manager.baremetal + compute_client = self.app.client_manager.compute + hypervisors = {h.hypervisor_hostname: h + for h in compute_client.hypervisors.list() + if h.hypervisor_type == 'ironic'} result = [] - for node in client.node.list(detail=True, maintenance=False): + maintenance = None if parsed_args.all else False + for node in bm_client.node.list(detail=True, maintenance=maintenance): + error = '' + if node.provision_state not in ('active', 'available'): + error = "Provision state %s" % node.provision_state + elif node.power_state in (None, 'error'): + error = "Power state %s" % node.power_state + elif node.maintenance: + error = "Maintenance" + else: + try: + hypervisor = hypervisors[node.uuid] + except KeyError: + error = 'No hypervisor record' + else: + if hypervisor.status != 'enabled': + error = 'Compute service disabled' + elif hypervisor.state != 'up': + error = 'Compute service down' + + if error and not parsed_args.all: continue caps = utils.node_get_capabilities(node) @@ -108,11 +143,15 @@ class ListProfiles(command.Lister): v.lower() in ('1', 'true')] # sorting for convenient display and testing possible_profiles.sort() - result.append((node.uuid, node.name or '', node.provision_state, - profile, ', '.join(possible_profiles))) - return ( - ("Node UUID", "Node Name", "Provision State", "Current Profile", - "Possible Profiles"), - result - ) + record = (node.uuid, node.name or '', node.provision_state, + profile, ', '.join(possible_profiles)) + if parsed_args.all: + record += (error,) + result.append(record) + + cols = ("Node UUID", "Node Name", "Provision State", "Current Profile", + "Possible Profiles") + if parsed_args.all: + cols += ('Error',) + return (cols, result)