Foundation for different deployment sources
Change-Id: I7c9538e37476d9d3ea5b9cc403419dda95cf77cc Story: #2002048 Task: #26064
This commit is contained in:
parent
1f92d9a5fb
commit
a34d0e0951
@ -26,6 +26,7 @@ from metalsmith import _os_api
|
||||
from metalsmith import _scheduler
|
||||
from metalsmith import _utils
|
||||
from metalsmith import exceptions
|
||||
from metalsmith import sources
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -178,7 +179,8 @@ class Provisioner(object):
|
||||
:param node: Node object, UUID or name. Will be reserved first, if
|
||||
not reserved already. Must be in the "available" state with
|
||||
maintenance mode off.
|
||||
:param image: Image name or UUID to provision.
|
||||
:param image: Image source - one of :mod:`~metalsmith.sources`,
|
||||
`Image` name or UUID.
|
||||
:param nics: List of virtual NICs to attach to physical ports.
|
||||
Each item is a dict with a key describing the type of the NIC:
|
||||
either a port (``{"port": "<port name or ID>"}``) or a network
|
||||
@ -203,6 +205,9 @@ class Provisioner(object):
|
||||
"""
|
||||
if config is None:
|
||||
config = _config.InstanceConfig()
|
||||
if isinstance(image, six.string_types):
|
||||
image = sources.Glance(image)
|
||||
|
||||
node = self._check_node_for_deploy(node)
|
||||
created_ports = []
|
||||
attached_ports = []
|
||||
@ -211,14 +216,7 @@ class Provisioner(object):
|
||||
hostname = self._check_hostname(node, hostname)
|
||||
root_disk_size = _utils.get_root_disk(root_disk_size, node)
|
||||
|
||||
try:
|
||||
image = self._api.get_image(image)
|
||||
except Exception as exc:
|
||||
raise exceptions.InvalidImage(
|
||||
'Cannot find image %(image)s: %(error)s' %
|
||||
{'image': image, 'error': exc})
|
||||
|
||||
LOG.debug('Image: %s', image)
|
||||
image._validate(self._api)
|
||||
|
||||
nics = self._get_nics(nics or [])
|
||||
|
||||
@ -235,17 +233,12 @@ class Provisioner(object):
|
||||
|
||||
capabilities['boot_option'] = 'netboot' if netboot else 'local'
|
||||
|
||||
updates = {'/instance_info/image_source': image.id,
|
||||
'/instance_info/root_gb': root_disk_size,
|
||||
updates = {'/instance_info/root_gb': root_disk_size,
|
||||
'/instance_info/capabilities': capabilities,
|
||||
'/extra/%s' % _CREATED_PORTS: created_ports,
|
||||
'/extra/%s' % _ATTACHED_PORTS: attached_ports,
|
||||
'/instance_info/%s' % _os_api.HOSTNAME_FIELD: hostname}
|
||||
|
||||
for prop in ('kernel', 'ramdisk'):
|
||||
value = getattr(image, '%s_id' % prop, None)
|
||||
if value:
|
||||
updates['/instance_info/%s' % prop] = value
|
||||
updates.update(image._node_updates(self._api))
|
||||
|
||||
LOG.debug('Updating node %(node)s with %(updates)s',
|
||||
{'node': _utils.log_node(node), 'updates': updates})
|
||||
|
73
metalsmith/sources.py
Normal file
73
metalsmith/sources.py
Normal file
@ -0,0 +1,73 @@
|
||||
# Copyright 2018 Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Image sources to use when provisioning nodes."""
|
||||
|
||||
import abc
|
||||
import logging
|
||||
|
||||
import six
|
||||
|
||||
from metalsmith import exceptions
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class _Source(object):
|
||||
|
||||
def _validate(self, api):
|
||||
"""Validate the source."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def _node_updates(self, api):
|
||||
"""Updates required for a node to use this source."""
|
||||
|
||||
|
||||
class Glance(_Source):
|
||||
"""Image from the OpenStack Image service."""
|
||||
|
||||
def __init__(self, image):
|
||||
"""Create a Glance source.
|
||||
|
||||
:param image: `Image` object, ID or name.
|
||||
"""
|
||||
self._image_id = image
|
||||
self._image_obj = None
|
||||
|
||||
def _validate(self, api):
|
||||
if self._image_obj is not None:
|
||||
return
|
||||
try:
|
||||
self._image_obj = api.get_image(self._image_id)
|
||||
except Exception as exc:
|
||||
raise exceptions.InvalidImage(
|
||||
'Cannot find image %(image)s: %(error)s' %
|
||||
{'image': self._image_id, 'error': exc})
|
||||
|
||||
def _node_updates(self, api):
|
||||
self._validate(api)
|
||||
LOG.debug('Image: %s', self._image_obj)
|
||||
|
||||
updates = {
|
||||
'/instance_info/image_source': self._image_obj.id
|
||||
}
|
||||
for prop in ('kernel', 'ramdisk'):
|
||||
value = getattr(self._image_obj, '%s_id' % prop, None)
|
||||
if value:
|
||||
updates['/instance_info/%s' % prop] = value
|
||||
|
||||
return updates
|
@ -22,6 +22,7 @@ from metalsmith import _instance
|
||||
from metalsmith import _os_api
|
||||
from metalsmith import _provisioner
|
||||
from metalsmith import exceptions
|
||||
from metalsmith import sources
|
||||
|
||||
|
||||
class Base(testtools.TestCase):
|
||||
@ -206,6 +207,26 @@ class TestProvisionNode(Base):
|
||||
self.assertFalse(self.api.release_node.called)
|
||||
self.assertFalse(self.api.delete_port.called)
|
||||
|
||||
def test_ok_with_source(self):
|
||||
inst = self.pr.provision_node(self.node, sources.Glance('image'),
|
||||
[{'network': 'network'}])
|
||||
|
||||
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_config(self):
|
||||
config = mock.MagicMock(spec=_config.InstanceConfig)
|
||||
inst = self.pr.provision_node(self.node, 'image',
|
||||
|
Loading…
Reference in New Issue
Block a user