Actually use the arch and platform to select appropriate images

The prior changes in this series enabled uploading of images tagged in
such a way that they could be used with multiple architectures, this
change uses the data from instckenv.json to select appropriate deploy
images for each node as it's registered to ironic.

Blueprint: multiarch-support
Depends-On: Id82de41e7a49c2d8124fc74125ed51031579aa80
Depends-On: Idaf05b8efce28cd0cbf339cf693db4f55a693d9b
Depends-On: I41dce6e25766562db4366021309b8c2b74a8ab80
Change-Id: I7c84f3035853d8ee7b8d45895e7acce8e9dd3d13
This commit is contained in:
Tony Breeds 2018-01-12 17:27:05 +11:00
parent 3e786fe832
commit 560b79cb1b
3 changed files with 209 additions and 3 deletions
tripleoclient

@ -13,6 +13,7 @@
# under the License.
#
import collections
import copy
import json
import mock
@ -347,7 +348,6 @@ class TestImportNode(fakes.TestOvercloudNode):
"00:0b:d0:69:7e:58"
]
}]
self.json_file = tempfile.NamedTemporaryFile(
mode='w', delete=False, suffix='.json')
json.dump(self.nodes_list, self.json_file)
@ -361,6 +361,14 @@ class TestImportNode(fakes.TestOvercloudNode):
# Get the command object to test
self.cmd = overcloud_node.ImportNode(self.app, None)
image = collections.namedtuple('image', ['id', 'name'])
self.app.client_manager.image = mock.Mock()
self.app.client_manager.image.images.list.return_value = [
image(id=1, name='bm-deploy-kernel'),
image(id=2, name='bm-deploy-ramdisk'),
image(id=3, name='overcloud-full'),
]
def _check_workflow_call(self, parsed_args, introspect=False,
provide=False, local=True, no_deploy_image=False):
self.websocket.wait_for_messages.return_value = [{
@ -453,6 +461,164 @@ class TestImportNode(fakes.TestOvercloudNode):
self._check_workflow_call(parsed_args, no_deploy_image=True)
class TestImportNodeMultiArch(fakes.TestOvercloudNode):
def setUp(self):
super(TestImportNodeMultiArch, self).setUp()
self.nodes_list = [{
"pm_user": "stack",
"pm_addr": "192.168.122.1",
"pm_password": "KEY1",
"pm_type": "pxe_ssh",
"mac": [
"00:0b:d0:69:7e:59"
],
}, {
"pm_user": "stack",
"pm_addr": "192.168.122.2",
"pm_password": "KEY2",
"pm_type": "pxe_ssh",
"arch": "x86_64",
"mac": [
"00:0b:d0:69:7e:58"
]
}, {
"pm_user": "stack",
"pm_addr": "192.168.122.3",
"pm_password": "KEY3",
"pm_type": "pxe_ssh",
"arch": "x86_64",
"platform": "SNB",
"mac": [
"00:0b:d0:69:7e:58"
]
}]
self.json_file = tempfile.NamedTemporaryFile(
mode='w', delete=False, suffix='.json')
json.dump(self.nodes_list, self.json_file)
self.json_file.close()
self.addCleanup(os.unlink, self.json_file.name)
self.workflow = self.app.client_manager.workflow_engine
client = self.app.client_manager.tripleoclient
self.websocket = client.messaging_websocket()
# Get the command object to test
self.cmd = overcloud_node.ImportNode(self.app, None)
image = collections.namedtuple('image', ['id', 'name'])
self.app.client_manager.image = mock.Mock()
self.app.client_manager.image.images.list.return_value = [
image(id=1, name='bm-deploy-kernel'),
image(id=2, name='bm-deploy-ramdisk'),
image(id=3, name='overcloud-full'),
image(id=4, name='x86_64-bm-deploy-kernel'),
image(id=5, name='x86_64-bm-deploy-ramdisk'),
image(id=6, name='x86_64-overcloud-full'),
image(id=7, name='SNB-x86_64-bm-deploy-kernel'),
image(id=8, name='SNB-x86_64-bm-deploy-ramdisk'),
image(id=9, name='SNB-x86_64-overcloud-full'),
]
def _check_workflow_call(self, parsed_args, introspect=False,
provide=False, local=True, no_deploy_image=False):
self.websocket.wait_for_messages.return_value = [{
"status": "SUCCESS",
"message": "Success",
"registered_nodes": [{
"uuid": "MOCK_NODE_UUID"
}]
}]
self.cmd.take_action(parsed_args)
nodes_list = copy.deepcopy(self.nodes_list)
# We expect update_nodes_deploy_data() to set these values for the
# nodes with an 'arch' field
nodes_list[1]['kernel_id'] = 4
nodes_list[1]['ramdisk_id'] = 5
nodes_list[2]['kernel_id'] = 7
nodes_list[2]['ramdisk_id'] = 8
call_count = 1
call_list = [mock.call(
'tripleo.baremetal.v1.register_or_update', workflow_input={
'nodes_json': nodes_list,
'kernel_name': None if no_deploy_image else 'bm-deploy-kernel',
'ramdisk_name': (None
if no_deploy_image else 'bm-deploy-ramdisk'),
'instance_boot_option': 'local' if local else 'netboot'
}
)]
if introspect:
call_count += 1
call_list.append(mock.call(
'tripleo.baremetal.v1.introspect', workflow_input={
'node_uuids': ['MOCK_NODE_UUID'],
'run_validations': False}
))
if provide:
call_count += 1
call_list.append(mock.call(
'tripleo.baremetal.v1.provide', workflow_input={
'node_uuids': ['MOCK_NODE_UUID']
}
))
self.workflow.executions.create.assert_has_calls(call_list)
self.assertEqual(self.workflow.executions.create.call_count,
call_count)
def test_import_only(self):
argslist = [self.json_file.name]
verifylist = [('introspect', False),
('provide', False)]
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
self._check_workflow_call(parsed_args)
def test_import_and_introspect(self):
argslist = [self.json_file.name, '--introspect']
verifylist = [('introspect', True),
('provide', False)]
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
self._check_workflow_call(parsed_args, introspect=True)
def test_import_and_provide(self):
argslist = [self.json_file.name, '--provide']
verifylist = [('introspect', False),
('provide', True)]
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
self._check_workflow_call(parsed_args, provide=True)
def test_import_and_introspect_and_provide(self):
argslist = [self.json_file.name, '--introspect', '--provide']
verifylist = [('introspect', True),
('provide', True)]
parsed_args = self.check_parser(self.cmd, argslist, verifylist)
self._check_workflow_call(parsed_args, introspect=True, provide=True)
def test_import_with_netboot(self):
arglist = [self.json_file.name, '--instance-boot-option', 'netboot']
verifylist = [('instance_boot_option', 'netboot')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self._check_workflow_call(parsed_args, local=False)
def test_import_with_no_deployed_image(self):
arglist = [self.json_file.name, '--no-deploy-image']
verifylist = [('no_deploy_image', True)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self._check_workflow_call(parsed_args, no_deploy_image=True)
class TestConfigureNode(fakes.TestOvercloudNode):
def setUp(self):

@ -1222,3 +1222,38 @@ def deploy_kernel(arch=None, platform=None):
def deploy_ramdisk(arch=None, platform=None):
return (_name_helper('bm-deploy-ramdisk', arch=arch, platform=platform),
'.initramfs')
def update_nodes_deploy_data(imageclient, nodes):
"""Add specific kernel and ramdisk IDs to a node.
Look at all images and update node data with the most specific
deploy_kernel and deploy_ramdisk for the architecture/platform comination
platform.
"""
img_map = {}
for image in imageclient.images.list():
name = image.name
# NOTE(tonyb): We don't want to include the default kernel or ramdisk
# in the map as that will short-circuit logic elesewhere.
if name != deploy_kernel()[0] and name != deploy_ramdisk()[0]:
img_map[image.name] = image.id
for node in nodes:
arch = node.get('arch')
platform = node.get('platform')
# NOTE(tonyb): Check to see if we have a specific kernel for this node
# and use that.
for kernel in [deploy_kernel(arch=arch, platform=platform)[0],
deploy_kernel(arch=arch)[0]]:
if 'kernel_id' not in node and kernel in img_map:
node['kernel_id'] = img_map[kernel]
break
# NOTE(tonyb): As above except for ramdisks
for ramdisk in [deploy_ramdisk(arch=arch, platform=platform)[0],
deploy_ramdisk(arch=arch)[0]]:
if 'ramdisk_id' not in node and ramdisk in img_map:
node['ramdisk_id'] = img_map[ramdisk]
break

@ -255,9 +255,13 @@ class ImportNode(command.Command):
deploy_kernel = None
deploy_ramdisk = None
else:
deploy_kernel = 'bm-deploy-kernel'
deploy_ramdisk = 'bm-deploy-ramdisk'
deploy_kernel = oooutils.deploy_kernel()[0]
deploy_ramdisk = oooutils.deploy_ramdisk()[0]
# Look for *specific* deploy images and update the node data if
# one is found.
oooutils.update_nodes_deploy_data(self.app.client_manager.image,
nodes_config)
nodes = baremetal.register_or_update(
self.app.client_manager,
nodes_json=nodes_config,
@ -391,6 +395,7 @@ class DiscoverNode(command.Command):
'(netboot).'))
return parser
# FIXME(tonyb): This is not multi-arch safe :(
def take_action(self, parsed_args):
self.log.debug("take_action(%s)" % parsed_args)