Store requested capabilities in instance_info

Also allow overriding them in the provision_node call.

Story: #2002171
Task: #20031
Change-Id: I4b3de4db6c3b6371e5a36921e32953ea354784cc
This commit is contained in:
Dmitry Tantsur 2018-07-05 17:49:33 +02:00
parent cbf7294be9
commit 2d6ccf26d8
2 changed files with 90 additions and 3 deletions

View File

@ -80,6 +80,10 @@ class Provisioner(object):
capabilities)
node = _scheduler.schedule_node(nodes, filters, reserver,
dry_run=self._dry_run)
if capabilities:
node = self._api.update_node(
node, {'/instance_info/capabilities': capabilities})
LOG.debug('Reserved node: %s', node)
return node
@ -146,7 +150,8 @@ class Provisioner(object):
return hostname
def provision_node(self, node, image, nics=None, root_disk_size=None,
config=None, hostname=None, netboot=False, wait=None):
config=None, hostname=None, netboot=False,
capabilities=None, wait=None):
"""Provision the node with the given image.
Example::
@ -172,6 +177,10 @@ class Provisioner(object):
:param hostname: Hostname to assign to the instance. Defaults to the
node's name or UUID.
:param netboot: Whether to use networking boot for final instances.
:param capabilities: Requested capabilities of the node. If present,
overwrites the capabilities set by :meth:`reserve_node`.
Note that the capabilities are not checked against the ones
provided by the node - use :meth:`reserve_node` for that.
:param wait: How many seconds to wait for the deployment to finish,
None to return immediately.
:return: :py:class:`metalsmith.Instance` object with the current
@ -200,6 +209,9 @@ class Provisioner(object):
nics = self._get_nics(nics or [])
if capabilities is None:
capabilities = node.instance_info.get('capabilities') or {}
if self._dry_run:
LOG.warning('Dry run, not provisioning node %s',
_utils.log_node(node))
@ -208,11 +220,11 @@ class Provisioner(object):
self._create_and_attach_ports(node, nics,
created_ports, attached_ports)
target_caps = {'boot_option': 'netboot' if netboot else 'local'}
capabilities['boot_option'] = 'netboot' if netboot else 'local'
updates = {'/instance_info/image_source': image.id,
'/instance_info/root_gb': root_disk_size,
'/instance_info/capabilities': target_caps,
'/instance_info/capabilities': capabilities,
'/extra/%s' % _CREATED_PORTS: created_ports,
'/extra/%s' % _ATTACHED_PORTS: attached_ports,
'/instance_info/%s' % _os_api.HOSTNAME_FIELD: hostname}

View File

@ -33,6 +33,7 @@ class Base(testtools.TestCase):
self.node = mock.Mock(spec=_os_api.NODE_FIELDS + ['to_dict'],
uuid='000', instance_uuid=None,
properties={'local_gb': 100},
instance_info={},
maintenance=False, extra={})
self.node.name = 'control-0'
@ -72,6 +73,7 @@ class TestReserveNode(Base):
node = self.pr.reserve_node('control')
self.assertIn(node, nodes)
self.assertFalse(self.api.update_node.called)
def test_with_capabilities(self):
nodes = [
@ -86,6 +88,8 @@ class TestReserveNode(Base):
node = self.pr.reserve_node('control', {'answer': '42'})
self.assertIs(node, expected)
self.api.update_node.assert_called_once_with(
node, {'/instance_info/capabilities': {'answer': '42'}})
CLEAN_UP = {
@ -295,6 +299,77 @@ class TestProvisionNode(Base):
self.assertFalse(self.api.release_node.called)
self.assertFalse(self.api.delete_port.called)
def test_with_capabilities(self):
inst = self.pr.provision_node(self.node, 'image',
[{'network': 'network'}],
capabilities={'answer': '42'})
self.updates['/instance_info/capabilities'] = {'boot_option': 'local',
'answer': '42'}
self.assertEqual(inst.uuid, self.node.uuid)
self.assertEqual(inst.node, self.node)
self.api.create_port.assert_called_once_with(
network_id=self.api.get_network.return_value.id)
self.api.attach_port_to_node.assert_called_once_with(
self.node.uuid, self.api.create_port.return_value.id)
self.api.update_node.assert_called_once_with(self.node, self.updates)
self.api.validate_node.assert_called_once_with(self.node,
validate_deploy=True)
self.api.node_action.assert_called_once_with(self.node, 'active',
configdrive=mock.ANY)
self.assertFalse(self.wait_mock.called)
self.assertFalse(self.api.release_node.called)
self.assertFalse(self.api.delete_port.called)
def test_with_existing_capabilities(self):
self.node.instance_info['capabilities'] = {'answer': '42'}
inst = self.pr.provision_node(self.node, 'image',
[{'network': 'network'}])
self.updates['/instance_info/capabilities'] = {'boot_option': 'local',
'answer': '42'}
self.assertEqual(inst.uuid, self.node.uuid)
self.assertEqual(inst.node, self.node)
self.api.create_port.assert_called_once_with(
network_id=self.api.get_network.return_value.id)
self.api.attach_port_to_node.assert_called_once_with(
self.node.uuid, self.api.create_port.return_value.id)
self.api.update_node.assert_called_once_with(self.node, self.updates)
self.api.validate_node.assert_called_once_with(self.node,
validate_deploy=True)
self.api.node_action.assert_called_once_with(self.node, 'active',
configdrive=mock.ANY)
self.assertFalse(self.wait_mock.called)
self.assertFalse(self.api.release_node.called)
self.assertFalse(self.api.delete_port.called)
def test_override_existing_capabilities(self):
self.node.instance_info['capabilities'] = {'answer': '1',
'cat': 'meow'}
inst = self.pr.provision_node(self.node, 'image',
[{'network': 'network'}],
capabilities={'answer': '42'})
self.updates['/instance_info/capabilities'] = {'boot_option': 'local',
'answer': '42'}
self.assertEqual(inst.uuid, self.node.uuid)
self.assertEqual(inst.node, self.node)
self.api.create_port.assert_called_once_with(
network_id=self.api.get_network.return_value.id)
self.api.attach_port_to_node.assert_called_once_with(
self.node.uuid, self.api.create_port.return_value.id)
self.api.update_node.assert_called_once_with(self.node, self.updates)
self.api.validate_node.assert_called_once_with(self.node,
validate_deploy=True)
self.api.node_action.assert_called_once_with(self.node, 'active',
configdrive=mock.ANY)
self.assertFalse(self.wait_mock.called)
self.assertFalse(self.api.release_node.called)
self.assertFalse(self.api.delete_port.called)
def test_with_wait(self):
self.api.get_port.return_value = mock.Mock(
spec=['fixed_ips'],