Merge "Improvements to unprovision command" into stable/train

This commit is contained in:
Zuul 2020-01-29 03:12:42 +00:00 committed by Gerrit Code Review
commit d0ca1e0e30
3 changed files with 164 additions and 7 deletions

View File

@ -1104,6 +1104,7 @@ class TestProvisionNode(fakes.TestOvercloudNode):
'tripleo.baremetal_deploy.v1.deploy_roles', 'tripleo.baremetal_deploy.v1.deploy_roles',
workflow_input={'roles': [{'name': 'Compute'}, workflow_input={'roles': [{'name': 'Compute'},
{'name': 'Controller'}], {'name': 'Controller'}],
'plan': 'overcloud',
'ssh_keys': ['I am a key'], 'ssh_keys': ['I am a key'],
'ssh_user_name': 'heat-admin'} 'ssh_user_name': 'heat-admin'}
) )
@ -1130,11 +1131,22 @@ class TestUnprovisionNode(fakes.TestOvercloudNode):
self.cmd = overcloud_node.UnprovisionNode(self.app, None) self.cmd = overcloud_node.UnprovisionNode(self.app, None)
def test_ok(self): def test_ok(self):
rv = mock.Mock()
rv.output = json.dumps({
'result': {
'instances': [
{'hostname': 'compute-0', 'name': 'baremetal-1'},
{'hostname': 'controller-0', 'name': 'baremetal-2'}
]
}
})
self.workflow.action_executions.create.return_value = rv
with tempfile.NamedTemporaryFile() as inp: with tempfile.NamedTemporaryFile() as inp:
inp.write(b'- name: Compute\n- name: Controller\n') inp.write(b'- name: Compute\n- name: Controller\n')
inp.flush() inp.flush()
argslist = [inp.name] argslist = ['--yes', inp.name]
verifylist = [('input', inp.name)] verifylist = [('input', inp.name), ('yes', True)]
parsed_args = self.check_parser(self.cmd, parsed_args = self.check_parser(self.cmd,
argslist, verifylist) argslist, verifylist)
@ -1142,6 +1154,66 @@ class TestUnprovisionNode(fakes.TestOvercloudNode):
self.workflow.executions.create.assert_called_once_with( self.workflow.executions.create.assert_called_once_with(
'tripleo.baremetal_deploy.v1.undeploy_roles', 'tripleo.baremetal_deploy.v1.undeploy_roles',
workflow_input={'roles': [{'name': 'Compute'}, workflow_input={
{'name': 'Controller'}]} 'plan': 'overcloud',
'roles': [{
'name': 'Unprovisioned',
'count': 0,
'instances': [
{'hostname': u'compute-0', 'provisioned': False},
{'hostname': u'controller-0', 'provisioned': False}
]
}]
}
)
def test_ok_all(self):
rv = mock.Mock()
rv.output = json.dumps({
'result': {
'instances': [
{'hostname': 'compute-0', 'name': 'baremetal-1'},
{'hostname': 'controller-0', 'name': 'baremetal-2'}
]
}
})
rv_provisioned = mock.Mock()
rv_provisioned.output = json.dumps({
'result': {
'instances': [
{'hostname': 'compute-1', 'name': 'baremetal-3'},
{'hostname': 'controller-1', 'name': 'baremetal-4'}
]
}
})
self.workflow.action_executions.create.side_effect = [
rv, rv_provisioned
]
with tempfile.NamedTemporaryFile() as inp:
inp.write(b'- name: Compute\n- name: Controller\n')
inp.flush()
argslist = ['--all', '--yes', inp.name]
verifylist = [('input', inp.name), ('yes', True), ('all', True)]
parsed_args = self.check_parser(self.cmd,
argslist, verifylist)
self.cmd.take_action(parsed_args)
self.workflow.executions.create.assert_called_once_with(
'tripleo.baremetal_deploy.v1.undeploy_roles',
workflow_input={
'plan': 'overcloud',
'roles': [{
'name': 'Unprovisioned',
'count': 0,
'instances': [
{'hostname': u'compute-0', 'provisioned': False},
{'hostname': u'controller-0', 'provisioned': False},
{'hostname': u'compute-1', 'provisioned': False},
{'hostname': u'controller-1', 'provisioned': False}
]
}]
}
) )

View File

@ -14,9 +14,12 @@
# #
import argparse import argparse
import collections
import logging import logging
import os import os
import sys
from cliff.formatters import table
from osc_lib import exceptions as oscexc from osc_lib import exceptions as oscexc
from osc_lib.i18n import _ from osc_lib.i18n import _
from osc_lib import utils from osc_lib import utils
@ -530,6 +533,7 @@ class ProvisionNode(command.Command):
output = baremetal.deploy_roles( output = baremetal.deploy_roles(
self.app.client_manager, self.app.client_manager,
plan=parsed_args.stack,
roles=roles, ssh_keys=[ssh_key], roles=roles, ssh_keys=[ssh_key],
ssh_user_name=parsed_args.overcloud_ssh_user) ssh_user_name=parsed_args.overcloud_ssh_user)
@ -547,6 +551,20 @@ class UnprovisionNode(command.Command):
def get_parser(self, prog_name): def get_parser(self, prog_name):
parser = super(UnprovisionNode, self).get_parser(prog_name) parser = super(UnprovisionNode, self).get_parser(prog_name)
parser.add_argument('--stack', dest='stack',
help=_('Name or ID of heat stack '
'(default=Env: OVERCLOUD_STACK_NAME)'),
default=utils.env('OVERCLOUD_STACK_NAME',
default='overcloud'))
parser.add_argument('--all',
help=_('Unprovision every instance in the '
'deployment'),
default=False,
action="store_true")
parser.add_argument('-y', '--yes',
help=_('Skip yes/no prompt (assume yes)'),
default=False,
action="store_true")
parser.add_argument('input', parser.add_argument('input',
metavar='<baremetal_deployment.yaml>', metavar='<baremetal_deployment.yaml>',
help=_('Configuration file describing the ' help=_('Configuration file describing the '
@ -559,10 +577,66 @@ class UnprovisionNode(command.Command):
with open(parsed_args.input, 'r') as fp: with open(parsed_args.input, 'r') as fp:
roles = yaml.safe_load(fp) roles = yaml.safe_load(fp)
# TODO(sbaker) call ExpandRolesAction to get a list of nodes = []
# instances being unprovisioned to prompt for confirmation expanded = baremetal.expand_roles(
self.app.client_manager,
roles=roles,
stackname=parsed_args.stack,
provisioned=False)
nodes.extend(expanded.get('instances', []))
if parsed_args.all:
expanded = baremetal.expand_roles(
self.app.client_manager,
roles=roles,
stackname=parsed_args.stack,
provisioned=True)
nodes.extend(expanded.get('instances', []))
if not nodes:
print('No nodes to unprovision')
return
self._print_nodes(nodes)
if not parsed_args.yes:
confirm = oooutils.prompt_user_for_confirmation(
message=_("Are you sure you want to unprovision these %s "
"nodes [y/N]? ") % parsed_args.stack,
logger=self.log)
if not confirm:
raise oscexc.CommandError("Action not confirmed, exiting.")
unprovision_role = self._build_unprovision_role(nodes)
print('Unprovisioning %d nodes' % len(nodes))
baremetal.undeploy_roles( baremetal.undeploy_roles(
self.app.client_manager, self.app.client_manager,
roles=roles) roles=unprovision_role,
plan=parsed_args.stack)
print('Unprovision complete') print('Unprovision complete')
def _build_unprovision_role(self, nodes):
# build a fake role called Unprovisioned which has an instance
# entry for every node to be unprovisioned
instances = [{'hostname': n['hostname'], 'provisioned': False}
for n in nodes if 'hostname' in n]
return [{
'name': 'Unprovisioned',
'count': 0,
'instances': instances
}]
def _print_nodes(self, nodes):
TableArgs = collections.namedtuple(
'TableArgs', 'print_empty max_width fit_width')
args = TableArgs(print_empty=True, max_width=80, fit_width=True)
nodes_data = [(i.get('hostname', ''),
i.get('name', '')) for i in nodes]
formatter = table.TableFormatter()
formatter.emit_list(
column_names=['hostname', 'name'],
data=nodes_data,
stdout=sys.stdout,
parsed_args=args
)

View File

@ -568,3 +568,14 @@ def undeploy_roles(clients, **workflow_input):
'Error undeploying nodes: {}'.format(payload['message'])) 'Error undeploying nodes: {}'.format(payload['message']))
return payload return payload
def expand_roles(clients, roles, stackname, provisioned):
workflow_client = clients.workflow_engine
return base.call_action(
workflow_client,
'tripleo.baremetal_deploy.expand_roles',
roles=roles,
stackname=stackname,
provisioned=provisioned
)