Merge "Refactor IRMCVirtualMediaIscsiDeploy by applying new BootInterface"
This commit is contained in:
commit
8f6add2843
|
@ -22,9 +22,11 @@ from ironic.common import exception
|
|||
from ironic.common.i18n import _
|
||||
from ironic.drivers import base
|
||||
from ironic.drivers.modules import ipmitool
|
||||
from ironic.drivers.modules.irmc import boot
|
||||
from ironic.drivers.modules.irmc import deploy
|
||||
from ironic.drivers.modules.irmc import management
|
||||
from ironic.drivers.modules.irmc import power
|
||||
from ironic.drivers.modules import iscsi_deploy
|
||||
|
||||
|
||||
class IRMCVirtualMediaIscsiDriver(base.BaseDriver):
|
||||
|
@ -33,8 +35,7 @@ class IRMCVirtualMediaIscsiDriver(base.BaseDriver):
|
|||
This driver implements the `core` functionality using
|
||||
:class:ironic.drivers.modules.irmc.power.IRMCPower for power management.
|
||||
and
|
||||
:class:ironic.drivers.modules.irmc.deploy.IRMCVirtualMediaIscsiDeploy for
|
||||
deploy.
|
||||
:class:ironic.drivers.modules.iscsi_deploy.ISCSIDeploy for deploy.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
|
@ -44,10 +45,11 @@ class IRMCVirtualMediaIscsiDriver(base.BaseDriver):
|
|||
reason=_("Unable to import python-scciclient library"))
|
||||
|
||||
self.power = power.IRMCPower()
|
||||
self.deploy = deploy.IRMCVirtualMediaIscsiDeploy()
|
||||
self.boot = boot.IRMCVirtualMediaBoot()
|
||||
self.deploy = iscsi_deploy.ISCSIDeploy()
|
||||
self.console = ipmitool.IPMIShellinaboxConsole()
|
||||
self.management = management.IRMCManagement()
|
||||
self.vendor = deploy.VendorPassthru()
|
||||
self.vendor = iscsi_deploy.VendorPassthru()
|
||||
|
||||
|
||||
class IRMCVirtualMediaAgentDriver(base.BaseDriver):
|
||||
|
|
|
@ -0,0 +1,673 @@
|
|||
# Copyright 2015 FUJITSU LIMITED
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
iRMC Boot Driver
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import importutils
|
||||
|
||||
from ironic.common import boot_devices
|
||||
from ironic.common import exception
|
||||
from ironic.common.glance_service import service_utils
|
||||
from ironic.common.i18n import _
|
||||
from ironic.common.i18n import _LE
|
||||
from ironic.common.i18n import _LI
|
||||
from ironic.common import images
|
||||
from ironic.common import states
|
||||
from ironic.common import utils
|
||||
from ironic.conductor import utils as manager_utils
|
||||
from ironic.drivers import base
|
||||
from ironic.drivers.modules import deploy_utils
|
||||
from ironic.drivers.modules.irmc import common as irmc_common
|
||||
|
||||
|
||||
scci = importutils.try_import('scciclient.irmc.scci')
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
try:
|
||||
if CONF.debug:
|
||||
scci.DEBUG = True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
opts = [
|
||||
cfg.StrOpt('remote_image_share_root',
|
||||
default='/remote_image_share_root',
|
||||
help=_('Ironic conductor node\'s "NFS" or "CIFS" root path')),
|
||||
cfg.StrOpt('remote_image_server',
|
||||
help=_('IP of remote image server')),
|
||||
cfg.StrOpt('remote_image_share_type',
|
||||
default='CIFS',
|
||||
help=_('Share type of virtual media, either "NFS" or "CIFS"')),
|
||||
cfg.StrOpt('remote_image_share_name',
|
||||
default='share',
|
||||
help=_('share name of remote_image_server')),
|
||||
cfg.StrOpt('remote_image_user_name',
|
||||
help=_('User name of remote_image_server')),
|
||||
cfg.StrOpt('remote_image_user_password', secret=True,
|
||||
help=_('Password of remote_image_user_name')),
|
||||
cfg.StrOpt('remote_image_user_domain',
|
||||
default='',
|
||||
help=_('Domain name of remote_image_user_name')),
|
||||
]
|
||||
|
||||
CONF.register_opts(opts, group='irmc')
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
REQUIRED_PROPERTIES = {
|
||||
'irmc_deploy_iso': _("Deployment ISO image file name. "
|
||||
"Required."),
|
||||
}
|
||||
|
||||
COMMON_PROPERTIES = REQUIRED_PROPERTIES
|
||||
|
||||
SUPPORTED_SHARE_TYPES = ('nfs', 'cifs')
|
||||
|
||||
|
||||
def _parse_config_option():
|
||||
"""Parse config file options.
|
||||
|
||||
This method checks config file options validity.
|
||||
|
||||
:raises: InvalidParameterValue, if config option has invalid value.
|
||||
"""
|
||||
error_msgs = []
|
||||
if not os.path.isdir(CONF.irmc.remote_image_share_root):
|
||||
error_msgs.append(
|
||||
_("Value '%s' for remote_image_share_root isn't a directory "
|
||||
"or doesn't exist.") %
|
||||
CONF.irmc.remote_image_share_root)
|
||||
if CONF.irmc.remote_image_share_type.lower() not in SUPPORTED_SHARE_TYPES:
|
||||
error_msgs.append(
|
||||
_("Value '%s' for remote_image_share_type is not supported "
|
||||
"value either 'NFS' or 'CIFS'.") %
|
||||
CONF.irmc.remote_image_share_type)
|
||||
if error_msgs:
|
||||
msg = (_("The following errors were encountered while parsing "
|
||||
"config file:%s") % error_msgs)
|
||||
raise exception.InvalidParameterValue(msg)
|
||||
|
||||
|
||||
def parse_driver_info(node):
|
||||
"""Gets the driver specific Node deployment info.
|
||||
|
||||
This method validates whether the 'driver_info' property of the
|
||||
supplied node contains the required or optional information properly
|
||||
for this driver to deploy images to the node.
|
||||
|
||||
:param node: a target node of the deployment
|
||||
:returns: the driver_info values of the node.
|
||||
:raises: MissingParameterValue, if any of the required parameters are
|
||||
missing.
|
||||
:raises: InvalidParameterValue, if any of the parameters have invalid
|
||||
value.
|
||||
"""
|
||||
d_info = node.driver_info
|
||||
deploy_info = {}
|
||||
|
||||
deploy_info['irmc_deploy_iso'] = d_info.get('irmc_deploy_iso')
|
||||
error_msg = _("Error validating iRMC virtual media deploy. Some parameters"
|
||||
" were missing in node's driver_info")
|
||||
deploy_utils.check_for_missing_params(deploy_info, error_msg)
|
||||
|
||||
if service_utils.is_image_href_ordinary_file_name(
|
||||
deploy_info['irmc_deploy_iso']):
|
||||
deploy_iso = os.path.join(CONF.irmc.remote_image_share_root,
|
||||
deploy_info['irmc_deploy_iso'])
|
||||
if not os.path.isfile(deploy_iso):
|
||||
msg = (_("Deploy ISO file, %(deploy_iso)s, "
|
||||
"not found for node: %(node)s.") %
|
||||
{'deploy_iso': deploy_iso, 'node': node.uuid})
|
||||
raise exception.InvalidParameterValue(msg)
|
||||
|
||||
return deploy_info
|
||||
|
||||
|
||||
def _parse_instance_info(node):
|
||||
"""Gets the instance specific Node deployment info.
|
||||
|
||||
This method validates whether the 'instance_info' property of the
|
||||
supplied node contains the required or optional information properly
|
||||
for this driver to deploy images to the node.
|
||||
|
||||
:param node: a target node of the deployment
|
||||
:returns: the instance_info values of the node.
|
||||
:raises: InvalidParameterValue, if any of the parameters have invalid
|
||||
value.
|
||||
"""
|
||||
i_info = node.instance_info
|
||||
deploy_info = {}
|
||||
|
||||
if i_info.get('irmc_boot_iso'):
|
||||
deploy_info['irmc_boot_iso'] = i_info['irmc_boot_iso']
|
||||
|
||||
if service_utils.is_image_href_ordinary_file_name(
|
||||
deploy_info['irmc_boot_iso']):
|
||||
boot_iso = os.path.join(CONF.irmc.remote_image_share_root,
|
||||
deploy_info['irmc_boot_iso'])
|
||||
|
||||
if not os.path.isfile(boot_iso):
|
||||
msg = (_("Boot ISO file, %(boot_iso)s, "
|
||||
"not found for node: %(node)s.") %
|
||||
{'boot_iso': boot_iso, 'node': node.uuid})
|
||||
raise exception.InvalidParameterValue(msg)
|
||||
|
||||
return deploy_info
|
||||
|
||||
|
||||
def parse_deploy_info(node):
|
||||
"""Gets the instance and driver specific Node deployment info.
|
||||
|
||||
This method validates whether the 'instance_info' and 'driver_info'
|
||||
property of the supplied node contains the required information for
|
||||
this driver to deploy images to the node.
|
||||
|
||||
:param node: a target node of the deployment
|
||||
:returns: a dict with the instance_info and driver_info values.
|
||||
:raises: MissingParameterValue, if any of the required parameters are
|
||||
missing.
|
||||
:raises: InvalidParameterValue, if any of the parameters have invalid
|
||||
value.
|
||||
"""
|
||||
deploy_info = {}
|
||||
deploy_info.update(deploy_utils.get_image_instance_info(node))
|
||||
deploy_info.update(parse_driver_info(node))
|
||||
deploy_info.update(_parse_instance_info(node))
|
||||
|
||||
return deploy_info
|
||||
|
||||
|
||||
def setup_deploy_iso(task, ramdisk_options):
|
||||
"""Attaches virtual media and sets it as boot device.
|
||||
|
||||
This method attaches the given deploy ISO as virtual media, prepares the
|
||||
arguments for ramdisk in virtual media floppy.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:param ramdisk_options: the options to be passed to the ramdisk in virtual
|
||||
media floppy.
|
||||
:raises: ImageRefValidationFailed if no image service can handle specified
|
||||
href.
|
||||
:raises: ImageCreationFailed, if it failed while creating the floppy image.
|
||||
:raises: IRMCOperationError, if some operation on iRMC failed.
|
||||
:raises: InvalidParameterValue if the validation of the
|
||||
PowerInterface or ManagementInterface fails.
|
||||
"""
|
||||
d_info = task.node.driver_info
|
||||
|
||||
deploy_iso_href = d_info['irmc_deploy_iso']
|
||||
if service_utils.is_image_href_ordinary_file_name(deploy_iso_href):
|
||||
deploy_iso_file = deploy_iso_href
|
||||
else:
|
||||
deploy_iso_file = _get_deploy_iso_name(task.node)
|
||||
deploy_iso_fullpathname = os.path.join(
|
||||
CONF.irmc.remote_image_share_root, deploy_iso_file)
|
||||
images.fetch(task.context, deploy_iso_href, deploy_iso_fullpathname)
|
||||
|
||||
_setup_vmedia_for_boot(task, deploy_iso_file, ramdisk_options)
|
||||
manager_utils.node_set_boot_device(task, boot_devices.CDROM)
|
||||
|
||||
|
||||
def _get_deploy_iso_name(node):
|
||||
"""Returns the deploy ISO file name for a given node.
|
||||
|
||||
:param node: the node for which ISO file name is to be provided.
|
||||
"""
|
||||
return "deploy-%s.iso" % node.uuid
|
||||
|
||||
|
||||
def _get_boot_iso_name(node):
|
||||
"""Returns the boot ISO file name for a given node.
|
||||
|
||||
:param node: the node for which ISO file name is to be provided.
|
||||
"""
|
||||
return "boot-%s.iso" % node.uuid
|
||||
|
||||
|
||||
def _prepare_boot_iso(task, root_uuid):
|
||||
"""Prepare a boot ISO to boot the node.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:param root_uuid: the uuid of the root partition.
|
||||
:raises: MissingParameterValue, if any of the required parameters are
|
||||
missing.
|
||||
:raises: InvalidParameterValue, if any of the parameters have invalid
|
||||
value.
|
||||
:raises: ImageCreationFailed, if creating boot ISO
|
||||
for BIOS boot_mode failed.
|
||||
"""
|
||||
deploy_info = parse_deploy_info(task.node)
|
||||
driver_internal_info = task.node.driver_internal_info
|
||||
|
||||
# fetch boot iso
|
||||
if deploy_info.get('irmc_boot_iso'):
|
||||
boot_iso_href = deploy_info['irmc_boot_iso']
|
||||
if service_utils.is_image_href_ordinary_file_name(boot_iso_href):
|
||||
driver_internal_info['irmc_boot_iso'] = boot_iso_href
|
||||
else:
|
||||
boot_iso_filename = _get_boot_iso_name(task.node)
|
||||
boot_iso_fullpathname = os.path.join(
|
||||
CONF.irmc.remote_image_share_root, boot_iso_filename)
|
||||
images.fetch(task.context, boot_iso_href, boot_iso_fullpathname)
|
||||
|
||||
driver_internal_info['irmc_boot_iso'] = boot_iso_filename
|
||||
|
||||
# create boot iso
|
||||
else:
|
||||
image_href = deploy_info['image_source']
|
||||
image_props = ['kernel_id', 'ramdisk_id']
|
||||
image_properties = images.get_image_properties(
|
||||
task.context, image_href, image_props)
|
||||
kernel_href = (task.node.instance_info.get('kernel') or
|
||||
image_properties['kernel_id'])
|
||||
ramdisk_href = (task.node.instance_info.get('ramdisk') or
|
||||
image_properties['ramdisk_id'])
|
||||
|
||||
deploy_iso_filename = _get_deploy_iso_name(task.node)
|
||||
deploy_iso = ('file://' + os.path.join(
|
||||
CONF.irmc.remote_image_share_root, deploy_iso_filename))
|
||||
boot_mode = deploy_utils.get_boot_mode_for_deploy(task.node)
|
||||
kernel_params = CONF.pxe.pxe_append_params
|
||||
|
||||
boot_iso_filename = _get_boot_iso_name(task.node)
|
||||
boot_iso_fullpathname = os.path.join(
|
||||
CONF.irmc.remote_image_share_root, boot_iso_filename)
|
||||
|
||||
images.create_boot_iso(task.context, boot_iso_fullpathname,
|
||||
kernel_href, ramdisk_href,
|
||||
deploy_iso, root_uuid,
|
||||
kernel_params, boot_mode)
|
||||
|
||||
driver_internal_info['irmc_boot_iso'] = boot_iso_filename
|
||||
|
||||
# save driver_internal_info['irmc_boot_iso']
|
||||
task.node.driver_internal_info = driver_internal_info
|
||||
task.node.save()
|
||||
|
||||
|
||||
def _get_floppy_image_name(node):
|
||||
"""Returns the floppy image name for a given node.
|
||||
|
||||
:param node: the node for which image name is to be provided.
|
||||
"""
|
||||
return "image-%s.img" % node.uuid
|
||||
|
||||
|
||||
def _prepare_floppy_image(task, params):
|
||||
"""Prepares the floppy image for passing the parameters.
|
||||
|
||||
This method prepares a temporary vfat filesystem image, which
|
||||
contains the parameters to be passed to the ramdisk.
|
||||
Then it uploads the file NFS or CIFS server.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:param params: a dictionary containing 'parameter name'->'value' mapping
|
||||
to be passed to the deploy ramdisk via the floppy image.
|
||||
:returns: floppy image filename
|
||||
:raises: ImageCreationFailed, if it failed while creating the floppy image.
|
||||
:raises: IRMCOperationError, if copying floppy image file failed.
|
||||
"""
|
||||
floppy_filename = _get_floppy_image_name(task.node)
|
||||
floppy_fullpathname = os.path.join(
|
||||
CONF.irmc.remote_image_share_root, floppy_filename)
|
||||
|
||||
with tempfile.NamedTemporaryFile() as vfat_image_tmpfile_obj:
|
||||
images.create_vfat_image(vfat_image_tmpfile_obj.name,
|
||||
parameters=params)
|
||||
try:
|
||||
shutil.copyfile(vfat_image_tmpfile_obj.name,
|
||||
floppy_fullpathname)
|
||||
except IOError as e:
|
||||
operation = _("Copying floppy image file")
|
||||
raise exception.IRMCOperationError(
|
||||
operation=operation, error=e)
|
||||
|
||||
return floppy_filename
|
||||
|
||||
|
||||
def attach_boot_iso_if_needed(task):
|
||||
"""Attaches boot ISO for a deployed node if it exists.
|
||||
|
||||
This method checks the instance info of the bare metal node for a
|
||||
boot ISO. If the instance info has a value of key 'irmc_boot_iso',
|
||||
it indicates that 'boot_option' is 'netboot'. Threfore it attaches
|
||||
the boot ISO on the bare metal node and then sets the node to boot from
|
||||
virtual media cdrom.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:raises: IRMCOperationError if attaching virtual media failed.
|
||||
:raises: InvalidParameterValue if the validation of the
|
||||
ManagementInterface fails.
|
||||
"""
|
||||
d_info = task.node.driver_internal_info
|
||||
node_state = task.node.provision_state
|
||||
|
||||
if 'irmc_boot_iso' in d_info and node_state == states.ACTIVE:
|
||||
_setup_vmedia_for_boot(task, d_info['irmc_boot_iso'])
|
||||
manager_utils.node_set_boot_device(task, boot_devices.CDROM)
|
||||
|
||||
|
||||
def _setup_vmedia_for_boot(task, bootable_iso_filename, parameters=None):
|
||||
"""Sets up the node to boot from the boot ISO image.
|
||||
|
||||
This method attaches a boot_iso on the node and passes
|
||||
the required parameters to it via a virtual floppy image.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:param bootable_iso_filename: a bootable ISO image to attach to.
|
||||
The iso file should be present in NFS/CIFS server.
|
||||
:param parameters: the parameters to pass in a virtual floppy image
|
||||
in a dictionary. This is optional.
|
||||
:raises: ImageCreationFailed, if it failed while creating a floppy image.
|
||||
:raises: IRMCOperationError, if attaching a virtual media failed.
|
||||
"""
|
||||
LOG.info(_LI("Setting up node %s to boot from virtual media"),
|
||||
task.node.uuid)
|
||||
|
||||
_detach_virtual_cd(task.node)
|
||||
_detach_virtual_fd(task.node)
|
||||
|
||||
if parameters:
|
||||
floppy_image_filename = _prepare_floppy_image(task, parameters)
|
||||
_attach_virtual_fd(task.node, floppy_image_filename)
|
||||
|
||||
_attach_virtual_cd(task.node, bootable_iso_filename)
|
||||
|
||||
|
||||
def cleanup_vmedia_boot(task):
|
||||
"""Cleans a node after a virtual media boot.
|
||||
|
||||
This method cleans up a node after a virtual media boot.
|
||||
It deletes floppy and cdrom images if they exist in NFS/CIFS server.
|
||||
It also ejects both the virtual media cdrom and the virtual media floppy.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:raises: IRMCOperationError if ejecting virtual media failed.
|
||||
"""
|
||||
LOG.debug("Cleaning up node %s after virtual media boot", task.node.uuid)
|
||||
|
||||
node = task.node
|
||||
_detach_virtual_cd(node)
|
||||
_detach_virtual_fd(node)
|
||||
|
||||
_remove_share_file(_get_floppy_image_name(node))
|
||||
_remove_share_file(_get_deploy_iso_name(node))
|
||||
|
||||
|
||||
def _remove_share_file(share_filename):
|
||||
"""Remove given file from the share file system.
|
||||
|
||||
:param share_filename: a file name to be removed.
|
||||
"""
|
||||
share_fullpathname = os.path.join(
|
||||
CONF.irmc.remote_image_share_name, share_filename)
|
||||
utils.unlink_without_raise(share_fullpathname)
|
||||
|
||||
|
||||
def _attach_virtual_cd(node, bootable_iso_filename):
|
||||
"""Attaches the given url as virtual media on the node.
|
||||
|
||||
:param node: an ironic node object.
|
||||
:param bootable_iso_filename: a bootable ISO image to attach to.
|
||||
The iso file should be present in NFS/CIFS server.
|
||||
:raises: IRMCOperationError if attaching virtual media failed.
|
||||
"""
|
||||
try:
|
||||
irmc_client = irmc_common.get_irmc_client(node)
|
||||
|
||||
cd_set_params = scci.get_virtual_cd_set_params_cmd(
|
||||
CONF.irmc.remote_image_server,
|
||||
CONF.irmc.remote_image_user_domain,
|
||||
scci.get_share_type(CONF.irmc.remote_image_share_type),
|
||||
CONF.irmc.remote_image_share_name,
|
||||
bootable_iso_filename,
|
||||
CONF.irmc.remote_image_user_name,
|
||||
CONF.irmc.remote_image_user_password)
|
||||
|
||||
irmc_client(cd_set_params, async=False)
|
||||
irmc_client(scci.MOUNT_CD, async=False)
|
||||
|
||||
except scci.SCCIClientError as irmc_exception:
|
||||
LOG.exception(_LE("Error while inserting virtual cdrom "
|
||||
"into node %(uuid)s. Error: %(error)s"),
|
||||
{'uuid': node.uuid, 'error': irmc_exception})
|
||||
operation = _("Inserting virtual cdrom")
|
||||
raise exception.IRMCOperationError(operation=operation,
|
||||
error=irmc_exception)
|
||||
|
||||
LOG.info(_LI("Attached virtual cdrom successfully"
|
||||
" for node %s"), node.uuid)
|
||||
|
||||
|
||||
def _detach_virtual_cd(node):
|
||||
"""Detaches virtual cdrom on the node.
|
||||
|
||||
:param node: an ironic node object.
|
||||
:raises: IRMCOperationError if eject virtual cdrom failed.
|
||||
"""
|
||||
try:
|
||||
irmc_client = irmc_common.get_irmc_client(node)
|
||||
|
||||
irmc_client(scci.UNMOUNT_CD)
|
||||
|
||||
except scci.SCCIClientError as irmc_exception:
|
||||
LOG.exception(_LE("Error while ejecting virtual cdrom "
|
||||
"from node %(uuid)s. Error: %(error)s"),
|
||||
{'uuid': node.uuid, 'error': irmc_exception})
|
||||
operation = _("Ejecting virtual cdrom")
|
||||
raise exception.IRMCOperationError(operation=operation,
|
||||
error=irmc_exception)
|
||||
|
||||
LOG.info(_LI("Detached virtual cdrom successfully"
|
||||
" for node %s"), node.uuid)
|
||||
|
||||
|
||||
def _attach_virtual_fd(node, floppy_image_filename):
|
||||
"""Attaches virtual floppy on the node.
|
||||
|
||||
:param node: an ironic node object.
|
||||
:raises: IRMCOperationError if insert virtual floppy failed.
|
||||
"""
|
||||
try:
|
||||
irmc_client = irmc_common.get_irmc_client(node)
|
||||
|
||||
fd_set_params = scci.get_virtual_fd_set_params_cmd(
|
||||
CONF.irmc.remote_image_server,
|
||||
CONF.irmc.remote_image_user_domain,
|
||||
scci.get_share_type(CONF.irmc.remote_image_share_type),
|
||||
CONF.irmc.remote_image_share_name,
|
||||
floppy_image_filename,
|
||||
CONF.irmc.remote_image_user_name,
|
||||
CONF.irmc.remote_image_user_password)
|
||||
|
||||
irmc_client(fd_set_params, async=False)
|
||||
irmc_client(scci.MOUNT_FD, async=False)
|
||||
|
||||
except scci.SCCIClientError as irmc_exception:
|
||||
LOG.exception(_LE("Error while inserting virtual floppy "
|
||||
"into node %(uuid)s. Error: %(error)s"),
|
||||
{'uuid': node.uuid, 'error': irmc_exception})
|
||||
operation = _("Inserting virtual floppy")
|
||||
raise exception.IRMCOperationError(operation=operation,
|
||||
error=irmc_exception)
|
||||
|
||||
LOG.info(_LI("Attached virtual floppy successfully"
|
||||
" for node %s"), node.uuid)
|
||||
|
||||
|
||||
def _detach_virtual_fd(node):
|
||||
"""Detaches virtual media floppy on the node.
|
||||
|
||||
:param node: an ironic node object.
|
||||
:raises: IRMCOperationError if eject virtual media floppy failed.
|
||||
"""
|
||||
try:
|
||||
irmc_client = irmc_common.get_irmc_client(node)
|
||||
|
||||
irmc_client(scci.UNMOUNT_FD)
|
||||
|
||||
except scci.SCCIClientError as irmc_exception:
|
||||
LOG.exception(_LE("Error while ejecting virtual floppy "
|
||||
"from node %(uuid)s. Error: %(error)s"),
|
||||
{'uuid': node.uuid, 'error': irmc_exception})
|
||||
operation = _("Ejecting virtual floppy")
|
||||
raise exception.IRMCOperationError(operation=operation,
|
||||
error=irmc_exception)
|
||||
|
||||
LOG.info(_LI("Detached virtual floppy successfully"
|
||||
" for node %s"), node.uuid)
|
||||
|
||||
|
||||
def check_share_fs_mounted():
|
||||
"""Check if Share File System (NFS or CIFS) is mounted.
|
||||
|
||||
:raises: InvalidParameterValue, if config option has invalid value.
|
||||
:raises: IRMCSharedFileSystemNotMounted, if shared file system is
|
||||
not mounted.
|
||||
"""
|
||||
_parse_config_option()
|
||||
if not os.path.ismount(CONF.irmc.remote_image_share_root):
|
||||
raise exception.IRMCSharedFileSystemNotMounted(
|
||||
share=CONF.irmc.remote_image_share_root)
|
||||
|
||||
|
||||
class IRMCVirtualMediaBoot(base.BootInterface):
|
||||
"""Interface for iSCSI deploy-related actions."""
|
||||
|
||||
def __init__(self):
|
||||
"""Constructor of IRMCVirtualMediaBoot.
|
||||
|
||||
:raises: IRMCSharedFileSystemNotMounted, if shared file system is
|
||||
not mounted.
|
||||
:raises: InvalidParameterValue, if config option has invalid value.
|
||||
"""
|
||||
check_share_fs_mounted()
|
||||
super(IRMCVirtualMediaBoot, self).__init__()
|
||||
|
||||
def get_properties(self):
|
||||
return COMMON_PROPERTIES
|
||||
|
||||
def validate(self, task):
|
||||
"""Validate the deployment information for the task's node.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:raises: InvalidParameterValue, if config option has invalid value.
|
||||
:raises: IRMCSharedFileSystemNotMounted, if shared file system is
|
||||
not mounted.
|
||||
:raises: InvalidParameterValue, if some information is invalid.
|
||||
:raises: MissingParameterValue if 'kernel_id' and 'ramdisk_id' are
|
||||
missing in the Glance image, or if 'kernel' and 'ramdisk' are
|
||||
missing in the Non Glance image.
|
||||
"""
|
||||
check_share_fs_mounted()
|
||||
|
||||
d_info = parse_deploy_info(task.node)
|
||||
if task.node.driver_internal_info.get('is_whole_disk_image'):
|
||||
props = []
|
||||
elif service_utils.is_glance_image(d_info['image_source']):
|
||||
props = ['kernel_id', 'ramdisk_id']
|
||||
else:
|
||||
props = ['kernel', 'ramdisk']
|
||||
deploy_utils.validate_image_properties(task.context, d_info,
|
||||
props)
|
||||
|
||||
def prepare_ramdisk(self, task, ramdisk_params):
|
||||
"""Prepares the deploy ramdisk using virtual media.
|
||||
|
||||
Prepares the options for the deployment ramdisk, sets the node to boot
|
||||
from virtual media cdrom.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:param ramdisk_params: the options to be passed to the deploy ramdisk.
|
||||
:raises: ImageRefValidationFailed if no image service can handle
|
||||
specified href.
|
||||
:raises: ImageCreationFailed, if it failed while creating the floppy
|
||||
image.
|
||||
:raises: InvalidParameterValue if the validation of the
|
||||
PowerInterface or ManagementInterface fails.
|
||||
:raises: IRMCOperationError, if some operation on iRMC fails.
|
||||
"""
|
||||
|
||||
deploy_nic_mac = deploy_utils.get_single_nic_with_vif_port_id(task)
|
||||
ramdisk_params['BOOTIF'] = deploy_nic_mac
|
||||
|
||||
setup_deploy_iso(task, ramdisk_params)
|
||||
|
||||
def clean_up_ramdisk(self, task):
|
||||
"""Cleans up the boot of ironic ramdisk.
|
||||
|
||||
This method cleans up the environment that was setup for booting the
|
||||
deploy ramdisk.
|
||||
|
||||
:param task: a task from TaskManager.
|
||||
:returns: None
|
||||
:raises: IRMCOperationError if iRMC operation failed.
|
||||
"""
|
||||
cleanup_vmedia_boot(task)
|
||||
|
||||
def prepare_instance(self, task):
|
||||
"""Prepares the boot of instance.
|
||||
|
||||
This method prepares the boot of the instance after reading
|
||||
relevant information from the node's database.
|
||||
|
||||
:param task: a task from TaskManager.
|
||||
:returns: None
|
||||
"""
|
||||
cleanup_vmedia_boot(task)
|
||||
|
||||
node = task.node
|
||||
iwdi = node.driver_internal_info.get('is_whole_disk_image')
|
||||
if deploy_utils.get_boot_option(node) == "local" or iwdi:
|
||||
manager_utils.node_set_boot_device(task, boot_devices.DISK,
|
||||
persistent=True)
|
||||
else:
|
||||
driver_internal_info = node.driver_internal_info
|
||||
root_uuid_or_disk_id = driver_internal_info['root_uuid_or_disk_id']
|
||||
self._configure_vmedia_boot(task, root_uuid_or_disk_id)
|
||||
|
||||
def clean_up_instance(self, task):
|
||||
"""Cleans up the boot of instance.
|
||||
|
||||
This method cleans up the environment that was setup for booting
|
||||
the instance.
|
||||
|
||||
:param task: a task from TaskManager.
|
||||
:returns: None
|
||||
:raises: IRMCOperationError if iRMC operation failed.
|
||||
"""
|
||||
_remove_share_file(_get_boot_iso_name(task.node))
|
||||
driver_internal_info = task.node.driver_internal_info
|
||||
driver_internal_info.pop('irmc_boot_iso', None)
|
||||
task.node.driver_internal_info = driver_internal_info
|
||||
task.node.save()
|
||||
cleanup_vmedia_boot(task)
|
||||
|
||||
def _configure_vmedia_boot(self, task, root_uuid_or_disk_id):
|
||||
"""Configure vmedia boot for the node."""
|
||||
node = task.node
|
||||
_prepare_boot_iso(task, root_uuid_or_disk_id)
|
||||
_setup_vmedia_for_boot(
|
||||
task, node.driver_internal_info['irmc_boot_iso'])
|
||||
manager_utils.node_set_boot_device(task, boot_devices.CDROM,
|
||||
persistent=True)
|
|
@ -16,649 +16,19 @@
|
|||
iRMC Deploy Driver
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import importutils
|
||||
|
||||
from ironic.common import boot_devices
|
||||
from ironic.common import exception
|
||||
from ironic.common.glance_service import service_utils
|
||||
from ironic.common.i18n import _
|
||||
from ironic.common.i18n import _LE
|
||||
from ironic.common.i18n import _LI
|
||||
from ironic.common.i18n import _LW
|
||||
from ironic.common import images
|
||||
from ironic.common import states
|
||||
from ironic.common import utils
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.conductor import utils as manager_utils
|
||||
from ironic.drivers import base
|
||||
from ironic.drivers.modules import agent
|
||||
from ironic.drivers.modules import agent_base_vendor
|
||||
from ironic.drivers.modules import deploy_utils
|
||||
from ironic.drivers.modules.irmc import common as irmc_common
|
||||
from ironic.drivers.modules import iscsi_deploy
|
||||
from ironic.drivers.modules.irmc import boot as irmc_boot
|
||||
|
||||
scci = importutils.try_import('scciclient.irmc.scci')
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
try:
|
||||
if CONF.debug:
|
||||
scci.DEBUG = True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
opts = [
|
||||
cfg.StrOpt('remote_image_share_root',
|
||||
default='/remote_image_share_root',
|
||||
help='Ironic conductor node\'s "NFS" or "CIFS" root path'),
|
||||
cfg.StrOpt('remote_image_server',
|
||||
help='IP of remote image server'),
|
||||
cfg.StrOpt('remote_image_share_type',
|
||||
default='CIFS',
|
||||
help='Share type of virtual media, either "NFS" or "CIFS"'),
|
||||
cfg.StrOpt('remote_image_share_name',
|
||||
default='share',
|
||||
help='share name of remote_image_server'),
|
||||
cfg.StrOpt('remote_image_user_name',
|
||||
help='User name of remote_image_server'),
|
||||
cfg.StrOpt('remote_image_user_password', secret=True,
|
||||
help='Password of remote_image_user_name'),
|
||||
cfg.StrOpt('remote_image_user_domain',
|
||||
default='',
|
||||
help='Domain name of remote_image_user_name'),
|
||||
]
|
||||
|
||||
CONF.register_opts(opts, group='irmc')
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
REQUIRED_PROPERTIES = {
|
||||
'irmc_deploy_iso': _("Deployment ISO image file name. "
|
||||
"Required."),
|
||||
}
|
||||
|
||||
COMMON_PROPERTIES = REQUIRED_PROPERTIES
|
||||
|
||||
CONF.import_opt('pxe_append_params', 'ironic.drivers.modules.iscsi_deploy',
|
||||
group='pxe')
|
||||
|
||||
SUPPORTED_SHARE_TYPES = ('nfs', 'cifs')
|
||||
|
||||
|
||||
def _parse_config_option():
|
||||
"""Parse config file options.
|
||||
|
||||
This method checks config file options validity.
|
||||
|
||||
:raises: InvalidParameterValue, if config option has invalid value.
|
||||
"""
|
||||
error_msgs = []
|
||||
if not os.path.isdir(CONF.irmc.remote_image_share_root):
|
||||
error_msgs.append(
|
||||
_("Value '%s' for remote_image_share_root isn't a directory "
|
||||
"or doesn't exist.") %
|
||||
CONF.irmc.remote_image_share_root)
|
||||
if CONF.irmc.remote_image_share_type.lower() not in SUPPORTED_SHARE_TYPES:
|
||||
error_msgs.append(
|
||||
_("Value '%s' for remote_image_share_type is not supported "
|
||||
"value either 'NFS' or 'CIFS'.") %
|
||||
CONF.irmc.remote_image_share_type)
|
||||
if error_msgs:
|
||||
msg = (_("The following errors were encountered while parsing "
|
||||
"config file:%s") % error_msgs)
|
||||
raise exception.InvalidParameterValue(msg)
|
||||
|
||||
|
||||
def _parse_driver_info(node):
|
||||
"""Gets the driver specific Node deployment info.
|
||||
|
||||
This method validates whether the 'driver_info' property of the
|
||||
supplied node contains the required or optional information properly
|
||||
for this driver to deploy images to the node.
|
||||
|
||||
:param node: a target node of the deployment
|
||||
:returns: the driver_info values of the node.
|
||||
:raises: MissingParameterValue, if any of the required parameters are
|
||||
missing.
|
||||
:raises: InvalidParameterValue, if any of the parameters have invalid
|
||||
value.
|
||||
"""
|
||||
d_info = node.driver_info
|
||||
deploy_info = {}
|
||||
|
||||
deploy_info['irmc_deploy_iso'] = d_info.get('irmc_deploy_iso')
|
||||
error_msg = _("Error validating iRMC virtual media deploy. Some parameters"
|
||||
" were missing in node's driver_info")
|
||||
deploy_utils.check_for_missing_params(deploy_info, error_msg)
|
||||
|
||||
if service_utils.is_image_href_ordinary_file_name(
|
||||
deploy_info['irmc_deploy_iso']):
|
||||
deploy_iso = os.path.join(CONF.irmc.remote_image_share_root,
|
||||
deploy_info['irmc_deploy_iso'])
|
||||
if not os.path.isfile(deploy_iso):
|
||||
msg = (_("Deploy ISO file, %(deploy_iso)s, "
|
||||
"not found for node: %(node)s.") %
|
||||
{'deploy_iso': deploy_iso, 'node': node.uuid})
|
||||
raise exception.InvalidParameterValue(msg)
|
||||
|
||||
return deploy_info
|
||||
|
||||
|
||||
def _parse_instance_info(node):
|
||||
"""Gets the instance specific Node deployment info.
|
||||
|
||||
This method validates whether the 'instance_info' property of the
|
||||
supplied node contains the required or optional information properly
|
||||
for this driver to deploy images to the node.
|
||||
|
||||
:param node: a target node of the deployment
|
||||
:returns: the instance_info values of the node.
|
||||
:raises: InvalidParameterValue, if any of the parameters have invalid
|
||||
value.
|
||||
"""
|
||||
i_info = node.instance_info
|
||||
deploy_info = {}
|
||||
|
||||
if i_info.get('irmc_boot_iso'):
|
||||
deploy_info['irmc_boot_iso'] = i_info['irmc_boot_iso']
|
||||
|
||||
if service_utils.is_image_href_ordinary_file_name(
|
||||
deploy_info['irmc_boot_iso']):
|
||||
boot_iso = os.path.join(CONF.irmc.remote_image_share_root,
|
||||
deploy_info['irmc_boot_iso'])
|
||||
|
||||
if not os.path.isfile(boot_iso):
|
||||
msg = (_("Boot ISO file, %(boot_iso)s, "
|
||||
"not found for node: %(node)s.") %
|
||||
{'boot_iso': boot_iso, 'node': node.uuid})
|
||||
raise exception.InvalidParameterValue(msg)
|
||||
|
||||
return deploy_info
|
||||
|
||||
|
||||
def _parse_deploy_info(node):
|
||||
"""Gets the instance and driver specific Node deployment info.
|
||||
|
||||
This method validates whether the 'instance_info' and 'driver_info'
|
||||
property of the supplied node contains the required information for
|
||||
this driver to deploy images to the node.
|
||||
|
||||
:param node: a target node of the deployment
|
||||
:returns: a dict with the instance_info and driver_info values.
|
||||
:raises: MissingParameterValue, if any of the required parameters are
|
||||
missing.
|
||||
:raises: InvalidParameterValue, if any of the parameters have invalid
|
||||
value.
|
||||
"""
|
||||
deploy_info = {}
|
||||
deploy_info.update(iscsi_deploy.parse_instance_info(node))
|
||||
deploy_info.update(_parse_driver_info(node))
|
||||
deploy_info.update(_parse_instance_info(node))
|
||||
|
||||
return deploy_info
|
||||
|
||||
|
||||
def _reboot_into_deploy_iso(task, ramdisk_options):
|
||||
"""Reboots the node into a given deploy ISO.
|
||||
|
||||
This method attaches the given deploy ISO as virtual media, prepares the
|
||||
arguments for ramdisk in virtual media floppy, and then reboots the node.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:param ramdisk_options: the options to be passed to the ramdisk in virtual
|
||||
media floppy.
|
||||
:raises: ImageRefValidationFailed if no image service can handle specified
|
||||
href.
|
||||
:raises: ImageCreationFailed, if it failed while creating the floppy image.
|
||||
:raises: IRMCOperationError, if some operation on iRMC failed.
|
||||
:raises: InvalidParameterValue if the validation of the
|
||||
PowerInterface or ManagementInterface fails.
|
||||
"""
|
||||
d_info = task.node.driver_info
|
||||
|
||||
deploy_iso_href = d_info['irmc_deploy_iso']
|
||||
if service_utils.is_image_href_ordinary_file_name(deploy_iso_href):
|
||||
deploy_iso_file = deploy_iso_href
|
||||
else:
|
||||
deploy_iso_file = _get_deploy_iso_name(task.node)
|
||||
deploy_iso_fullpathname = os.path.join(
|
||||
CONF.irmc.remote_image_share_root, deploy_iso_file)
|
||||
images.fetch(task.context, deploy_iso_href, deploy_iso_fullpathname)
|
||||
|
||||
setup_vmedia_for_boot(task, deploy_iso_file, ramdisk_options)
|
||||
manager_utils.node_set_boot_device(task, boot_devices.CDROM)
|
||||
manager_utils.node_power_action(task, states.REBOOT)
|
||||
|
||||
|
||||
def _get_deploy_iso_name(node):
|
||||
"""Returns the deploy ISO file name for a given node.
|
||||
|
||||
:param node: the node for which ISO file name is to be provided.
|
||||
"""
|
||||
return "deploy-%s.iso" % node.uuid
|
||||
|
||||
|
||||
def _get_boot_iso_name(node):
|
||||
"""Returns the boot ISO file name for a given node.
|
||||
|
||||
:param node: the node for which ISO file name is to be provided.
|
||||
"""
|
||||
return "boot-%s.iso" % node.uuid
|
||||
|
||||
|
||||
def _prepare_boot_iso(task, root_uuid):
|
||||
"""Prepare a boot ISO to boot the node.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:param root_uuid: the uuid of the root partition.
|
||||
:raises: MissingParameterValue, if any of the required parameters are
|
||||
missing.
|
||||
:raises: InvalidParameterValue, if any of the parameters have invalid
|
||||
value.
|
||||
:raises: ImageCreationFailed, if creating boot ISO
|
||||
for BIOS boot_mode failed.
|
||||
"""
|
||||
deploy_info = _parse_deploy_info(task.node)
|
||||
driver_internal_info = task.node.driver_internal_info
|
||||
|
||||
# fetch boot iso
|
||||
if deploy_info.get('irmc_boot_iso'):
|
||||
boot_iso_href = deploy_info['irmc_boot_iso']
|
||||
if service_utils.is_image_href_ordinary_file_name(boot_iso_href):
|
||||
driver_internal_info['irmc_boot_iso'] = boot_iso_href
|
||||
else:
|
||||
boot_iso_filename = _get_boot_iso_name(task.node)
|
||||
boot_iso_fullpathname = os.path.join(
|
||||
CONF.irmc.remote_image_share_root, boot_iso_filename)
|
||||
images.fetch(task.context, boot_iso_href, boot_iso_fullpathname)
|
||||
|
||||
driver_internal_info['irmc_boot_iso'] = boot_iso_filename
|
||||
|
||||
# create boot iso
|
||||
else:
|
||||
image_href = deploy_info['image_source']
|
||||
image_props = ['kernel_id', 'ramdisk_id']
|
||||
image_properties = images.get_image_properties(
|
||||
task.context, image_href, image_props)
|
||||
kernel_href = (task.node.instance_info.get('kernel') or
|
||||
image_properties['kernel_id'])
|
||||
ramdisk_href = (task.node.instance_info.get('ramdisk') or
|
||||
image_properties['ramdisk_id'])
|
||||
|
||||
deploy_iso_filename = _get_deploy_iso_name(task.node)
|
||||
deploy_iso = ('file://' + os.path.join(
|
||||
CONF.irmc.remote_image_share_root, deploy_iso_filename))
|
||||
boot_mode = deploy_utils.get_boot_mode_for_deploy(task.node)
|
||||
kernel_params = CONF.pxe.pxe_append_params
|
||||
|
||||
boot_iso_filename = _get_boot_iso_name(task.node)
|
||||
boot_iso_fullpathname = os.path.join(
|
||||
CONF.irmc.remote_image_share_root, boot_iso_filename)
|
||||
|
||||
images.create_boot_iso(task.context, boot_iso_fullpathname,
|
||||
kernel_href, ramdisk_href,
|
||||
deploy_iso, root_uuid,
|
||||
kernel_params, boot_mode)
|
||||
|
||||
driver_internal_info['irmc_boot_iso'] = boot_iso_filename
|
||||
|
||||
# save driver_internal_info['irmc_boot_iso']
|
||||
task.node.driver_internal_info = driver_internal_info
|
||||
task.node.save()
|
||||
|
||||
|
||||
def _get_floppy_image_name(node):
|
||||
"""Returns the floppy image name for a given node.
|
||||
|
||||
:param node: the node for which image name is to be provided.
|
||||
"""
|
||||
return "image-%s.img" % node.uuid
|
||||
|
||||
|
||||
def _prepare_floppy_image(task, params):
|
||||
"""Prepares the floppy image for passing the parameters.
|
||||
|
||||
This method prepares a temporary vfat filesystem image, which
|
||||
contains the parameters to be passed to the ramdisk.
|
||||
Then it uploads the file NFS or CIFS server.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:param params: a dictionary containing 'parameter name'->'value' mapping
|
||||
to be passed to the deploy ramdisk via the floppy image.
|
||||
:returns: floppy image filename
|
||||
:raises: ImageCreationFailed, if it failed while creating the floppy image.
|
||||
:raises: IRMCOperationError, if copying floppy image file failed.
|
||||
"""
|
||||
floppy_filename = _get_floppy_image_name(task.node)
|
||||
floppy_fullpathname = os.path.join(
|
||||
CONF.irmc.remote_image_share_root, floppy_filename)
|
||||
|
||||
with tempfile.NamedTemporaryFile() as vfat_image_tmpfile_obj:
|
||||
images.create_vfat_image(vfat_image_tmpfile_obj.name,
|
||||
parameters=params)
|
||||
try:
|
||||
shutil.copyfile(vfat_image_tmpfile_obj.name,
|
||||
floppy_fullpathname)
|
||||
except IOError as e:
|
||||
operation = _("Copying floppy image file")
|
||||
raise exception.IRMCOperationError(
|
||||
operation=operation, error=e)
|
||||
|
||||
return floppy_filename
|
||||
|
||||
|
||||
def setup_vmedia_for_boot(task, bootable_iso_filename, parameters=None):
|
||||
"""Sets up the node to boot from the boot ISO image.
|
||||
|
||||
This method attaches a boot_iso on the node and passes
|
||||
the required parameters to it via a virtual floppy image.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:param bootable_iso_filename: a bootable ISO image to attach to.
|
||||
The iso file should be present in NFS/CIFS server.
|
||||
:param parameters: the parameters to pass in a virtual floppy image
|
||||
in a dictionary. This is optional.
|
||||
:raises: ImageCreationFailed, if it failed while creating a floppy image.
|
||||
:raises: IRMCOperationError, if attaching a virtual media failed.
|
||||
"""
|
||||
LOG.info(_LI("Setting up node %s to boot from virtual media"),
|
||||
task.node.uuid)
|
||||
|
||||
_detach_virtual_cd(task.node)
|
||||
_detach_virtual_fd(task.node)
|
||||
|
||||
if parameters:
|
||||
floppy_image_filename = _prepare_floppy_image(task, parameters)
|
||||
_attach_virtual_fd(task.node, floppy_image_filename)
|
||||
|
||||
_attach_virtual_cd(task.node, bootable_iso_filename)
|
||||
|
||||
|
||||
def _cleanup_vmedia_boot(task):
|
||||
"""Cleans a node after a virtual media boot.
|
||||
|
||||
This method cleans up a node after a virtual media boot.
|
||||
It deletes a floppy image if it exists in NFS/CIFS server.
|
||||
It also ejects both the virtual media cdrom and the virtual media floppy.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:raises: IRMCOperationError if ejecting virtual media failed.
|
||||
"""
|
||||
LOG.debug("Cleaning up node %s after virtual media boot", task.node.uuid)
|
||||
|
||||
node = task.node
|
||||
_detach_virtual_cd(node)
|
||||
_detach_virtual_fd(node)
|
||||
|
||||
_remove_share_file(_get_floppy_image_name(node))
|
||||
_remove_share_file(_get_deploy_iso_name(node))
|
||||
|
||||
|
||||
def _remove_share_file(share_filename):
|
||||
"""remove a file in the share file system.
|
||||
|
||||
:param share_filename: a file name to be removed.
|
||||
"""
|
||||
share_fullpathname = os.path.join(
|
||||
CONF.irmc.remote_image_share_name, share_filename)
|
||||
utils.unlink_without_raise(share_fullpathname)
|
||||
|
||||
|
||||
def _attach_virtual_cd(node, bootable_iso_filename):
|
||||
"""Attaches the given url as virtual media on the node.
|
||||
|
||||
:param node: an ironic node object.
|
||||
:param bootable_iso_filename: a bootable ISO image to attach to.
|
||||
The iso file should be present in NFS/CIFS server.
|
||||
:raises: IRMCOperationError if attaching virtual media failed.
|
||||
"""
|
||||
try:
|
||||
irmc_client = irmc_common.get_irmc_client(node)
|
||||
|
||||
cd_set_params = scci.get_virtual_cd_set_params_cmd(
|
||||
CONF.irmc.remote_image_server,
|
||||
CONF.irmc.remote_image_user_domain,
|
||||
scci.get_share_type(CONF.irmc.remote_image_share_type),
|
||||
CONF.irmc.remote_image_share_name,
|
||||
bootable_iso_filename,
|
||||
CONF.irmc.remote_image_user_name,
|
||||
CONF.irmc.remote_image_user_password)
|
||||
|
||||
irmc_client(cd_set_params, async=False)
|
||||
irmc_client(scci.MOUNT_CD, async=False)
|
||||
|
||||
except scci.SCCIClientError as irmc_exception:
|
||||
LOG.exception(_LE("Error while inserting virtual cdrom "
|
||||
"from node %(uuid)s. Error: %(error)s"),
|
||||
{'uuid': node.uuid, 'error': irmc_exception})
|
||||
operation = _("Inserting virtual cdrom")
|
||||
raise exception.IRMCOperationError(operation=operation,
|
||||
error=irmc_exception)
|
||||
|
||||
LOG.info(_LI("Attached virtual cdrom successfully"
|
||||
" for node %s"), node.uuid)
|
||||
|
||||
|
||||
def _detach_virtual_cd(node):
|
||||
"""Detaches virtual cdrom on the node.
|
||||
|
||||
:param node: an ironic node object.
|
||||
:raises: IRMCOperationError if eject virtual cdrom failed.
|
||||
"""
|
||||
try:
|
||||
irmc_client = irmc_common.get_irmc_client(node)
|
||||
|
||||
irmc_client(scci.UNMOUNT_CD)
|
||||
|
||||
except scci.SCCIClientError as irmc_exception:
|
||||
LOG.exception(_LE("Error while ejecting virtual cdrom "
|
||||
"from node %(uuid)s. Error: %(error)s"),
|
||||
{'uuid': node.uuid, 'error': irmc_exception})
|
||||
operation = _("Ejecting virtual cdrom")
|
||||
raise exception.IRMCOperationError(operation=operation,
|
||||
error=irmc_exception)
|
||||
|
||||
LOG.info(_LI("Detached virtual cdrom successfully"
|
||||
" for node %s"), node.uuid)
|
||||
|
||||
|
||||
def _attach_virtual_fd(node, floppy_image_filename):
|
||||
"""Attaches virtual floppy on the node.
|
||||
|
||||
:param node: an ironic node object.
|
||||
:raises: IRMCOperationError if insert virtual floppy failed.
|
||||
"""
|
||||
try:
|
||||
irmc_client = irmc_common.get_irmc_client(node)
|
||||
|
||||
fd_set_params = scci.get_virtual_fd_set_params_cmd(
|
||||
CONF.irmc.remote_image_server,
|
||||
CONF.irmc.remote_image_user_domain,
|
||||
scci.get_share_type(CONF.irmc.remote_image_share_type),
|
||||
CONF.irmc.remote_image_share_name,
|
||||
floppy_image_filename,
|
||||
CONF.irmc.remote_image_user_name,
|
||||
CONF.irmc.remote_image_user_password)
|
||||
|
||||
irmc_client(fd_set_params, async=False)
|
||||
irmc_client(scci.MOUNT_FD, async=False)
|
||||
|
||||
except scci.SCCIClientError as irmc_exception:
|
||||
LOG.exception(_LE("Error while inserting virtual floppy "
|
||||
"from node %(uuid)s. Error: %(error)s"),
|
||||
{'uuid': node.uuid, 'error': irmc_exception})
|
||||
operation = _("Inserting virtual floppy")
|
||||
raise exception.IRMCOperationError(operation=operation,
|
||||
error=irmc_exception)
|
||||
|
||||
LOG.info(_LI("Attached virtual floppy successfully"
|
||||
" for node %s"), node.uuid)
|
||||
|
||||
|
||||
def _detach_virtual_fd(node):
|
||||
"""Detaches virtual media on the node.
|
||||
|
||||
:param node: an ironic node object.
|
||||
:raises: IRMCOperationError if eject virtual media failed.
|
||||
"""
|
||||
try:
|
||||
irmc_client = irmc_common.get_irmc_client(node)
|
||||
|
||||
irmc_client(scci.UNMOUNT_FD)
|
||||
|
||||
except scci.SCCIClientError as irmc_exception:
|
||||
LOG.exception(_LE("Error while ejecting virtual floppy "
|
||||
"from node %(uuid)s. Error: %(error)s"),
|
||||
{'uuid': node.uuid, 'error': irmc_exception})
|
||||
operation = _("Ejecting virtual floppy")
|
||||
raise exception.IRMCOperationError(operation=operation,
|
||||
error=irmc_exception)
|
||||
|
||||
LOG.info(_LI("Detached virtual floppy successfully"
|
||||
" for node %s"), node.uuid)
|
||||
|
||||
|
||||
def _check_share_fs_mounted():
|
||||
"""Check if Share File System (NFS or CIFS) is mounted.
|
||||
|
||||
:raises: InvalidParameterValue, if config option has invalid value.
|
||||
:raises: IRMCSharedFileSystemNotMounted, if shared file system is
|
||||
not mounted.
|
||||
"""
|
||||
_parse_config_option()
|
||||
if not os.path.ismount(CONF.irmc.remote_image_share_root):
|
||||
raise exception.IRMCSharedFileSystemNotMounted(
|
||||
share=CONF.irmc.remote_image_share_root)
|
||||
|
||||
|
||||
class IRMCVirtualMediaIscsiDeploy(base.DeployInterface):
|
||||
"""Interface for iSCSI deploy-related actions."""
|
||||
|
||||
def __init__(self):
|
||||
"""Constructor of IRMCVirtualMediaIscsiDeploy.
|
||||
|
||||
:raises: IRMCSharedFileSystemNotMounted, if shared file system is
|
||||
not mounted.
|
||||
:raises: InvalidParameterValue, if config option has invalid value.
|
||||
"""
|
||||
_check_share_fs_mounted()
|
||||
super(IRMCVirtualMediaIscsiDeploy, self).__init__()
|
||||
|
||||
def get_properties(self):
|
||||
return COMMON_PROPERTIES
|
||||
|
||||
def validate(self, task):
|
||||
"""Validate the deployment information for the task's node.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:raises: InvalidParameterValue, if config option has invalid value.
|
||||
:raises: IRMCSharedFileSystemNotMounted, if shared file system is
|
||||
not mounted.
|
||||
:raises: InvalidParameterValue, if some information is invalid.
|
||||
:raises: MissingParameterValue if 'kernel_id' and 'ramdisk_id' are
|
||||
missing in the Glance image, or if 'kernel' and 'ramdisk' are
|
||||
missing in the Non Glance image.
|
||||
"""
|
||||
_check_share_fs_mounted()
|
||||
iscsi_deploy.validate(task)
|
||||
|
||||
d_info = _parse_deploy_info(task.node)
|
||||
if task.node.driver_internal_info.get('is_whole_disk_image'):
|
||||
props = []
|
||||
elif service_utils.is_glance_image(d_info['image_source']):
|
||||
props = ['kernel_id', 'ramdisk_id']
|
||||
else:
|
||||
props = ['kernel', 'ramdisk']
|
||||
deploy_utils.validate_image_properties(task.context, d_info,
|
||||
props)
|
||||
deploy_utils.validate_capabilities(task.node)
|
||||
|
||||
@task_manager.require_exclusive_lock
|
||||
def deploy(self, task):
|
||||
"""Start deployment of the task's node.
|
||||
|
||||
Fetches the instance image, prepares the options for the deployment
|
||||
ramdisk, sets the node to boot from virtual media cdrom, and reboots
|
||||
the given node.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:returns: deploy state DEPLOYWAIT.
|
||||
:raises: InstanceDeployFailure, if image size if greater than root
|
||||
partition.
|
||||
:raises: ImageCreationFailed, if it failed while creating the floppy
|
||||
image.
|
||||
:raises: IRMCOperationError, if some operation on iRMC fails.
|
||||
"""
|
||||
node = task.node
|
||||
manager_utils.node_power_action(task, states.POWER_OFF)
|
||||
|
||||
iscsi_deploy.cache_instance_image(task.context, node)
|
||||
iscsi_deploy.check_image_size(task)
|
||||
|
||||
deploy_ramdisk_opts = iscsi_deploy.build_deploy_ramdisk_options(node)
|
||||
agent_opts = deploy_utils.build_agent_options(node)
|
||||
deploy_ramdisk_opts.update(agent_opts)
|
||||
deploy_nic_mac = deploy_utils.get_single_nic_with_vif_port_id(task)
|
||||
deploy_ramdisk_opts['BOOTIF'] = deploy_nic_mac
|
||||
|
||||
_reboot_into_deploy_iso(task, deploy_ramdisk_opts)
|
||||
|
||||
return states.DEPLOYWAIT
|
||||
|
||||
@task_manager.require_exclusive_lock
|
||||
def tear_down(self, task):
|
||||
"""Tear down a previous deployment on the task's node.
|
||||
|
||||
Power off the node. All actual clean-up is done in the clean_up()
|
||||
method which should be called separately.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:returns: deploy state DELETED.
|
||||
"""
|
||||
_remove_share_file(_get_boot_iso_name(task.node))
|
||||
driver_internal_info = task.node.driver_internal_info
|
||||
driver_internal_info.pop('irmc_boot_iso', None)
|
||||
task.node.driver_internal_info = driver_internal_info
|
||||
task.node.save()
|
||||
manager_utils.node_power_action(task, states.POWER_OFF)
|
||||
return states.DELETED
|
||||
|
||||
def prepare(self, task):
|
||||
"""Prepare the deployment environment for the task's node.
|
||||
|
||||
If preparation of the deployment environment ahead of time is possible,
|
||||
this method should be implemented by the driver.
|
||||
|
||||
If implemented, this method must be idempotent. It may be called
|
||||
multiple times for the same node on the same conductor, and it may be
|
||||
called by multiple conductors in parallel. Therefore, it must not
|
||||
require an exclusive lock.
|
||||
|
||||
This method is called before `deploy`.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
"""
|
||||
pass
|
||||
|
||||
def clean_up(self, task):
|
||||
"""Clean up the deployment environment for the task's node.
|
||||
|
||||
Unlinks instance image and triggers image cache cleanup.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
"""
|
||||
_cleanup_vmedia_boot(task)
|
||||
iscsi_deploy.destroy_images(task.node.uuid)
|
||||
|
||||
def take_over(self, task):
|
||||
pass
|
||||
|
||||
|
||||
class IRMCVirtualMediaAgentDeploy(base.DeployInterface):
|
||||
|
||||
|
@ -669,7 +39,7 @@ class IRMCVirtualMediaAgentDeploy(base.DeployInterface):
|
|||
not mounted.
|
||||
:raises: InvalidParameterValue, if config option has invalid value.
|
||||
"""
|
||||
_check_share_fs_mounted()
|
||||
irmc_boot.check_share_fs_mounted()
|
||||
super(IRMCVirtualMediaAgentDeploy, self).__init__()
|
||||
|
||||
"""Interface for Agent deploy-related actions."""
|
||||
|
@ -678,7 +48,7 @@ class IRMCVirtualMediaAgentDeploy(base.DeployInterface):
|
|||
|
||||
:returns: dictionary of <property name>:<property description> entries.
|
||||
"""
|
||||
return COMMON_PROPERTIES
|
||||
return irmc_boot.COMMON_PROPERTIES
|
||||
|
||||
def validate(self, task):
|
||||
"""Validate the driver-specific Node deployment info.
|
||||
|
@ -689,8 +59,8 @@ class IRMCVirtualMediaAgentDeploy(base.DeployInterface):
|
|||
:raises: InvalidParameterValue, if config option has invalid value.
|
||||
:raises: MissingParameterValue if some parameters are missing.
|
||||
"""
|
||||
_check_share_fs_mounted()
|
||||
_parse_driver_info(task.node)
|
||||
irmc_boot.check_share_fs_mounted()
|
||||
irmc_boot.parse_driver_info(task.node)
|
||||
deploy_utils.validate_capabilities(task.node)
|
||||
|
||||
@task_manager.require_exclusive_lock
|
||||
|
@ -707,7 +77,8 @@ class IRMCVirtualMediaAgentDeploy(base.DeployInterface):
|
|||
:raises: IRMCOperationError, if some operation on iRMC fails.
|
||||
"""
|
||||
deploy_ramdisk_opts = deploy_utils.build_agent_options(task.node)
|
||||
_reboot_into_deploy_iso(task, deploy_ramdisk_opts)
|
||||
irmc_boot.setup_deploy_iso(task, deploy_ramdisk_opts)
|
||||
manager_utils.node_power_action(task, states.REBOOT)
|
||||
|
||||
return states.DEPLOYWAIT
|
||||
|
||||
|
@ -738,7 +109,7 @@ class IRMCVirtualMediaAgentDeploy(base.DeployInterface):
|
|||
|
||||
:param task: a TaskManager instance.
|
||||
"""
|
||||
_cleanup_vmedia_boot(task)
|
||||
irmc_boot.cleanup_vmedia_boot(task)
|
||||
|
||||
def take_over(self, task):
|
||||
"""Take over management of this node from a dead conductor.
|
||||
|
@ -748,172 +119,6 @@ class IRMCVirtualMediaAgentDeploy(base.DeployInterface):
|
|||
pass
|
||||
|
||||
|
||||
class VendorPassthru(agent_base_vendor.BaseAgentVendor):
|
||||
"""Vendor-specific interfaces for iRMC deploy drivers."""
|
||||
|
||||
def get_properties(self):
|
||||
return COMMON_PROPERTIES
|
||||
|
||||
def validate(self, task, method, **kwargs):
|
||||
"""Validate vendor-specific actions.
|
||||
|
||||
Checks if a valid vendor passthru method was passed and validates
|
||||
the parameters for the vendor passthru method.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:param method: method to be validated.
|
||||
:param kwargs: kwargs containing the vendor passthru method's
|
||||
parameters.
|
||||
:raises: MissingParameterValue, if some required parameters were not
|
||||
passed.
|
||||
:raises: InvalidParameterValue, if any of the parameters have invalid
|
||||
value.
|
||||
"""
|
||||
if method == 'pass_deploy_info':
|
||||
iscsi_deploy.get_deploy_info(task.node, **kwargs)
|
||||
elif method == 'pass_bootloader_install_info':
|
||||
iscsi_deploy.validate_pass_bootloader_info_input(task, kwargs)
|
||||
|
||||
def _configure_vmedia_boot(self, task, root_uuid_or_disk_id):
|
||||
"""Configure vmedia boot for the node."""
|
||||
node = task.node
|
||||
_prepare_boot_iso(task, root_uuid_or_disk_id)
|
||||
setup_vmedia_for_boot(
|
||||
task, node.driver_internal_info['irmc_boot_iso'])
|
||||
manager_utils.node_set_boot_device(task, boot_devices.CDROM,
|
||||
persistent=True)
|
||||
|
||||
@base.passthru(['POST'])
|
||||
@task_manager.require_exclusive_lock
|
||||
def pass_bootloader_install_info(self, task, **kwargs):
|
||||
"""Accepts the results of bootloader installation.
|
||||
|
||||
This method acts as a vendor passthru and accepts the result of
|
||||
bootloader installation. If the bootloader installation was
|
||||
successful, then it notifies the baremetal to proceed to reboot
|
||||
and makes the instance active. If bootloader installation failed,
|
||||
then it sets provisioning as failed and powers off the node.
|
||||
|
||||
:param task: A TaskManager object.
|
||||
:param kwargs: The arguments sent with vendor passthru. The expected
|
||||
kwargs are::
|
||||
|
||||
'key': The deploy key for authorization
|
||||
'status': 'SUCCEEDED' or 'FAILED'
|
||||
'error': The error message if status == 'FAILED'
|
||||
'address': The IP address of the ramdisk
|
||||
"""
|
||||
LOG.warning(_LW("The node %s is using the bash deploy ramdisk for "
|
||||
"its deployment. This deploy ramdisk has been "
|
||||
"deprecated. Please use the ironic-python-agent "
|
||||
"(IPA) ramdisk instead."), task.node.uuid)
|
||||
task.process_event('resume')
|
||||
iscsi_deploy.validate_bootloader_install_status(task, kwargs)
|
||||
iscsi_deploy.finish_deploy(task, kwargs['address'])
|
||||
|
||||
@base.passthru(['POST'])
|
||||
@task_manager.require_exclusive_lock
|
||||
def pass_deploy_info(self, task, **kwargs):
|
||||
"""Continues the iSCSI deployment from where ramdisk left off.
|
||||
|
||||
This method continues the iSCSI deployment from the conductor node
|
||||
and writes the deploy image to the bare metal's disk. After that,
|
||||
it does the following depending on boot_option for deploy:
|
||||
|
||||
- If the boot_option requested for this deploy is 'local', then it
|
||||
sets the node to boot from disk (ramdisk installs the boot loader
|
||||
present within the image to the bare metal's disk).
|
||||
- If the boot_option requested is 'netboot' or no boot_option is
|
||||
requested, it finds/creates the boot ISO to boot the instance
|
||||
image, attaches the boot ISO to the bare metal and then sets
|
||||
the node to boot from CDROM.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:param kwargs: kwargs containing parameters for iSCSI deployment.
|
||||
:raises: InvalidState
|
||||
"""
|
||||
node = task.node
|
||||
LOG.warning(_LW("The node %s is using the bash deploy ramdisk for "
|
||||
"its deployment. This deploy ramdisk has been "
|
||||
"deprecated. Please use the ironic-python-agent "
|
||||
"(IPA) ramdisk instead."), node.uuid)
|
||||
task.process_event('resume')
|
||||
|
||||
LOG.debug('Continuing iSCSI virtual media deployment on node %s',
|
||||
node.uuid)
|
||||
|
||||
is_whole_disk_image = node.driver_internal_info.get(
|
||||
'is_whole_disk_image')
|
||||
uuid_dict = iscsi_deploy.continue_deploy(task, **kwargs)
|
||||
root_uuid_or_disk_id = uuid_dict.get(
|
||||
'root uuid', uuid_dict.get('disk identifier'))
|
||||
|
||||
try:
|
||||
_cleanup_vmedia_boot(task)
|
||||
if (deploy_utils.get_boot_option(node) == "local" or
|
||||
is_whole_disk_image):
|
||||
manager_utils.node_set_boot_device(task, boot_devices.DISK,
|
||||
persistent=True)
|
||||
|
||||
# Ask the ramdisk to install bootloader and
|
||||
# wait for the call-back through the vendor passthru
|
||||
# 'pass_bootloader_install_info', if it's not a whole
|
||||
# disk image.
|
||||
if not is_whole_disk_image:
|
||||
deploy_utils.notify_ramdisk_to_proceed(kwargs['address'])
|
||||
task.process_event('wait')
|
||||
return
|
||||
|
||||
else:
|
||||
self._configure_vmedia_boot(task, root_uuid_or_disk_id)
|
||||
|
||||
except Exception as e:
|
||||
LOG.exception(_LE('Deploy failed for instance %(instance)s. '
|
||||
'Error: %(error)s'),
|
||||
{'instance': node.instance_uuid, 'error': e})
|
||||
msg = _('Failed to continue iSCSI deployment.')
|
||||
deploy_utils.set_failed_state(task, msg)
|
||||
else:
|
||||
iscsi_deploy.finish_deploy(task, kwargs.get('address'))
|
||||
|
||||
@task_manager.require_exclusive_lock
|
||||
def continue_deploy(self, task, **kwargs):
|
||||
"""Method invoked when deployed with the IPA ramdisk.
|
||||
|
||||
This method is invoked during a heartbeat from an agent when
|
||||
the node is in wait-call-back state. This deploys the image on
|
||||
the node and then configures the node to boot according to the
|
||||
desired boot option (netboot or localboot).
|
||||
|
||||
:param task: a TaskManager object containing the node.
|
||||
:param kwargs: the kwargs passed from the heartbeat method.
|
||||
:raises: InstanceDeployFailure, if it encounters some error during
|
||||
the deploy.
|
||||
"""
|
||||
node = task.node
|
||||
task.process_event('resume')
|
||||
|
||||
LOG.debug('Continuing IPA deployment on node %s', node.uuid)
|
||||
|
||||
is_whole_disk_image = node.driver_internal_info.get(
|
||||
'is_whole_disk_image')
|
||||
_cleanup_vmedia_boot(task)
|
||||
uuid_dict = iscsi_deploy.do_agent_iscsi_deploy(task, self._client)
|
||||
root_uuid = uuid_dict.get('root uuid')
|
||||
|
||||
if (deploy_utils.get_boot_option(node) == "local" or
|
||||
is_whole_disk_image):
|
||||
efi_system_part_uuid = uuid_dict.get(
|
||||
'efi system partition uuid')
|
||||
self.configure_local_boot(
|
||||
task, root_uuid=root_uuid,
|
||||
efi_system_part_uuid=efi_system_part_uuid)
|
||||
else:
|
||||
self._configure_vmedia_boot(task, root_uuid)
|
||||
|
||||
self.reboot_and_finish_deploy(task)
|
||||
|
||||
|
||||
class IRMCVirtualMediaAgentVendorInterface(agent.AgentVendorInterface):
|
||||
"""Interface for vendor passthru related actions."""
|
||||
|
||||
|
@ -922,7 +127,7 @@ class IRMCVirtualMediaAgentVendorInterface(agent.AgentVendorInterface):
|
|||
LOG.debug('Preparing to reboot to instance for node %s',
|
||||
node.uuid)
|
||||
|
||||
_cleanup_vmedia_boot(task)
|
||||
irmc_boot.cleanup_vmedia_boot(task)
|
||||
|
||||
super(IRMCVirtualMediaAgentVendorInterface,
|
||||
self).reboot_to_instance(task, **kwargs)
|
||||
|
|
|
@ -19,17 +19,16 @@ from oslo_config import cfg
|
|||
from oslo_log import log as logging
|
||||
from oslo_utils import importutils
|
||||
|
||||
from ironic.common import boot_devices
|
||||
from ironic.common import exception
|
||||
from ironic.common.i18n import _
|
||||
from ironic.common.i18n import _LE
|
||||
from ironic.common import states
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.conductor import utils as manager_utils
|
||||
from ironic.drivers import base
|
||||
from ironic.drivers.modules import ipmitool
|
||||
from ironic.drivers.modules.irmc import boot as irmc_boot
|
||||
from ironic.drivers.modules.irmc import common as irmc_common
|
||||
from ironic.drivers.modules.irmc import deploy as irmc_deploy
|
||||
|
||||
|
||||
scci = importutils.try_import('scciclient.irmc.scci')
|
||||
|
||||
|
@ -43,28 +42,6 @@ if scci:
|
|||
states.REBOOT: scci.POWER_RESET}
|
||||
|
||||
|
||||
def _attach_boot_iso_if_needed(task):
|
||||
"""Attaches boot ISO for a deployed node if it exists.
|
||||
|
||||
This method checks the instance info of the bare metal node for a
|
||||
boot ISO. If the instance info has a value of key 'irmc_boot_iso',
|
||||
it indicates that 'boot_option' is 'netboot'. Threfore it attaches
|
||||
the boot ISO on the bare metal node and then sets the node to boot from
|
||||
virtual media cdrom.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:raises: IRMCOperationError if attaching virtual media failed.
|
||||
:raises: InvalidParameterValue if the validation of the
|
||||
ManagementInterface fails.
|
||||
"""
|
||||
d_info = task.node.driver_internal_info
|
||||
node_state = task.node.provision_state
|
||||
|
||||
if 'irmc_boot_iso' in d_info and node_state == states.ACTIVE:
|
||||
irmc_deploy.setup_vmedia_for_boot(task, d_info['irmc_boot_iso'])
|
||||
manager_utils.node_set_boot_device(task, boot_devices.CDROM)
|
||||
|
||||
|
||||
def _set_power_state(task, target_state):
|
||||
"""Turns the server power on/off or do a reboot.
|
||||
|
||||
|
@ -80,7 +57,7 @@ def _set_power_state(task, target_state):
|
|||
irmc_client = irmc_common.get_irmc_client(node)
|
||||
|
||||
if target_state in (states.POWER_ON, states.REBOOT):
|
||||
_attach_boot_iso_if_needed(task)
|
||||
irmc_boot.attach_boot_iso_if_needed(task)
|
||||
|
||||
try:
|
||||
irmc_client(STATES_MAP[target_state])
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -19,13 +19,11 @@ Test class for iRMC Power Driver
|
|||
import mock
|
||||
from oslo_config import cfg
|
||||
|
||||
from ironic.common import boot_devices
|
||||
from ironic.common import exception
|
||||
from ironic.common import states
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.conductor import utils as manager_utils
|
||||
from ironic.drivers.modules.irmc import boot as irmc_boot
|
||||
from ironic.drivers.modules.irmc import common as irmc_common
|
||||
from ironic.drivers.modules.irmc import deploy as irmc_deploy
|
||||
from ironic.drivers.modules.irmc import power as irmc_power
|
||||
from ironic.tests.unit.conductor import mgr_utils
|
||||
from ironic.tests.unit.db import base as db_base
|
||||
|
@ -49,17 +47,17 @@ class IRMCPowerInternalMethodsTestCase(db_base.DbTestCase):
|
|||
driver_info=driver_info,
|
||||
instance_uuid='instance_uuid_123')
|
||||
|
||||
@mock.patch.object(irmc_power, '_attach_boot_iso_if_needed')
|
||||
@mock.patch.object(irmc_boot, 'attach_boot_iso_if_needed')
|
||||
def test__set_power_state_power_on_ok(
|
||||
self,
|
||||
_attach_boot_iso_if_needed_mock,
|
||||
attach_boot_iso_if_needed_mock,
|
||||
get_irmc_client_mock):
|
||||
irmc_client = get_irmc_client_mock.return_value
|
||||
target_state = states.POWER_ON
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
irmc_power._set_power_state(task, target_state)
|
||||
_attach_boot_iso_if_needed_mock.assert_called_once_with(task)
|
||||
attach_boot_iso_if_needed_mock.assert_called_once_with(task)
|
||||
irmc_client.assert_called_once_with(irmc_power.scci.POWER_ON)
|
||||
|
||||
def test__set_power_state_power_off_ok(self,
|
||||
|
@ -71,17 +69,17 @@ class IRMCPowerInternalMethodsTestCase(db_base.DbTestCase):
|
|||
irmc_power._set_power_state(task, target_state)
|
||||
irmc_client.assert_called_once_with(irmc_power.scci.POWER_OFF)
|
||||
|
||||
@mock.patch.object(irmc_power, '_attach_boot_iso_if_needed')
|
||||
@mock.patch.object(irmc_boot, 'attach_boot_iso_if_needed')
|
||||
def test__set_power_state_power_reboot_ok(
|
||||
self,
|
||||
_attach_boot_iso_if_needed_mock,
|
||||
attach_boot_iso_if_needed_mock,
|
||||
get_irmc_client_mock):
|
||||
irmc_client = get_irmc_client_mock.return_value
|
||||
target_state = states.REBOOT
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
irmc_power._set_power_state(task, target_state)
|
||||
_attach_boot_iso_if_needed_mock.assert_called_once_with(task)
|
||||
attach_boot_iso_if_needed_mock.assert_called_once_with(task)
|
||||
irmc_client.assert_called_once_with(irmc_power.scci.POWER_RESET)
|
||||
|
||||
def test__set_power_state_invalid_target_state(self,
|
||||
|
@ -106,41 +104,6 @@ class IRMCPowerInternalMethodsTestCase(db_base.DbTestCase):
|
|||
task,
|
||||
states.POWER_ON)
|
||||
|
||||
@mock.patch.object(manager_utils, 'node_set_boot_device', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(irmc_deploy, 'setup_vmedia_for_boot', spec_set=True,
|
||||
autospec=True)
|
||||
def test__attach_boot_iso_if_needed(
|
||||
self,
|
||||
setup_vmedia_mock,
|
||||
set_boot_device_mock,
|
||||
get_irmc_client_mock):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
task.node.provision_state = states.ACTIVE
|
||||
task.node.driver_internal_info['irmc_boot_iso'] = 'boot-iso'
|
||||
irmc_power._attach_boot_iso_if_needed(task)
|
||||
setup_vmedia_mock.assert_called_once_with(task, 'boot-iso')
|
||||
set_boot_device_mock.assert_called_once_with(
|
||||
task, boot_devices.CDROM)
|
||||
|
||||
@mock.patch.object(manager_utils, 'node_set_boot_device', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(irmc_deploy, 'setup_vmedia_for_boot', spec_set=True,
|
||||
autospec=True)
|
||||
def test__attach_boot_iso_if_needed_on_rebuild(
|
||||
self,
|
||||
setup_vmedia_mock,
|
||||
set_boot_device_mock,
|
||||
get_irmc_client_mock):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
task.node.provision_state = states.DEPLOYING
|
||||
task.node.driver_internal_info['irmc_boot_iso'] = 'boot-iso'
|
||||
irmc_power._attach_boot_iso_if_needed(task)
|
||||
self.assertFalse(setup_vmedia_mock.called)
|
||||
self.assertFalse(set_boot_device_mock.called)
|
||||
|
||||
|
||||
class IRMCPowerTestCase(db_base.DbTestCase):
|
||||
def setUp(self):
|
||||
|
|
|
@ -21,13 +21,14 @@ import testtools
|
|||
|
||||
from ironic.common import exception
|
||||
from ironic.drivers import irmc
|
||||
from ironic.drivers.modules import iscsi_deploy
|
||||
|
||||
|
||||
class IRMCVirtualMediaIscsiTestCase(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
irmc.deploy._check_share_fs_mounted_patcher.start()
|
||||
self.addCleanup(irmc.deploy._check_share_fs_mounted_patcher.stop)
|
||||
irmc.boot.check_share_fs_mounted_patcher.start()
|
||||
self.addCleanup(irmc.boot.check_share_fs_mounted_patcher.stop)
|
||||
super(IRMCVirtualMediaIscsiTestCase, self).setUp()
|
||||
|
||||
@mock.patch.object(irmc.importutils, 'try_import', spec_set=True,
|
||||
|
@ -39,13 +40,14 @@ class IRMCVirtualMediaIscsiTestCase(testtools.TestCase):
|
|||
driver = irmc.IRMCVirtualMediaIscsiDriver()
|
||||
|
||||
self.assertIsInstance(driver.power, irmc.power.IRMCPower)
|
||||
self.assertIsInstance(driver.deploy,
|
||||
irmc.deploy.IRMCVirtualMediaIscsiDeploy)
|
||||
self.assertIsInstance(driver.boot,
|
||||
irmc.boot.IRMCVirtualMediaBoot)
|
||||
self.assertIsInstance(driver.deploy, iscsi_deploy.ISCSIDeploy)
|
||||
self.assertIsInstance(driver.console,
|
||||
irmc.ipmitool.IPMIShellinaboxConsole)
|
||||
self.assertIsInstance(driver.management,
|
||||
irmc.management.IRMCManagement)
|
||||
self.assertIsInstance(driver.vendor, irmc.deploy.VendorPassthru)
|
||||
self.assertIsInstance(driver.vendor, iscsi_deploy.VendorPassthru)
|
||||
|
||||
@mock.patch.object(irmc.importutils, 'try_import')
|
||||
def test___init___try_import_exception(self, mock_try_import):
|
||||
|
@ -54,7 +56,7 @@ class IRMCVirtualMediaIscsiTestCase(testtools.TestCase):
|
|||
self.assertRaises(exception.DriverLoadError,
|
||||
irmc.IRMCVirtualMediaIscsiDriver)
|
||||
|
||||
@mock.patch.object(irmc.deploy.IRMCVirtualMediaIscsiDeploy, '__init__',
|
||||
@mock.patch.object(irmc.boot.IRMCVirtualMediaBoot, '__init__',
|
||||
spec_set=True, autospec=True)
|
||||
def test___init___share_fs_not_mounted_exception(self, __init___mock):
|
||||
__init___mock.side_effect = iter(
|
||||
|
@ -67,8 +69,8 @@ class IRMCVirtualMediaIscsiTestCase(testtools.TestCase):
|
|||
class IRMCVirtualMediaAgentTestCase(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
irmc.deploy._check_share_fs_mounted_patcher.start()
|
||||
self.addCleanup(irmc.deploy._check_share_fs_mounted_patcher.stop)
|
||||
irmc.boot.check_share_fs_mounted_patcher.start()
|
||||
self.addCleanup(irmc.boot.check_share_fs_mounted_patcher.stop)
|
||||
super(IRMCVirtualMediaAgentTestCase, self).setUp()
|
||||
|
||||
@mock.patch.object(irmc.importutils, 'try_import', spec_set=True,
|
||||
|
|
|
@ -204,12 +204,12 @@ if 'ironic.drivers.modules.irmc' in sys.modules:
|
|||
|
||||
# install mock object to prevent 'iscsi_irmc' and 'agent_irmc' from
|
||||
# checking whether NFS/CIFS share file system is mounted or not.
|
||||
irmc_deploy = importutils.import_module(
|
||||
'ironic.drivers.modules.irmc.deploy')
|
||||
irmc_deploy._check_share_fs_mounted_orig = irmc_deploy._check_share_fs_mounted
|
||||
irmc_deploy._check_share_fs_mounted_patcher = mock.patch(
|
||||
'ironic.drivers.modules.irmc.deploy._check_share_fs_mounted')
|
||||
irmc_deploy._check_share_fs_mounted_patcher.return_value = None
|
||||
irmc_boot = importutils.import_module(
|
||||
'ironic.drivers.modules.irmc.boot')
|
||||
irmc_boot.check_share_fs_mounted_orig = irmc_boot.check_share_fs_mounted
|
||||
irmc_boot.check_share_fs_mounted_patcher = mock.patch(
|
||||
'ironic.drivers.modules.irmc.boot.check_share_fs_mounted')
|
||||
irmc_boot.check_share_fs_mounted_patcher.return_value = None
|
||||
|
||||
|
||||
pyremotevbox = importutils.try_import('pyremotevbox')
|
||||
|
|
Loading…
Reference in New Issue