overcloud node delete, use ansible for unprovision
Change-Id: I1340345702a506fe195429b6e4900eb9ce95e827 Story: 2007212 Task: 38457
This commit is contained in:
parent
ed3cee8641
commit
bd0588e3ef
@ -41,6 +41,7 @@ class TestDeleteNode(fakes.TestDeleteNode):
|
|||||||
|
|
||||||
# Get the command object to test
|
# Get the command object to test
|
||||||
self.cmd = overcloud_node.DeleteNode(self.app, None)
|
self.cmd = overcloud_node.DeleteNode(self.app, None)
|
||||||
|
self.cmd.app_args = mock.Mock(verbose_level=1)
|
||||||
self.app.client_manager.workflow_engine = mock.Mock()
|
self.app.client_manager.workflow_engine = mock.Mock()
|
||||||
self.tripleoclient = mock.Mock()
|
self.tripleoclient = mock.Mock()
|
||||||
|
|
||||||
@ -134,13 +135,11 @@ class TestDeleteNode(fakes.TestDeleteNode):
|
|||||||
|
|
||||||
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
||||||
autospec=True)
|
autospec=True)
|
||||||
@mock.patch('tripleoclient.workflows.baremetal.expand_roles',
|
@mock.patch('tripleoclient.utils.tempfile')
|
||||||
autospec=True)
|
def test_node_delete_baremetal_deployment(self,
|
||||||
@mock.patch('tripleoclient.workflows.baremetal.undeploy_roles',
|
mock_tempfile,
|
||||||
autospec=True)
|
|
||||||
def test_node_delete_baremetal_deployment(self, mock_undeploy_roles,
|
|
||||||
mock_expand_roles,
|
|
||||||
mock_playbook):
|
mock_playbook):
|
||||||
|
|
||||||
bm_yaml = [{
|
bm_yaml = [{
|
||||||
'name': 'Compute',
|
'name': 'Compute',
|
||||||
'count': 5,
|
'count': 5,
|
||||||
@ -159,60 +158,27 @@ class TestDeleteNode(fakes.TestDeleteNode):
|
|||||||
}]
|
}]
|
||||||
}]
|
}]
|
||||||
|
|
||||||
expand_to_delete = {
|
tmp = tempfile.mkdtemp()
|
||||||
'instances': [{
|
mock_tempfile.mkdtemp.side_effect = [
|
||||||
'name': 'baremetal-1',
|
tmp,
|
||||||
'hostname': 'overcast-controller-1'
|
tempfile.mkdtemp(),
|
||||||
}, {
|
tempfile.mkdtemp(),
|
||||||
'name': 'baremetal-2',
|
tempfile.mkdtemp()
|
||||||
'hostname': 'overcast-compute-0'
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
expand_to_translate = {
|
|
||||||
'environment': {
|
|
||||||
'parameter_defaults': {
|
|
||||||
'ComputeRemovalPolicies': [{
|
|
||||||
'resource_list': [0]
|
|
||||||
}],
|
|
||||||
'ControllerRemovalPolicies': [{
|
|
||||||
'resource_list': [1]
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mock_expand_roles.side_effect = [
|
|
||||||
expand_to_delete,
|
|
||||||
expand_to_translate
|
|
||||||
]
|
]
|
||||||
|
|
||||||
res_list = self.app.client_manager.orchestration.resources.list
|
unprovision_confirm = os.path.join(tmp, 'unprovision_confirm.json')
|
||||||
res_list.return_value = [
|
with open(unprovision_confirm, 'w') as confirm:
|
||||||
mock.Mock(
|
confirm.write(json.dumps([
|
||||||
resource_type='OS::TripleO::ComputeServer',
|
{
|
||||||
parent_resource='0',
|
'hostname': 'overcast-controller-1',
|
||||||
physical_resource_id='aaaa'
|
'name': 'baremetal-1',
|
||||||
),
|
'id': 'aaaa'
|
||||||
mock.Mock(
|
}, {
|
||||||
resource_type='OS::TripleO::ComputeServer',
|
'hostname': 'overcast-compute-0',
|
||||||
parent_resource='1',
|
'name': 'baremetal-2',
|
||||||
physical_resource_id='bbbb'
|
'id': 'bbbb'
|
||||||
),
|
}
|
||||||
mock.Mock(
|
]))
|
||||||
resource_type='OS::TripleO::ControllerServer',
|
|
||||||
parent_resource='0',
|
|
||||||
physical_resource_id='cccc'
|
|
||||||
),
|
|
||||||
mock.Mock(
|
|
||||||
resource_type='OS::TripleO::ControllerServer',
|
|
||||||
parent_resource='1',
|
|
||||||
physical_resource_id='dddd'
|
|
||||||
),
|
|
||||||
mock.Mock(
|
|
||||||
resource_type='OS::TripleO::ControllerServer',
|
|
||||||
parent_resource='2',
|
|
||||||
physical_resource_id='eeee'
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
with tempfile.NamedTemporaryFile(mode='w') as inp:
|
with tempfile.NamedTemporaryFile(mode='w') as inp:
|
||||||
yaml.dump(bm_yaml, inp, encoding='utf-8')
|
yaml.dump(bm_yaml, inp, encoding='utf-8')
|
||||||
@ -229,71 +195,94 @@ class TestDeleteNode(fakes.TestDeleteNode):
|
|||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
# Verify
|
# Verify
|
||||||
res_list.assert_called_once_with('overcast', nested_depth=5)
|
mock_playbook.assert_has_calls([
|
||||||
mock_expand_roles.assert_has_calls([
|
|
||||||
mock.call(
|
mock.call(
|
||||||
self.app.client_manager,
|
playbook='cli-overcloud-node-unprovision.yaml',
|
||||||
provisioned=False,
|
inventory='localhost,',
|
||||||
roles=bm_yaml,
|
verbosity=0,
|
||||||
stackname='overcast'
|
workdir=mock.ANY,
|
||||||
|
playbook_dir='/usr/share/ansible/tripleo-playbooks',
|
||||||
|
extra_vars={
|
||||||
|
'stack_name': 'overcast',
|
||||||
|
'baremetal_deployment': [{
|
||||||
|
'count': 5,
|
||||||
|
'instances': [{
|
||||||
|
'hostname': 'overcast-compute-0',
|
||||||
|
'name': 'baremetal-2',
|
||||||
|
'provisioned': False
|
||||||
|
}],
|
||||||
|
'name': 'Compute'
|
||||||
|
}, {
|
||||||
|
'count': 2,
|
||||||
|
'instances': [{
|
||||||
|
'hostname': 'overcast-controller-1',
|
||||||
|
'name': 'baremetal-1',
|
||||||
|
'provisioned': False
|
||||||
|
}], 'name': 'Controller'
|
||||||
|
}],
|
||||||
|
'prompt': True,
|
||||||
|
'unprovision_confirm': unprovision_confirm,
|
||||||
|
},
|
||||||
),
|
),
|
||||||
mock.call(
|
mock.call(
|
||||||
self.app.client_manager,
|
playbook='cli-grant-local-access.yaml',
|
||||||
provisioned=True,
|
inventory='localhost,',
|
||||||
roles=bm_yaml,
|
workdir=mock.ANY,
|
||||||
stackname='overcast'
|
playbook_dir='/usr/share/ansible/tripleo-playbooks',
|
||||||
|
extra_vars={
|
||||||
|
'access_path': '/var/lib/mistral',
|
||||||
|
'execution_user': mock.ANY},
|
||||||
|
),
|
||||||
|
mock.call(
|
||||||
|
playbook=mock.ANY,
|
||||||
|
inventory=mock.ANY,
|
||||||
|
workdir=mock.ANY,
|
||||||
|
playbook_dir=mock.ANY,
|
||||||
|
skip_tags='opendev-validation',
|
||||||
|
ansible_cfg=None,
|
||||||
|
verbosity=1,
|
||||||
|
ssh_user='tripleo-admin',
|
||||||
|
key=mock.ANY,
|
||||||
|
limit_hosts='overcast-controller-1:overcast-compute-0',
|
||||||
|
ansible_timeout=90,
|
||||||
|
reproduce_command=True,
|
||||||
|
extra_env_variables={'ANSIBLE_BECOME': True},
|
||||||
|
extra_vars=None,
|
||||||
|
tags=None
|
||||||
|
),
|
||||||
|
mock.call(
|
||||||
|
inventory='localhost,',
|
||||||
|
playbook='cli-overcloud-node-unprovision.yaml',
|
||||||
|
verbosity=0,
|
||||||
|
workdir=mock.ANY,
|
||||||
|
playbook_dir='/usr/share/ansible/tripleo-playbooks',
|
||||||
|
extra_vars={
|
||||||
|
'stack_name': 'overcast',
|
||||||
|
'baremetal_deployment': [{
|
||||||
|
'count': 5,
|
||||||
|
'instances': [{
|
||||||
|
'hostname': 'overcast-compute-0',
|
||||||
|
'name': 'baremetal-2',
|
||||||
|
'provisioned': False
|
||||||
|
}], 'name': 'Compute'
|
||||||
|
}, {
|
||||||
|
'count': 2,
|
||||||
|
'instances': [{
|
||||||
|
'hostname': 'overcast-controller-1',
|
||||||
|
'name': 'baremetal-1',
|
||||||
|
'provisioned': False
|
||||||
|
}],
|
||||||
|
'name': 'Controller'
|
||||||
|
}],
|
||||||
|
'prompt': False
|
||||||
|
},
|
||||||
)
|
)
|
||||||
])
|
])
|
||||||
|
|
||||||
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
@mock.patch('tripleoclient.utils.run_ansible_playbook',
|
||||||
autospec=True)
|
autospec=True)
|
||||||
@mock.patch('tripleoclient.workflows.baremetal.expand_roles',
|
@mock.patch('tripleoclient.utils.tempfile')
|
||||||
autospec=True)
|
def test_nodes_to_delete(self, mock_tempfile, mock_playbook):
|
||||||
def test_nodes_to_delete(self, mock_expand_roles, mock_playbook):
|
|
||||||
bm_yaml = [{
|
|
||||||
'name': 'Compute',
|
|
||||||
'count': 5,
|
|
||||||
'instances': [{
|
|
||||||
'name': 'baremetal-2',
|
|
||||||
'hostname': 'overcast-compute-0',
|
|
||||||
'provisioned': False
|
|
||||||
}],
|
|
||||||
}, {
|
|
||||||
'name': 'Controller',
|
|
||||||
'count': 2,
|
|
||||||
'instances': [{
|
|
||||||
'name': 'baremetal-1',
|
|
||||||
'hostname': 'overcast-controller-1',
|
|
||||||
'provisioned': False
|
|
||||||
}]
|
|
||||||
}]
|
|
||||||
mock_expand_roles.return_value = {
|
|
||||||
'instances': [{
|
|
||||||
'name': 'baremetal-1',
|
|
||||||
'hostname': 'overcast-controller-1'
|
|
||||||
}, {
|
|
||||||
'name': 'baremetal-2',
|
|
||||||
'hostname': 'overcast-compute-0'
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
argslist = ['--baremetal-deployment', '/foo/bm_deploy.yaml']
|
|
||||||
verifylist = [
|
|
||||||
('baremetal_deployment', '/foo/bm_deploy.yaml')
|
|
||||||
]
|
|
||||||
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
|
|
||||||
result = self.cmd._nodes_to_delete(parsed_args, bm_yaml)
|
|
||||||
expected = '''+-----------------------+-------------+
|
|
||||||
| hostname | name |
|
|
||||||
+-----------------------+-------------+
|
|
||||||
| overcast-controller-1 | baremetal-1 |
|
|
||||||
| overcast-compute-0 | baremetal-2 |
|
|
||||||
+-----------------------+-------------+
|
|
||||||
'''
|
|
||||||
self.assertEqual(expected, result)
|
|
||||||
|
|
||||||
@mock.patch('tripleoclient.workflows.baremetal.expand_roles',
|
|
||||||
autospec=True)
|
|
||||||
def test_translate_nodes_to_resources(self, mock_expand_roles):
|
|
||||||
bm_yaml = [{
|
bm_yaml = [{
|
||||||
'name': 'Compute',
|
'name': 'Compute',
|
||||||
'count': 5,
|
'count': 5,
|
||||||
@ -312,56 +301,38 @@ class TestDeleteNode(fakes.TestDeleteNode):
|
|||||||
}]
|
}]
|
||||||
}]
|
}]
|
||||||
|
|
||||||
res_list = self.app.client_manager.orchestration.resources.list
|
tmp = tempfile.mkdtemp()
|
||||||
res_list.return_value = [
|
mock_tempfile.mkdtemp.return_value = tmp
|
||||||
mock.Mock(
|
|
||||||
resource_type='OS::TripleO::ComputeServer',
|
|
||||||
parent_resource='0',
|
|
||||||
physical_resource_id='aaaa'
|
|
||||||
),
|
|
||||||
mock.Mock(
|
|
||||||
resource_type='OS::TripleO::ComputeServer',
|
|
||||||
parent_resource='1',
|
|
||||||
physical_resource_id='bbbb'
|
|
||||||
),
|
|
||||||
mock.Mock(
|
|
||||||
resource_type='OS::TripleO::ControllerServer',
|
|
||||||
parent_resource='0',
|
|
||||||
physical_resource_id='cccc'
|
|
||||||
),
|
|
||||||
mock.Mock(
|
|
||||||
resource_type='OS::TripleO::ControllerServer',
|
|
||||||
parent_resource='1',
|
|
||||||
physical_resource_id='dddd'
|
|
||||||
),
|
|
||||||
mock.Mock(
|
|
||||||
resource_type='OS::TripleO::ControllerServer',
|
|
||||||
parent_resource='2',
|
|
||||||
physical_resource_id='eeee'
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
mock_expand_roles.return_value = {
|
unprovision_confirm = os.path.join(tmp, 'unprovision_confirm.json')
|
||||||
'environment': {
|
with open(unprovision_confirm, 'w') as confirm:
|
||||||
'parameter_defaults': {
|
confirm.write(json.dumps([
|
||||||
'ComputeRemovalPolicies': [{
|
{
|
||||||
'resource_list': [0]
|
'hostname': 'compute-0',
|
||||||
}],
|
'name': 'baremetal-1',
|
||||||
'ControllerRemovalPolicies': [{
|
'id': 'aaaa'
|
||||||
'resource_list': [1]
|
}, {
|
||||||
}]
|
'hostname': 'controller-0',
|
||||||
|
'name': 'baremetal-2',
|
||||||
|
'id': 'bbbb'
|
||||||
}
|
}
|
||||||
}
|
]))
|
||||||
}
|
|
||||||
|
|
||||||
argslist = ['--baremetal-deployment', '/foo/bm_deploy.yaml']
|
argslist = ['--baremetal-deployment', '/foo/bm_deploy.yaml']
|
||||||
verifylist = [
|
verifylist = [
|
||||||
('baremetal_deployment', '/foo/bm_deploy.yaml')
|
('baremetal_deployment', '/foo/bm_deploy.yaml')
|
||||||
]
|
]
|
||||||
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
|
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
|
||||||
result = self.cmd._translate_nodes_to_resources(
|
nodes_text, nodes = self.cmd._nodes_to_delete(parsed_args, bm_yaml)
|
||||||
parsed_args, bm_yaml)
|
expected = '''+--------------+-------------+------+
|
||||||
self.assertEqual(['aaaa', 'dddd'], result)
|
| hostname | name | id |
|
||||||
|
+--------------+-------------+------+
|
||||||
|
| compute-0 | baremetal-1 | aaaa |
|
||||||
|
| controller-0 | baremetal-2 | bbbb |
|
||||||
|
+--------------+-------------+------+
|
||||||
|
'''
|
||||||
|
self.assertEqual(expected, nodes_text)
|
||||||
|
self.assertEqual(['compute-0', 'controller-0'], nodes)
|
||||||
|
|
||||||
|
|
||||||
class TestProvideNode(fakes.TestOvercloudNode):
|
class TestProvideNode(fakes.TestOvercloudNode):
|
||||||
|
@ -14,7 +14,9 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from cliff.formatters import table
|
from cliff.formatters import table
|
||||||
@ -87,76 +89,46 @@ class DeleteNode(command.Command):
|
|||||||
return parser
|
return parser
|
||||||
|
|
||||||
def _nodes_to_delete(self, parsed_args, roles):
|
def _nodes_to_delete(self, parsed_args, roles):
|
||||||
# expand for provisioned:False to get a list of nodes
|
with oooutils.TempDirs() as tmp:
|
||||||
# to delete
|
unprovision_confirm = os.path.join(
|
||||||
expanded = baremetal.expand_roles(
|
tmp, 'unprovision_confirm.json')
|
||||||
self.app.client_manager,
|
|
||||||
roles=roles,
|
oooutils.run_ansible_playbook(
|
||||||
stackname=parsed_args.stack,
|
playbook='cli-overcloud-node-unprovision.yaml',
|
||||||
provisioned=False)
|
inventory='localhost,',
|
||||||
nodes = expanded.get('instances', [])
|
verbosity=self.app_args.verbose_level - 1,
|
||||||
|
workdir=tmp,
|
||||||
|
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
|
||||||
|
extra_vars={
|
||||||
|
"stack_name": parsed_args.stack,
|
||||||
|
"baremetal_deployment": roles,
|
||||||
|
"prompt": True,
|
||||||
|
"unprovision_confirm": unprovision_confirm,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
with open(unprovision_confirm) as f:
|
||||||
|
nodes = json.load(f)
|
||||||
if not nodes:
|
if not nodes:
|
||||||
print('No nodes to unprovision')
|
print('No nodes to unprovision')
|
||||||
return
|
return None, None
|
||||||
TableArgs = collections.namedtuple(
|
TableArgs = collections.namedtuple(
|
||||||
'TableArgs', 'print_empty max_width fit_width')
|
'TableArgs', 'print_empty max_width fit_width')
|
||||||
args = TableArgs(print_empty=True, max_width=80, fit_width=True)
|
args = TableArgs(print_empty=True, max_width=-1, fit_width=True)
|
||||||
nodes_data = [(i.get('hostname', ''),
|
nodes_data = [(i.get('hostname', ''),
|
||||||
i.get('name', '')) for i in nodes]
|
i.get('name', ''),
|
||||||
|
i.get('id', '')) for i in nodes]
|
||||||
|
|
||||||
|
node_hostnames = [i['hostname'] for i in nodes if 'hostname' in i]
|
||||||
|
|
||||||
formatter = table.TableFormatter()
|
formatter = table.TableFormatter()
|
||||||
output = six.StringIO()
|
output = six.StringIO()
|
||||||
formatter.emit_list(
|
formatter.emit_list(
|
||||||
column_names=['hostname', 'name'],
|
column_names=['hostname', 'name', 'id'],
|
||||||
data=nodes_data,
|
data=nodes_data,
|
||||||
stdout=output,
|
stdout=output,
|
||||||
parsed_args=args
|
parsed_args=args
|
||||||
)
|
)
|
||||||
return output.getvalue()
|
return output.getvalue(), node_hostnames
|
||||||
|
|
||||||
def _translate_nodes_to_resources(self, parsed_args, roles):
|
|
||||||
# build a dict of resource type names to role name
|
|
||||||
role_types = dict(
|
|
||||||
('OS::TripleO::%sServer' % r['name'], r['name'])
|
|
||||||
for r in roles
|
|
||||||
)
|
|
||||||
expanded = baremetal.expand_roles(
|
|
||||||
self.app.client_manager,
|
|
||||||
roles=roles,
|
|
||||||
stackname=parsed_args.stack,
|
|
||||||
provisioned=True)
|
|
||||||
|
|
||||||
parameters = expanded.get(
|
|
||||||
'environment', {}).get('parameter_defaults', {})
|
|
||||||
|
|
||||||
# build a dict with the role and
|
|
||||||
# a list of indexes of nodes to delete for that role
|
|
||||||
removal_indexes = {}
|
|
||||||
for role in role_types.values():
|
|
||||||
removal_indexes.setdefault(role, [])
|
|
||||||
param = '%sRemovalPolicies' % role
|
|
||||||
policies = parameters.get(param, [])
|
|
||||||
if policies:
|
|
||||||
removal_indexes[role] = policies[0].get('resource_list', [])
|
|
||||||
|
|
||||||
nodes = []
|
|
||||||
clients = self.app.client_manager
|
|
||||||
|
|
||||||
# iterate every server resource and compare its index with
|
|
||||||
# the list of indexes to be deleted
|
|
||||||
resources = clients.orchestration.resources.list(
|
|
||||||
parsed_args.stack, nested_depth=5)
|
|
||||||
for res in resources:
|
|
||||||
if res.resource_type not in role_types:
|
|
||||||
continue
|
|
||||||
role = role_types[res.resource_type]
|
|
||||||
removal_list = removal_indexes.get(role, [])
|
|
||||||
|
|
||||||
index = int(res.parent_resource)
|
|
||||||
if index in removal_list:
|
|
||||||
node = res.physical_resource_id
|
|
||||||
nodes.append(node)
|
|
||||||
return nodes
|
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
self.log.debug("take_action(%s)" % parsed_args)
|
self.log.debug("take_action(%s)" % parsed_args)
|
||||||
@ -166,10 +138,8 @@ class DeleteNode(command.Command):
|
|||||||
with open(parsed_args.baremetal_deployment, 'r') as fp:
|
with open(parsed_args.baremetal_deployment, 'r') as fp:
|
||||||
roles = yaml.safe_load(fp)
|
roles = yaml.safe_load(fp)
|
||||||
|
|
||||||
nodes_text = self._nodes_to_delete(parsed_args, roles)
|
nodes_text, nodes = self._nodes_to_delete(parsed_args, roles)
|
||||||
if nodes_text:
|
if nodes_text:
|
||||||
nodes = self._translate_nodes_to_resources(
|
|
||||||
parsed_args, roles)
|
|
||||||
print(nodes_text)
|
print(nodes_text)
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
@ -204,10 +174,19 @@ class DeleteNode(command.Command):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if parsed_args.baremetal_deployment:
|
if parsed_args.baremetal_deployment:
|
||||||
baremetal.undeploy_roles(
|
with oooutils.TempDirs() as tmp:
|
||||||
self.app.client_manager,
|
oooutils.run_ansible_playbook(
|
||||||
roles=roles,
|
playbook='cli-overcloud-node-unprovision.yaml',
|
||||||
plan=parsed_args.stack)
|
inventory='localhost,',
|
||||||
|
verbosity=self.app_args.verbose_level - 1,
|
||||||
|
workdir=tmp,
|
||||||
|
playbook_dir=constants.ANSIBLE_TRIPLEO_PLAYBOOKS,
|
||||||
|
extra_vars={
|
||||||
|
"stack_name": parsed_args.stack,
|
||||||
|
"baremetal_deployment": roles,
|
||||||
|
"prompt": False,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ProvideNode(command.Command):
|
class ProvideNode(command.Command):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user