Merge "New command "overcloud node extract provisioned""

This commit is contained in:
Zuul 2020-05-07 19:29:53 +00:00 committed by Gerrit Code Review
commit 5a8255b44c
3 changed files with 207 additions and 0 deletions

View File

@ -76,6 +76,7 @@ openstack.tripleoclient.v2 =
overcloud_node_bios_reset = tripleoclient.v1.overcloud_bios:ResetBIOS overcloud_node_bios_reset = tripleoclient.v1.overcloud_bios:ResetBIOS
overcloud_node_provision = tripleoclient.v2.overcloud_node:ProvisionNode overcloud_node_provision = tripleoclient.v2.overcloud_node:ProvisionNode
overcloud_node_unprovision = tripleoclient.v2.overcloud_node:UnprovisionNode overcloud_node_unprovision = tripleoclient.v2.overcloud_node:UnprovisionNode
overcloud_node_extract_provisioned = tripleoclient.v1.overcloud_node:ExtractProvisionedNode
overcloud_parameters_set = tripleoclient.v1.overcloud_parameters:SetParameters overcloud_parameters_set = tripleoclient.v1.overcloud_parameters:SetParameters
overcloud_plan_create = tripleoclient.v1.overcloud_plan:CreatePlan overcloud_plan_create = tripleoclient.v1.overcloud_plan:CreatePlan
overcloud_plan_delete = tripleoclient.v1.overcloud_plan:DeletePlan overcloud_plan_delete = tripleoclient.v1.overcloud_plan:DeletePlan

View File

@ -749,3 +749,123 @@ class TestDiscoverNode(fakes.TestOvercloudNode):
parsed_args = self.check_parser(self.cmd, argslist, verifylist) parsed_args = self.check_parser(self.cmd, argslist, verifylist)
self.cmd.take_action(parsed_args) self.cmd.take_action(parsed_args)
class TestExtractProvisionedNode(test_utils.TestCommand):
def setUp(self):
super(TestExtractProvisionedNode, self).setUp()
self.orchestration = mock.Mock()
self.app.client_manager.orchestration = self.orchestration
self.baremetal = mock.Mock()
self.app.client_manager.baremetal = self.baremetal
self.cmd = overcloud_node.ExtractProvisionedNode(self.app, None)
self.extract_file = tempfile.NamedTemporaryFile(
mode='w', delete=False, suffix='.yaml')
self.extract_file.close()
self.addCleanup(os.unlink, self.extract_file.name)
def test_extract(self):
stack_dict = {
'parameters': {
'ComputeHostnameFormat': '%stackname%-novacompute-%index%',
'ControllerHostnameFormat': '%stackname%-controller-%index%'
},
'outputs': [{
'output_key': 'AnsibleHostVarsMap',
'output_value': {
'Compute': [
'overcloud-novacompute-0'
],
'Controller': [
'overcloud-controller-0',
'overcloud-controller-1',
'overcloud-controller-2'
],
}
}]
}
stack = mock.Mock()
stack.to_dict.return_value = stack_dict
self.orchestration.stacks.get.return_value = stack
nodes = [
mock.Mock(),
mock.Mock(),
mock.Mock(),
mock.Mock()
]
nodes[0].name = 'bm-0'
nodes[1].name = 'bm-1'
nodes[2].name = 'bm-2'
nodes[3].name = 'bm-3'
nodes[0].instance_info = {'display_name': 'overcloud-controller-0'}
nodes[1].instance_info = {'display_name': 'overcloud-controller-1'}
nodes[2].instance_info = {'display_name': 'overcloud-controller-2'}
nodes[3].instance_info = {'display_name': 'overcloud-novacompute-0'}
self.baremetal.node.list.return_value = nodes
argslist = ['--output', self.extract_file.name, '--yes']
self.app.command_options = argslist
verifylist = [('output', self.extract_file.name), ('yes', True)]
parsed_args = self.check_parser(self.cmd,
argslist, verifylist)
self.cmd.take_action(parsed_args)
result = self.cmd.app.stdout.make_string()
self.assertEqual([{
'name': 'Compute',
'count': 1,
'hostname_format': '%stackname%-novacompute-%index%',
'instances': [{
'hostname': 'overcloud-novacompute-0',
'name': 'bm-3'
}],
}, {
'name': 'Controller',
'count': 3,
'hostname_format': '%stackname%-controller-%index%',
'instances': [{
'hostname': 'overcloud-controller-0',
'name': 'bm-0'
}, {
'hostname': 'overcloud-controller-1',
'name': 'bm-1'
}, {
'hostname': 'overcloud-controller-2',
'name': 'bm-2'
}],
}], yaml.safe_load(result))
with open(self.extract_file.name) as f:
self.assertEqual(yaml.safe_load(result), yaml.safe_load(f))
def test_extract_empty(self):
stack_dict = {
'parameters': {},
'outputs': []
}
stack = mock.Mock()
stack.to_dict.return_value = stack_dict
self.orchestration.stacks.get.return_value = stack
nodes = []
self.baremetal.node.list.return_value = nodes
argslist = []
self.app.command_options = argslist
verifylist = []
parsed_args = self.check_parser(self.cmd,
argslist, verifylist)
self.cmd.take_action(parsed_args)
result = self.cmd.app.stdout.make_string()
self.assertIsNone(yaml.safe_load(result))

View File

@ -14,9 +14,11 @@
# #
import collections import collections
import datetime
import json import json
import logging import logging
import os import os
import sys
from cliff.formatters import table from cliff.formatters import table
from osc_lib import exceptions as oscexc from osc_lib import exceptions as oscexc
@ -439,3 +441,87 @@ class DiscoverNode(command.Command):
baremetal.provide(self.app.client_manager, baremetal.provide(self.app.client_manager,
node_uuids=nodes_uuids node_uuids=nodes_uuids
) )
class ExtractProvisionedNode(command.Command):
log = logging.getLogger(__name__ + ".ExtractProvisionedNode")
def _setup_clients(self):
self.clients = self.app.client_manager
self.orchestration_client = self.clients.orchestration
self.baremetal_client = self.clients.baremetal
def get_parser(self, prog_name):
parser = super(ExtractProvisionedNode, 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('-o', '--output',
metavar='<baremetal_deployment.yaml>',
help=_('The output file path describing the '
'baremetal deployment'))
parser.add_argument('-y', '--yes', default=False, action='store_true',
help=_('Skip yes/no prompt for existing files '
'(assume yes).'))
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)
self._setup_clients()
stack = oooutils.get_stack(self.orchestration_client,
parsed_args.stack)
host_vars = oooutils.get_stack_output_item(
stack, 'AnsibleHostVarsMap') or {}
parameters = stack.to_dict().get('parameters', {})
# list all baremetal nodes and map hostname to node name
node_details = self.baremetal_client.node.list(detail=True)
hostname_node_map = {}
for node in node_details:
hostname = node.instance_info.get('display_name')
if hostname and node.name:
hostname_node_map[hostname] = node.name
role_data = six.StringIO()
role_data.write('# Generated with the following on %s\n#\n' %
datetime.datetime.now().isoformat())
role_data.write('# openstack %s\n#\n\n' %
' '.join(self.app.command_options))
for role, entries in host_vars.items():
role_count = len(entries)
# skip zero count roles
if not role_count:
continue
role_data.write('- name: %s\n' % role)
role_data.write(' count: %s\n' % role_count)
hostname_format = parameters.get('%sHostnameFormat' % role)
if hostname_format:
role_data.write(' hostname_format: "%s"\n' % hostname_format)
role_data.write(' instances:\n')
for entry in sorted(entries):
role_data.write(' - hostname: %s\n' % entry)
if entry in hostname_node_map:
role_data.write(' name: %s\n' %
hostname_node_map[entry])
if parsed_args.output:
if (os.path.exists(parsed_args.output)
and not parsed_args.yes and sys.stdin.isatty()):
prompt_response = six.moves.input(
('Overwrite existing file %s [y/N]?' % parsed_args.output)
).lower()
if not prompt_response.startswith('y'):
raise oscexc.CommandError(
"Will not overwrite existing file:"
" %s" % parsed_args.output)
with open(parsed_args.output, 'w+') as fp:
fp.write(role_data.getvalue())
self.app.stdout.write(role_data.getvalue())