Merge "New command "overcloud node extract provisioned""
This commit is contained in:
commit
5a8255b44c
|
@ -76,6 +76,7 @@ openstack.tripleoclient.v2 =
|
|||
overcloud_node_bios_reset = tripleoclient.v1.overcloud_bios:ResetBIOS
|
||||
overcloud_node_provision = tripleoclient.v2.overcloud_node:ProvisionNode
|
||||
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_plan_create = tripleoclient.v1.overcloud_plan:CreatePlan
|
||||
overcloud_plan_delete = tripleoclient.v1.overcloud_plan:DeletePlan
|
||||
|
|
|
@ -749,3 +749,123 @@ class TestDiscoverNode(fakes.TestOvercloudNode):
|
|||
|
||||
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
|
||||
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))
|
||||
|
|
|
@ -14,9 +14,11 @@
|
|||
#
|
||||
|
||||
import collections
|
||||
import datetime
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
from cliff.formatters import table
|
||||
from osc_lib import exceptions as oscexc
|
||||
|
@ -439,3 +441,87 @@ class DiscoverNode(command.Command):
|
|||
baremetal.provide(self.app.client_manager,
|
||||
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())
|
||||
|
|
Loading…
Reference in New Issue