Merge "Add 'overcloud node discover' command"
This commit is contained in:
commit
11ad017824
5
releasenotes/notes/ipmi-discovery-aaee9fb7082ffac4.yaml
Normal file
5
releasenotes/notes/ipmi-discovery-aaee9fb7082ffac4.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Add new command ``openstack overcloud node discover`` for nodes discovery
|
||||
by probing a range of IP addresses for accessible BMCs.
|
@ -78,6 +78,7 @@ openstack.tripleoclient.v1 =
|
||||
overcloud_node_import = tripleoclient.v1.overcloud_node:ImportNode
|
||||
overcloud_node_introspect = tripleoclient.v1.overcloud_node:IntrospectNode
|
||||
overcloud_node_provide = tripleoclient.v1.overcloud_node:ProvideNode
|
||||
overcloud_node_discover = tripleoclient.v1.overcloud_node:DiscoverNode
|
||||
overcloud_parameters_set = tripleoclient.v1.overcloud_parameters:SetParameters
|
||||
overcloud_plan_create = tripleoclient.v1.overcloud_plan:CreatePlan
|
||||
overcloud_plan_delete = tripleoclient.v1.overcloud_plan:DeletePlan
|
||||
|
@ -639,3 +639,100 @@ class TestConfigureNode(fakes.TestOvercloudNode):
|
||||
'tripleo.baremetal.v1.configure',
|
||||
workflow_input=self.workflow_input
|
||||
)
|
||||
|
||||
|
||||
class TestDiscoverNode(fakes.TestOvercloudNode):
|
||||
|
||||
def setUp(self):
|
||||
super(TestDiscoverNode, self).setUp()
|
||||
|
||||
self.workflow = self.app.client_manager.workflow_engine
|
||||
client = self.app.client_manager.tripleoclient
|
||||
self.websocket = client.messaging_websocket()
|
||||
|
||||
self.cmd = overcloud_node.DiscoverNode(self.app, None)
|
||||
|
||||
self.websocket.wait_for_messages.return_value = [{
|
||||
"status": "SUCCESS",
|
||||
"message": "Success",
|
||||
"registered_nodes": [{
|
||||
"uuid": "MOCK_NODE_UUID"
|
||||
}]
|
||||
}]
|
||||
|
||||
def test_with_ip_range(self):
|
||||
argslist = ['--range', '10.0.0.0/24',
|
||||
'--credentials', 'admin:password']
|
||||
verifylist = [('ip_addresses', '10.0.0.0/24'),
|
||||
('credentials', ['admin:password'])]
|
||||
|
||||
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.v1.discover_and_enroll_nodes',
|
||||
workflow_input={'ip_addresses': '10.0.0.0/24',
|
||||
'credentials': [['admin', 'password']],
|
||||
'queue_name': mock.ANY,
|
||||
'kernel_name': 'bm-deploy-kernel',
|
||||
'ramdisk_name': 'bm-deploy-ramdisk',
|
||||
'instance_boot_option': 'local'}
|
||||
)
|
||||
|
||||
def test_with_address_list(self):
|
||||
argslist = ['--ip', '10.0.0.1', '--ip', '10.0.0.2',
|
||||
'--credentials', 'admin:password']
|
||||
verifylist = [('ip_addresses', ['10.0.0.1', '10.0.0.2']),
|
||||
('credentials', ['admin:password'])]
|
||||
|
||||
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.v1.discover_and_enroll_nodes',
|
||||
workflow_input={'ip_addresses': ['10.0.0.1', '10.0.0.2'],
|
||||
'credentials': [['admin', 'password']],
|
||||
'queue_name': mock.ANY,
|
||||
'kernel_name': 'bm-deploy-kernel',
|
||||
'ramdisk_name': 'bm-deploy-ramdisk',
|
||||
'instance_boot_option': 'local'}
|
||||
)
|
||||
|
||||
def test_with_all_options(self):
|
||||
argslist = ['--range', '10.0.0.0/24',
|
||||
'--credentials', 'admin:password',
|
||||
'--credentials', 'admin2:password2',
|
||||
'--port', '623', '--port', '6230',
|
||||
'--introspect', '--provide', '--run-validations',
|
||||
'--no-deploy-image', '--instance-boot-option', 'netboot']
|
||||
verifylist = [('ip_addresses', '10.0.0.0/24'),
|
||||
('credentials', ['admin:password', 'admin2:password2']),
|
||||
('port', [623, 6230]),
|
||||
('introspect', True),
|
||||
('run_validations', True),
|
||||
('provide', True),
|
||||
('no_deploy_image', True),
|
||||
('instance_boot_option', 'netboot')]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
workflows_calls = [
|
||||
mock.call('tripleo.baremetal.v1.discover_and_enroll_nodes',
|
||||
workflow_input={'ip_addresses': '10.0.0.0/24',
|
||||
'credentials': [['admin', 'password'],
|
||||
['admin2', 'password2']],
|
||||
'ports': [623, 6230],
|
||||
'queue_name': mock.ANY,
|
||||
'kernel_name': None,
|
||||
'ramdisk_name': None,
|
||||
'instance_boot_option': 'netboot'}),
|
||||
mock.call('tripleo.baremetal.v1.introspect',
|
||||
workflow_input={'node_uuids': ['MOCK_NODE_UUID'],
|
||||
'run_validations': True,
|
||||
'queue_name': mock.ANY}),
|
||||
mock.call('tripleo.baremetal.v1.provide',
|
||||
workflow_input={'node_uuids': ['MOCK_NODE_UUID'],
|
||||
'queue_name': mock.ANY}),
|
||||
]
|
||||
self.workflow.executions.create.assert_has_calls(workflows_calls)
|
||||
|
@ -316,3 +316,83 @@ class ConfigureNode(command.Command):
|
||||
overwrite_root_device_hints=(
|
||||
parsed_args.overwrite_root_device_hints)
|
||||
)
|
||||
|
||||
|
||||
class DiscoverNode(command.Command):
|
||||
"""Discover overcloud nodes by polling their BMCs."""
|
||||
|
||||
log = logging.getLogger(__name__ + ".DiscoverNode")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(DiscoverNode, self).get_parser(prog_name)
|
||||
ip_group = parser.add_mutually_exclusive_group(required=True)
|
||||
ip_group.add_argument('--ip', action='append',
|
||||
dest='ip_addresses', metavar='<ips>',
|
||||
help=_('IP address(es) to probe'))
|
||||
ip_group.add_argument('--range', dest='ip_addresses',
|
||||
metavar='<range>', help=_('IP range to probe'))
|
||||
parser.add_argument('--credentials', metavar='<key:value>',
|
||||
action='append', required=True,
|
||||
help=_('Key/value pairs of possible credentials'))
|
||||
parser.add_argument('--port', action='append', metavar='<ports>',
|
||||
type=int, help=_('BMC port(s) to probe'))
|
||||
parser.add_argument('--introspect', action='store_true',
|
||||
help=_('Introspect the imported nodes'))
|
||||
parser.add_argument('--run-validations', action='store_true',
|
||||
default=False,
|
||||
help=_('Run the pre-deployment validations. These '
|
||||
'external validations are from the TripleO '
|
||||
'Validations project.'))
|
||||
parser.add_argument('--provide', action='store_true',
|
||||
help=_('Provide (make available) the nodes'))
|
||||
parser.add_argument('--no-deploy-image', action='store_true',
|
||||
help=_('Skip setting the deploy kernel and '
|
||||
'ramdisk.'))
|
||||
parser.add_argument('--instance-boot-option',
|
||||
choices=['local', 'netboot'], default='local',
|
||||
help=_('Whether to set instances for booting from '
|
||||
'local hard drive (local) or network '
|
||||
'(netboot).'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug("take_action(%s)" % parsed_args)
|
||||
|
||||
queue_name = str(uuid.uuid4())
|
||||
|
||||
if parsed_args.no_deploy_image:
|
||||
deploy_kernel = None
|
||||
deploy_ramdisk = None
|
||||
else:
|
||||
deploy_kernel = 'bm-deploy-kernel'
|
||||
deploy_ramdisk = 'bm-deploy-ramdisk'
|
||||
|
||||
credentials = [list(x.split(':', 1)) for x in parsed_args.credentials]
|
||||
kwargs = {}
|
||||
# Leave it up to the workflow to figure out the defaults
|
||||
if parsed_args.port:
|
||||
kwargs['ports'] = parsed_args.port
|
||||
|
||||
nodes = baremetal.discover_and_enroll(
|
||||
self.app.client_manager,
|
||||
ip_addresses=parsed_args.ip_addresses,
|
||||
credentials=credentials,
|
||||
queue_name=queue_name,
|
||||
kernel_name=deploy_kernel,
|
||||
ramdisk_name=deploy_ramdisk,
|
||||
instance_boot_option=parsed_args.instance_boot_option,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
nodes_uuids = [node['uuid'] for node in nodes]
|
||||
|
||||
if parsed_args.introspect:
|
||||
baremetal.introspect(self.app.client_manager,
|
||||
node_uuids=nodes_uuids,
|
||||
run_validations=parsed_args.run_validations,
|
||||
queue_name=queue_name)
|
||||
|
||||
if parsed_args.provide:
|
||||
baremetal.provide(self.app.client_manager,
|
||||
node_uuids=nodes_uuids,
|
||||
queue_name=queue_name)
|
||||
|
@ -296,3 +296,34 @@ def create_raid_configuration(clients, **workflow_input):
|
||||
else:
|
||||
raise RuntimeError(
|
||||
'Failed to create RAID: {}'.format(payload['message']))
|
||||
|
||||
|
||||
def discover_and_enroll(clients, **workflow_input):
|
||||
"""Discover nodes.
|
||||
|
||||
Run the tripleo.baremetal.v1.discover_and_enroll_nodes Mistral workflow.
|
||||
"""
|
||||
|
||||
workflow_client = clients.workflow_engine
|
||||
tripleoclients = clients.tripleoclient
|
||||
queue_name = workflow_input['queue_name']
|
||||
|
||||
with tripleoclients.messaging_websocket(queue_name) as ws:
|
||||
execution = base.start_workflow(
|
||||
workflow_client,
|
||||
'tripleo.baremetal.v1.discover_and_enroll_nodes',
|
||||
workflow_input=workflow_input
|
||||
)
|
||||
|
||||
for payload in base.wait_for_messages(workflow_client, ws, execution):
|
||||
if payload.get('message'):
|
||||
print(payload['message'])
|
||||
|
||||
if payload['status'] == 'SUCCESS':
|
||||
registered_nodes = payload['registered_nodes']
|
||||
for nd in registered_nodes:
|
||||
print('Successfully registered node UUID %s' % nd['uuid'])
|
||||
return registered_nodes
|
||||
else:
|
||||
raise exceptions.RegisterOrUpdateError(
|
||||
'Exception discovering nodes: {}'.format(payload['message']))
|
||||
|
Loading…
Reference in New Issue
Block a user