Merge "Remove the hard dependency of swift from ilo drivers"
This commit is contained in:
commit
ed8db121d7
|
@ -1,14 +1,5 @@
|
|||
[DEFAULT]
|
||||
|
||||
#
|
||||
# Options defined in oslo.service.periodic_task
|
||||
#
|
||||
|
||||
# Some periodic tasks can be run in a separate process. Should
|
||||
# we run them here? (boolean value)
|
||||
#run_external_periodic_tasks=true
|
||||
|
||||
|
||||
#
|
||||
# Options defined in oslo.service.service
|
||||
#
|
||||
|
@ -29,77 +20,12 @@
|
|||
|
||||
|
||||
#
|
||||
# Options defined in oslo.messaging
|
||||
# Options defined in oslo.service.periodic_task
|
||||
#
|
||||
|
||||
# Size of RPC connection pool. (integer value)
|
||||
#rpc_conn_pool_size=30
|
||||
|
||||
# ZeroMQ bind address. Should be a wildcard (*), an ethernet
|
||||
# interface, or IP. The "host" option should point or resolve
|
||||
# to this address. (string value)
|
||||
#rpc_zmq_bind_address=*
|
||||
|
||||
# MatchMaker driver. (string value)
|
||||
#rpc_zmq_matchmaker=local
|
||||
|
||||
# ZeroMQ receiver listening port. (integer value)
|
||||
#rpc_zmq_port=9501
|
||||
|
||||
# Number of ZeroMQ contexts, defaults to 1. (integer value)
|
||||
#rpc_zmq_contexts=1
|
||||
|
||||
# Maximum number of ingress messages to locally buffer per
|
||||
# topic. Default is unlimited. (integer value)
|
||||
#rpc_zmq_topic_backlog=<None>
|
||||
|
||||
# Directory for holding IPC sockets. (string value)
|
||||
#rpc_zmq_ipc_dir=/var/run/openstack
|
||||
|
||||
# Name of this node. Must be a valid hostname, FQDN, or IP
|
||||
# address. Must match "host" option, if running Nova. (string
|
||||
# value)
|
||||
#rpc_zmq_host=ironic
|
||||
|
||||
# Seconds to wait before a cast expires (TTL). Only supported
|
||||
# by impl_zmq. (integer value)
|
||||
#rpc_cast_timeout=30
|
||||
|
||||
# Heartbeat frequency. (integer value)
|
||||
#matchmaker_heartbeat_freq=300
|
||||
|
||||
# Heartbeat time-to-live. (integer value)
|
||||
#matchmaker_heartbeat_ttl=600
|
||||
|
||||
# Size of executor thread pool. (integer value)
|
||||
# Deprecated group/name - [DEFAULT]/rpc_thread_pool_size
|
||||
#executor_thread_pool_size=64
|
||||
|
||||
# The Drivers(s) to handle sending notifications. Possible
|
||||
# values are messaging, messagingv2, routing, log, test, noop
|
||||
# (multi valued)
|
||||
#notification_driver=
|
||||
|
||||
# AMQP topic used for OpenStack notifications. (list value)
|
||||
# Deprecated group/name - [rpc_notifier2]/topics
|
||||
#notification_topics=notifications
|
||||
|
||||
# Seconds to wait for a response from a call. (integer value)
|
||||
#rpc_response_timeout=60
|
||||
|
||||
# A URL representing the messaging driver to use and its full
|
||||
# configuration. If not set, we fall back to the rpc_backend
|
||||
# option and driver specific configuration. (string value)
|
||||
#transport_url=<None>
|
||||
|
||||
# The messaging driver to use, defaults to rabbit. Other
|
||||
# drivers include qpid and zmq. (string value)
|
||||
#rpc_backend=rabbit
|
||||
|
||||
# The default exchange under which topics are scoped. May be
|
||||
# overridden by an exchange name specified in the
|
||||
# transport_url option. (string value)
|
||||
#control_exchange=openstack
|
||||
# Some periodic tasks can be run in a separate process. Should
|
||||
# we run them here? (boolean value)
|
||||
#run_external_periodic_tasks=true
|
||||
|
||||
|
||||
#
|
||||
|
@ -195,6 +121,80 @@
|
|||
#fatal_deprecations=false
|
||||
|
||||
|
||||
#
|
||||
# Options defined in oslo.messaging
|
||||
#
|
||||
|
||||
# Size of RPC connection pool. (integer value)
|
||||
#rpc_conn_pool_size=30
|
||||
|
||||
# ZeroMQ bind address. Should be a wildcard (*), an ethernet
|
||||
# interface, or IP. The "host" option should point or resolve
|
||||
# to this address. (string value)
|
||||
#rpc_zmq_bind_address=*
|
||||
|
||||
# MatchMaker driver. (string value)
|
||||
#rpc_zmq_matchmaker=local
|
||||
|
||||
# ZeroMQ receiver listening port. (integer value)
|
||||
#rpc_zmq_port=9501
|
||||
|
||||
# Number of ZeroMQ contexts, defaults to 1. (integer value)
|
||||
#rpc_zmq_contexts=1
|
||||
|
||||
# Maximum number of ingress messages to locally buffer per
|
||||
# topic. Default is unlimited. (integer value)
|
||||
#rpc_zmq_topic_backlog=<None>
|
||||
|
||||
# Directory for holding IPC sockets. (string value)
|
||||
#rpc_zmq_ipc_dir=/var/run/openstack
|
||||
|
||||
# Name of this node. Must be a valid hostname, FQDN, or IP
|
||||
# address. Must match "host" option, if running Nova. (string
|
||||
# value)
|
||||
#rpc_zmq_host=ironic
|
||||
|
||||
# Seconds to wait before a cast expires (TTL). Only supported
|
||||
# by impl_zmq. (integer value)
|
||||
#rpc_cast_timeout=30
|
||||
|
||||
# Heartbeat frequency. (integer value)
|
||||
#matchmaker_heartbeat_freq=300
|
||||
|
||||
# Heartbeat time-to-live. (integer value)
|
||||
#matchmaker_heartbeat_ttl=600
|
||||
|
||||
# Size of executor thread pool. (integer value)
|
||||
# Deprecated group/name - [DEFAULT]/rpc_thread_pool_size
|
||||
#executor_thread_pool_size=64
|
||||
|
||||
# The Drivers(s) to handle sending notifications. Possible
|
||||
# values are messaging, messagingv2, routing, log, test, noop
|
||||
# (multi valued)
|
||||
#notification_driver=
|
||||
|
||||
# AMQP topic used for OpenStack notifications. (list value)
|
||||
# Deprecated group/name - [rpc_notifier2]/topics
|
||||
#notification_topics=notifications
|
||||
|
||||
# Seconds to wait for a response from a call. (integer value)
|
||||
#rpc_response_timeout=60
|
||||
|
||||
# A URL representing the messaging driver to use and its full
|
||||
# configuration. If not set, we fall back to the rpc_backend
|
||||
# option and driver specific configuration. (string value)
|
||||
#transport_url=<None>
|
||||
|
||||
# The messaging driver to use, defaults to rabbit. Other
|
||||
# drivers include qpid and zmq. (string value)
|
||||
#rpc_backend=rabbit
|
||||
|
||||
# The default exchange under which topics are scoped. May be
|
||||
# overridden by an exchange name specified in the
|
||||
# transport_url option. (string value)
|
||||
#control_exchange=openstack
|
||||
|
||||
|
||||
#
|
||||
# Options defined in ironic.netconf
|
||||
#
|
||||
|
@ -979,6 +979,14 @@
|
|||
# (integer value)
|
||||
#swift_object_expiry_timeout=900
|
||||
|
||||
# Set this to True to use http web server to host floppy
|
||||
# images and generated boot ISO. This requires http_root and
|
||||
# http_url to be configured in the [deploy] section of the
|
||||
# config file. If this is set to False, then Ironic will use
|
||||
# Swift to host the floppy images and generated boot_iso.
|
||||
# (boolean value)
|
||||
#use_web_server_for_images=false
|
||||
|
||||
|
||||
#
|
||||
# Options defined in ironic.drivers.modules.ilo.deploy
|
||||
|
@ -1176,6 +1184,10 @@
|
|||
# Verify HTTPS connections. (boolean value)
|
||||
#insecure=false
|
||||
|
||||
# The region in which the identity server can be found.
|
||||
# (string value)
|
||||
#region_name=<None>
|
||||
|
||||
# Directory used to cache files related to PKI tokens. (string
|
||||
# value)
|
||||
#signing_dir=<None>
|
||||
|
|
|
@ -595,3 +595,8 @@ class UcsConnectionError(IronicException):
|
|||
|
||||
class WolOperationError(IronicException):
|
||||
pass
|
||||
|
||||
|
||||
class ImageUploadFailed(IronicException):
|
||||
message = _("Failed to upload %(image_name)s image to web server "
|
||||
"%(web_server)s, reason: %(reason)s")
|
||||
|
|
|
@ -440,7 +440,7 @@ def get_temp_url_for_glance_image(context, image_uuid):
|
|||
|
||||
|
||||
def create_boot_iso(context, output_filename, kernel_href,
|
||||
ramdisk_href, deploy_iso_uuid, root_uuid=None,
|
||||
ramdisk_href, deploy_iso_href, root_uuid=None,
|
||||
kernel_params=None, boot_mode=None):
|
||||
"""Creates a bootable ISO image for a node.
|
||||
|
||||
|
@ -453,7 +453,7 @@ def create_boot_iso(context, output_filename, kernel_href,
|
|||
:param output_filename: the absolute path of the output ISO file
|
||||
:param kernel_href: URL or glance uuid of the kernel to use
|
||||
:param ramdisk_href: URL or glance uuid of the ramdisk to use
|
||||
:param deploy_iso_uuid: URL or glance uuid of the deploy iso used
|
||||
:param deploy_iso_href: URL or glance uuid of the deploy iso used
|
||||
:param root_uuid: uuid of the root filesystem (optional)
|
||||
:param kernel_params: a string containing whitespace separated values
|
||||
kernel cmdline arguments of the form K=V or K (optional).
|
||||
|
@ -473,8 +473,8 @@ def create_boot_iso(context, output_filename, kernel_href,
|
|||
params.append(kernel_params)
|
||||
|
||||
if boot_mode == 'uefi':
|
||||
deploy_iso = os.path.join(tmpdir, deploy_iso_uuid)
|
||||
fetch(context, deploy_iso_uuid, deploy_iso)
|
||||
deploy_iso = os.path.join(tmpdir, deploy_iso_href.split('/')[-1])
|
||||
fetch(context, deploy_iso_href, deploy_iso)
|
||||
create_isolinux_image_for_uefi(output_filename,
|
||||
deploy_iso,
|
||||
kernel_path,
|
||||
|
|
|
@ -16,12 +16,15 @@
|
|||
Common functionalities shared between different iLO modules.
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import importutils
|
||||
import six.moves.urllib.parse as urlparse
|
||||
from six.moves.urllib.parse import urljoin
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.common.glance_service import service_utils
|
||||
|
@ -30,6 +33,7 @@ from ironic.common.i18n import _LE
|
|||
from ironic.common.i18n import _LI
|
||||
from ironic.common import images
|
||||
from ironic.common import swift
|
||||
from ironic.common import utils
|
||||
from ironic.drivers.modules import deploy_utils
|
||||
|
||||
ilo_client = importutils.try_import('proliantutils.ilo.client')
|
||||
|
@ -54,6 +58,15 @@ opts = [
|
|||
default=900,
|
||||
help=_('Amount of time in seconds for Swift objects to '
|
||||
'auto-expire.')),
|
||||
cfg.BoolOpt('use_web_server_for_images',
|
||||
default=False,
|
||||
help=_('Set this to True to use http web server to host '
|
||||
'floppy images and generated boot ISO. This '
|
||||
'requires http_root and http_url to be configured '
|
||||
'in the [deploy] section of the config file. If this '
|
||||
'is set to False, then Ironic will use Swift '
|
||||
'to host the floppy images and generated '
|
||||
'boot_iso.')),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -89,6 +102,36 @@ BOOT_MODE_ILO_TO_GENERIC = dict(
|
|||
(v, k) for (k, v) in BOOT_MODE_GENERIC_TO_ILO.items())
|
||||
|
||||
|
||||
def copy_image_to_web_server(source_file_path, destination):
|
||||
"""Copies the given image to the http web server.
|
||||
|
||||
This method copies the given image to the http_root location.
|
||||
It enables read-write access to the image else the deploy fails
|
||||
as the image file at the web_server url is inaccessible.
|
||||
|
||||
:param source_file_path: The absolute path of the image file
|
||||
which needs to be copied to the
|
||||
web server root.
|
||||
:param destination: The name of the file that
|
||||
will contain the copied image.
|
||||
:raises: ImageUploadFailed exception if copying the source
|
||||
file to the web server fails.
|
||||
:returns: image url after the source image is uploaded.
|
||||
|
||||
"""
|
||||
|
||||
image_url = urljoin(CONF.deploy.http_url, destination)
|
||||
image_path = os.path.join(CONF.deploy.http_root, destination)
|
||||
try:
|
||||
shutil.copyfile(source_file_path, image_path)
|
||||
except IOError as exc:
|
||||
raise exception.ImageUploadFailed(image_name=destination,
|
||||
web_server=CONF.deploy.http_url,
|
||||
reason=exc)
|
||||
os.chmod(image_path, 0o644)
|
||||
return image_url
|
||||
|
||||
|
||||
def parse_driver_info(node):
|
||||
"""Gets the driver specific Node info.
|
||||
|
||||
|
@ -228,10 +271,11 @@ def _prepare_floppy_image(task, params):
|
|||
|
||||
This method prepares a temporary vfat filesystem image. Then it adds
|
||||
a file into the image which contains the parameters to be passed to
|
||||
the ramdisk. After adding the parameters, it then uploads the file to Swift
|
||||
in 'swift_ilo_container', setting it to auto-expire after
|
||||
'swift_object_expiry_timeout' seconds. Then it returns the temp url for the
|
||||
Swift object.
|
||||
the ramdisk. After adding the parameters, it then uploads the file either
|
||||
to Swift in 'swift_ilo_container', setting it to auto-expire after
|
||||
'swift_object_expiry_timeout' seconds or in web server. Then it returns
|
||||
the temp url for the Swift object or the http url for the uploaded floppy
|
||||
image depending upon value of CONF.ilo.use_web_server_for_images.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:param params: a dictionary containing 'parameter name'->'value' mapping
|
||||
|
@ -245,22 +289,38 @@ def _prepare_floppy_image(task, params):
|
|||
|
||||
vfat_image_tmpfile = vfat_image_tmpfile_obj.name
|
||||
images.create_vfat_image(vfat_image_tmpfile, parameters=params)
|
||||
|
||||
container = CONF.ilo.swift_ilo_container
|
||||
object_name = _get_floppy_image_name(task.node)
|
||||
timeout = CONF.ilo.swift_object_expiry_timeout
|
||||
if CONF.ilo.use_web_server_for_images:
|
||||
image_url = copy_image_to_web_server(vfat_image_tmpfile,
|
||||
object_name)
|
||||
return image_url
|
||||
else:
|
||||
container = CONF.ilo.swift_ilo_container
|
||||
timeout = CONF.ilo.swift_object_expiry_timeout
|
||||
|
||||
object_headers = {'X-Delete-After': timeout}
|
||||
swift_api = swift.SwiftAPI()
|
||||
swift_api.create_object(container, object_name,
|
||||
vfat_image_tmpfile,
|
||||
object_headers=object_headers)
|
||||
temp_url = swift_api.get_temp_url(container, object_name, timeout)
|
||||
object_headers = {'X-Delete-After': timeout}
|
||||
swift_api = swift.SwiftAPI()
|
||||
swift_api.create_object(container, object_name,
|
||||
vfat_image_tmpfile,
|
||||
object_headers=object_headers)
|
||||
temp_url = swift_api.get_temp_url(container, object_name, timeout)
|
||||
|
||||
LOG.debug("Uploaded floppy image %(object_name)s to %(container)s "
|
||||
"for deployment.",
|
||||
{'object_name': object_name, 'container': container})
|
||||
return temp_url
|
||||
LOG.debug("Uploaded floppy image %(object_name)s to %(container)s "
|
||||
"for deployment.",
|
||||
{'object_name': object_name, 'container': container})
|
||||
return temp_url
|
||||
|
||||
|
||||
def destroy_floppy_image_from_web_server(node):
|
||||
"""Removes the temporary floppy image.
|
||||
|
||||
It removes the floppy image created for deploy.
|
||||
:param node: an ironic node object.
|
||||
"""
|
||||
|
||||
object_name = _get_floppy_image_name(node)
|
||||
image_path = os.path.join(CONF.deploy.http_root, object_name)
|
||||
utils.unlink_without_raise(image_path)
|
||||
|
||||
|
||||
def attach_vmedia(node, device, url):
|
||||
|
@ -394,7 +454,6 @@ def setup_vmedia_for_boot(task, boot_iso, parameters=None):
|
|||
"""
|
||||
LOG.info(_LI("Setting up node %s to boot from virtual media"),
|
||||
task.node.uuid)
|
||||
|
||||
if parameters:
|
||||
floppy_image_temp_url = _prepare_floppy_image(task, parameters)
|
||||
attach_vmedia(task.node, 'FLOPPY', floppy_image_temp_url)
|
||||
|
@ -411,7 +470,6 @@ def setup_vmedia_for_boot(task, boot_iso, parameters=None):
|
|||
elif service_utils.is_glance_image(boot_iso):
|
||||
boot_iso_url = (
|
||||
images.get_temp_url_for_glance_image(task.context, boot_iso))
|
||||
|
||||
attach_vmedia(task.node, 'CDROM', boot_iso_url or boot_iso)
|
||||
|
||||
|
||||
|
@ -443,23 +501,26 @@ 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 the
|
||||
floppy image if it exists in CONF.ilo.swift_ilo_container. It also
|
||||
ejects both virtual media cdrom and virtual media floppy.
|
||||
floppy image if it exists in CONF.ilo.swift_ilo_container or web server.
|
||||
It also ejects both virtual media cdrom and virtual media floppy.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
"""
|
||||
LOG.debug("Cleaning up node %s after virtual media boot", task.node.uuid)
|
||||
|
||||
container = CONF.ilo.swift_ilo_container
|
||||
object_name = _get_floppy_image_name(task.node)
|
||||
try:
|
||||
swift_api = swift.SwiftAPI()
|
||||
swift_api.delete_object(container, object_name)
|
||||
except exception.SwiftOperationError as e:
|
||||
LOG.exception(_LE("Error while deleting %(object_name)s from "
|
||||
"%(container)s. Error: %(error)s"),
|
||||
{'object_name': object_name, 'container': container,
|
||||
'error': e})
|
||||
if not CONF.ilo.use_web_server_for_images:
|
||||
container = CONF.ilo.swift_ilo_container
|
||||
object_name = _get_floppy_image_name(task.node)
|
||||
try:
|
||||
swift_api = swift.SwiftAPI()
|
||||
swift_api.delete_object(container, object_name)
|
||||
except exception.SwiftOperationError as e:
|
||||
LOG.exception(_LE("Error while deleting %(object_name)s from "
|
||||
"%(container)s. Error: %(error)s"),
|
||||
{'object_name': object_name, 'container': container,
|
||||
'error': e})
|
||||
else:
|
||||
destroy_floppy_image_from_web_server(task.node)
|
||||
eject_vmedia_devices(task)
|
||||
|
||||
|
||||
|
|
|
@ -15,11 +15,13 @@
|
|||
iLO Deploy Driver(s) and supporting methods.
|
||||
"""
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
import six.moves.urllib.parse as urlparse
|
||||
|
||||
from ironic.common import boot_devices
|
||||
from ironic.common import dhcp_factory
|
||||
|
@ -33,6 +35,7 @@ from ironic.common import image_service
|
|||
from ironic.common import images
|
||||
from ironic.common import states
|
||||
from ironic.common import swift
|
||||
from ironic.common import utils
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.conductor import utils as manager_utils
|
||||
from ironic.drivers import base
|
||||
|
@ -122,6 +125,7 @@ def _get_boot_iso(task, root_uuid):
|
|||
"instance_info['ilo_boot_iso']. Either %s "
|
||||
"is not a valid HTTP(S) URL or is "
|
||||
"not reachable."), boot_iso)
|
||||
|
||||
return task.node.instance_info['ilo_boot_iso']
|
||||
|
||||
# Option 2 - Check if user has provided a boot_iso in Glance. If boot_iso
|
||||
|
@ -156,45 +160,59 @@ def _get_boot_iso(task, root_uuid):
|
|||
# ISO. Such a synchronisation mechanism doesn't exist in ironic as of now.
|
||||
|
||||
# Option 3 - Create boot_iso from kernel/ramdisk, upload to Swift
|
||||
# and provide its name.
|
||||
# or web server and provide its name.
|
||||
deploy_iso_uuid = deploy_info['ilo_deploy_iso']
|
||||
boot_mode = deploy_utils.get_boot_mode_for_deploy(task.node)
|
||||
boot_iso_object_name = _get_boot_iso_object_name(task.node)
|
||||
kernel_params = CONF.pxe.pxe_append_params
|
||||
container = CONF.ilo.swift_ilo_container
|
||||
|
||||
with tempfile.NamedTemporaryFile(dir=CONF.tempdir) as fileobj:
|
||||
boot_iso_tmp_file = fileobj.name
|
||||
images.create_boot_iso(task.context, boot_iso_tmp_file,
|
||||
kernel_href, ramdisk_href,
|
||||
deploy_iso_uuid, root_uuid,
|
||||
kernel_params, boot_mode)
|
||||
swift_api = swift.SwiftAPI()
|
||||
swift_api.create_object(container, boot_iso_object_name,
|
||||
boot_iso_tmp_file)
|
||||
|
||||
LOG.debug("Created boot_iso %s in Swift", boot_iso_object_name)
|
||||
if CONF.ilo.use_web_server_for_images:
|
||||
boot_iso_url = (
|
||||
ilo_common.copy_image_to_web_server(boot_iso_tmp_file,
|
||||
boot_iso_object_name))
|
||||
LOG.debug("Created boot_iso %(boot_iso)s for node %(node)s",
|
||||
{'boot_iso': boot_iso_url, 'node': task.node.uuid})
|
||||
return boot_iso_url
|
||||
else:
|
||||
container = CONF.ilo.swift_ilo_container
|
||||
swift_api = swift.SwiftAPI()
|
||||
swift_api.create_object(container, boot_iso_object_name,
|
||||
boot_iso_tmp_file)
|
||||
|
||||
return 'swift:%s' % boot_iso_object_name
|
||||
LOG.debug("Created boot_iso %s in Swift", boot_iso_object_name)
|
||||
return 'swift:%s' % boot_iso_object_name
|
||||
|
||||
|
||||
def _clean_up_boot_iso_for_instance(node):
|
||||
"""Deletes the boot ISO if it was created in Swift for the instance.
|
||||
"""Deletes the boot ISO if it was created for the instance.
|
||||
|
||||
:param node: an ironic node object.
|
||||
"""
|
||||
ilo_boot_iso = node.instance_info.get('ilo_boot_iso')
|
||||
if not (ilo_boot_iso and ilo_boot_iso.startswith('swift')):
|
||||
if not ilo_boot_iso:
|
||||
return
|
||||
swift_api = swift.SwiftAPI()
|
||||
container = CONF.ilo.swift_ilo_container
|
||||
boot_iso_object_name = _get_boot_iso_object_name(node)
|
||||
try:
|
||||
swift_api.delete_object(container, boot_iso_object_name)
|
||||
except exception.SwiftOperationError as e:
|
||||
LOG.exception(_LE("Failed to clean up boot ISO for %(node)s."
|
||||
"Error: %(error)s."),
|
||||
{'node': node.uuid, 'error': e})
|
||||
if ilo_boot_iso.startswith('swift'):
|
||||
swift_api = swift.SwiftAPI()
|
||||
container = CONF.ilo.swift_ilo_container
|
||||
boot_iso_object_name = _get_boot_iso_object_name(node)
|
||||
try:
|
||||
swift_api.delete_object(container, boot_iso_object_name)
|
||||
except exception.SwiftOperationError as e:
|
||||
LOG.exception(_LE("Failed to clean up boot ISO for node "
|
||||
"%(node)s. Error: %(error)s."),
|
||||
{'node': node.uuid, 'error': e})
|
||||
elif CONF.ilo.use_web_server_for_images:
|
||||
result = urlparse.urlparse(ilo_boot_iso)
|
||||
ilo_boot_iso_name = os.path.basename(result.path)
|
||||
boot_iso_path = os.path.join(
|
||||
CONF.deploy.http_root, ilo_boot_iso_name)
|
||||
utils.unlink_without_raise(boot_iso_path)
|
||||
|
||||
|
||||
def _parse_driver_info(node):
|
||||
|
@ -491,7 +509,10 @@ class IloVirtualMediaIscsiDeploy(base.DeployInterface):
|
|||
:param task: a TaskManager instance containing the node to act on.
|
||||
"""
|
||||
_clean_up_boot_iso_for_instance(task.node)
|
||||
iscsi_deploy.destroy_images(task.node.uuid)
|
||||
if not CONF.ilo.use_web_server_for_images:
|
||||
iscsi_deploy.destroy_images(task.node.uuid)
|
||||
else:
|
||||
ilo_common.destroy_floppy_image_from_web_server(task.node)
|
||||
|
||||
def take_over(self, task):
|
||||
pass
|
||||
|
@ -965,7 +986,6 @@ class VendorPassthru(agent_base_vendor.BaseAgentVendor):
|
|||
task.process_event('resume')
|
||||
node = task.node
|
||||
LOG.debug('Continuing the deployment on node %s', node.uuid)
|
||||
|
||||
ilo_common.cleanup_vmedia_boot(task)
|
||||
|
||||
iwdi = node.driver_internal_info.get('is_whole_disk_image')
|
||||
|
|
|
@ -725,6 +725,30 @@ class FsImageTestCase(base.TestCase):
|
|||
'output_file', 'tmpdir/deploy_iso-uuid', 'tmpdir/kernel-uuid',
|
||||
'tmpdir/ramdisk-uuid', params)
|
||||
|
||||
@mock.patch.object(images, 'create_isolinux_image_for_uefi', autospec=True)
|
||||
@mock.patch.object(images, 'fetch', autospec=True)
|
||||
@mock.patch.object(utils, 'tempdir', autospec=True)
|
||||
def test_create_boot_iso_for_uefi_for_hrefs(
|
||||
self, tempdir_mock, fetch_images_mock, create_isolinux_mock):
|
||||
mock_file_handle = mock.MagicMock(spec=file)
|
||||
mock_file_handle.__enter__.return_value = 'tmpdir'
|
||||
tempdir_mock.return_value = mock_file_handle
|
||||
|
||||
images.create_boot_iso('ctx', 'output_file', 'http://kernel-href',
|
||||
'http://ramdisk-href', 'http://deploy_iso-href',
|
||||
'root-uuid', 'kernel-params', 'uefi')
|
||||
expected_calls = [mock.call('ctx', 'http://kernel-href',
|
||||
'tmpdir/kernel-href'),
|
||||
mock.call('ctx', 'http://ramdisk-href',
|
||||
'tmpdir/ramdisk-href'),
|
||||
mock.call('ctx', 'http://deploy_iso-href',
|
||||
'tmpdir/deploy_iso-href')]
|
||||
fetch_images_mock.assert_has_calls(expected_calls)
|
||||
params = ['root=UUID=root-uuid', 'kernel-params']
|
||||
create_isolinux_mock.assert_called_once_with(
|
||||
'output_file', 'tmpdir/deploy_iso-href', 'tmpdir/kernel-href',
|
||||
'tmpdir/ramdisk-href', params)
|
||||
|
||||
@mock.patch.object(images, 'create_isolinux_image_for_bios', autospec=True)
|
||||
@mock.patch.object(images, 'fetch', autospec=True)
|
||||
@mock.patch.object(utils, 'tempdir', autospec=True)
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
|
||||
"""Test class for common methods used by iLO modules."""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
import mock
|
||||
|
@ -25,6 +27,7 @@ import six
|
|||
from ironic.common import exception
|
||||
from ironic.common import images
|
||||
from ironic.common import swift
|
||||
from ironic.common import utils
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.drivers.modules.ilo import common as ilo_common
|
||||
from ironic.tests.conductor import utils as mgr_utils
|
||||
|
@ -215,6 +218,39 @@ class IloCommonMethodsTestCase(db_base.DbTestCase):
|
|||
'ilo_cont', object_name, timeout)
|
||||
self.assertEqual('temp-url', temp_url)
|
||||
|
||||
@mock.patch.object(ilo_common, 'copy_image_to_web_server',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(images, 'create_vfat_image', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(tempfile, 'NamedTemporaryFile', spec_set=True,
|
||||
autospec=True)
|
||||
def test__prepare_floppy_image_use_webserver(self, tempfile_mock,
|
||||
fatimage_mock,
|
||||
copy_mock):
|
||||
mock_image_file_handle = mock.MagicMock(spec=file)
|
||||
mock_image_file_obj = mock.MagicMock(spec=file)
|
||||
mock_image_file_obj.name = 'image-tmp-file'
|
||||
mock_image_file_handle.__enter__.return_value = mock_image_file_obj
|
||||
|
||||
tempfile_mock.return_value = mock_image_file_handle
|
||||
self.config(use_web_server_for_images=True, group='ilo')
|
||||
deploy_args = {'arg1': 'val1', 'arg2': 'val2'}
|
||||
CONF.deploy.http_url = "http://abc.com/httpboot"
|
||||
CONF.deploy.http_root = "/httpboot"
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
node_uuid = task.node.uuid
|
||||
object_name = 'image-' + node_uuid
|
||||
http_url = CONF.deploy.http_url + '/' + object_name
|
||||
copy_mock.return_value = "http://abc.com/httpboot/" + object_name
|
||||
temp_url = ilo_common._prepare_floppy_image(task, deploy_args)
|
||||
|
||||
fatimage_mock.assert_called_once_with('image-tmp-file',
|
||||
parameters=deploy_args)
|
||||
copy_mock.assert_called_once_with('image-tmp-file', object_name)
|
||||
self.assertEqual(http_url, temp_url)
|
||||
|
||||
@mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
|
||||
autospec=True)
|
||||
def test_attach_vmedia(self, get_ilo_object_mock):
|
||||
|
@ -445,6 +481,21 @@ class IloCommonMethodsTestCase(db_base.DbTestCase):
|
|||
'ilo_cont', 'image-node-uuid')
|
||||
eject_mock.assert_called_once_with(task)
|
||||
|
||||
@mock.patch.object(ilo_common, 'eject_vmedia_devices',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(ilo_common, 'destroy_floppy_image_from_web_server',
|
||||
spec_set=True, autospec=True)
|
||||
def test_cleanup_vmedia_boot_for_webserver(self,
|
||||
destroy_image_mock,
|
||||
eject_mock):
|
||||
CONF.ilo.use_web_server_for_images = True
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
ilo_common.cleanup_vmedia_boot(task)
|
||||
destroy_image_mock.assert_called_once_with(task.node)
|
||||
eject_mock.assert_called_once_with(task)
|
||||
|
||||
@mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
|
||||
autospec=True)
|
||||
def test_eject_vmedia_devices(self, get_ilo_object_mock):
|
||||
|
@ -572,3 +623,53 @@ class IloCommonMethodsTestCase(db_base.DbTestCase):
|
|||
ilo_common.set_secure_boot_mode,
|
||||
task, False)
|
||||
ilo_mock_object.set_secure_boot_mode.assert_called_once_with(False)
|
||||
|
||||
@mock.patch.object(os, 'chmod', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(shutil, 'copyfile', spec_set=True,
|
||||
autospec=True)
|
||||
def test_copy_image_to_web_server(self, copy_mock,
|
||||
chmod_mock):
|
||||
CONF.deploy.http_url = "http://x.y.z.a/webserver/"
|
||||
CONF.deploy.http_root = "/webserver"
|
||||
expected_url = "http://x.y.z.a/webserver/image-UUID"
|
||||
source = 'tmp_image_file'
|
||||
destination = "image-UUID"
|
||||
image_path = "/webserver/image-UUID"
|
||||
actual_url = ilo_common.copy_image_to_web_server(source, destination)
|
||||
self.assertEqual(expected_url, actual_url)
|
||||
copy_mock.assert_called_once_with(source, image_path)
|
||||
chmod_mock.assert_called_once_with(image_path, 0o644)
|
||||
|
||||
@mock.patch.object(os, 'chmod', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(shutil, 'copyfile', spec_set=True,
|
||||
autospec=True)
|
||||
def test_copy_image_to_web_server_fails(self, copy_mock,
|
||||
chmod_mock):
|
||||
CONF.deploy.http_url = "http://x.y.z.a/webserver/"
|
||||
CONF.deploy.http_root = "/webserver"
|
||||
source = 'tmp_image_file'
|
||||
destination = "image-UUID"
|
||||
image_path = "/webserver/image-UUID"
|
||||
exc = exception.ImageUploadFailed('reason')
|
||||
copy_mock.side_effect = exc
|
||||
self.assertRaises(exception.ImageUploadFailed,
|
||||
ilo_common.copy_image_to_web_server,
|
||||
source, destination)
|
||||
copy_mock.assert_called_once_with(source, image_path)
|
||||
self.assertFalse(chmod_mock.called)
|
||||
|
||||
@mock.patch.object(utils, 'unlink_without_raise', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(ilo_common, '_get_floppy_image_name', spec_set=True,
|
||||
autospec=True)
|
||||
def test_destroy_floppy_image_from_web_server(self, get_floppy_name_mock,
|
||||
utils_mock):
|
||||
get_floppy_name_mock.return_value = 'image-uuid'
|
||||
CONF.deploy.http_root = "/webserver/"
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
ilo_common.destroy_floppy_image_from_web_server(task.node)
|
||||
get_floppy_name_mock.assert_called_once_with(task.node)
|
||||
utils_mock.assert_called_once_with('/webserver/image-uuid')
|
||||
|
|
|
@ -28,6 +28,7 @@ from ironic.common import image_service
|
|||
from ironic.common import images
|
||||
from ironic.common import states
|
||||
from ironic.common import swift
|
||||
from ironic.common import utils
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.conductor import utils as manager_utils
|
||||
from ironic.drivers.modules import agent
|
||||
|
@ -177,7 +178,6 @@ class IloDeployPrivateMethodsTestCase(db_base.DbTestCase):
|
|||
capability_mock, boot_object_name_mock,
|
||||
swift_api_mock,
|
||||
create_boot_iso_mock, tempfile_mock):
|
||||
CONF.keystone_authtoken.auth_uri = 'http://authurl'
|
||||
CONF.ilo.swift_ilo_container = 'ilo-cont'
|
||||
CONF.pxe.pxe_append_params = 'kernel-params'
|
||||
|
||||
|
@ -196,7 +196,6 @@ class IloDeployPrivateMethodsTestCase(db_base.DbTestCase):
|
|||
boot_object_name_mock.return_value = 'abcdef'
|
||||
create_boot_iso_mock.return_value = '/path/to/boot-iso'
|
||||
capability_mock.return_value = 'uefi'
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
boot_iso_actual = ilo_deploy._get_boot_iso(task, 'root-uuid')
|
||||
|
@ -219,6 +218,69 @@ class IloDeployPrivateMethodsTestCase(db_base.DbTestCase):
|
|||
boot_iso_expected = 'swift:abcdef'
|
||||
self.assertEqual(boot_iso_expected, boot_iso_actual)
|
||||
|
||||
@mock.patch.object(ilo_common, 'copy_image_to_web_server', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(tempfile, 'NamedTemporaryFile', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(images, 'create_boot_iso', spec_set=True, autospec=True)
|
||||
@mock.patch.object(ilo_deploy, '_get_boot_iso_object_name', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(driver_utils, 'get_node_capability', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(images, 'get_image_properties', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(ilo_deploy, '_parse_deploy_info', spec_set=True,
|
||||
autospec=True)
|
||||
def test__get_boot_iso_create_use_webserver_true_ramdisk_webserver(
|
||||
self, deploy_info_mock, image_props_mock,
|
||||
capability_mock, boot_object_name_mock,
|
||||
create_boot_iso_mock, tempfile_mock,
|
||||
copy_file_mock):
|
||||
CONF.ilo.swift_ilo_container = 'ilo-cont'
|
||||
CONF.ilo.use_web_server_for_images = True
|
||||
CONF.deploy.http_url = "http://10.10.1.30/httpboot"
|
||||
CONF.deploy.http_root = "/httpboot"
|
||||
CONF.pxe.pxe_append_params = 'kernel-params'
|
||||
|
||||
fileobj_mock = mock.MagicMock(spec=file)
|
||||
fileobj_mock.name = 'tmpfile'
|
||||
mock_file_handle = mock.MagicMock(spec=file)
|
||||
mock_file_handle.__enter__.return_value = fileobj_mock
|
||||
tempfile_mock.return_value = mock_file_handle
|
||||
|
||||
ramdisk_href = "http://10.10.1.30/httpboot/ramdisk"
|
||||
kernel_href = "http://10.10.1.30/httpboot/kernel"
|
||||
deploy_info_mock.return_value = {'image_source': 'image-uuid',
|
||||
'ilo_deploy_iso': 'deploy_iso_uuid'}
|
||||
image_props_mock.return_value = {'boot_iso': None,
|
||||
'kernel_id': kernel_href,
|
||||
'ramdisk_id': ramdisk_href}
|
||||
boot_object_name_mock.return_value = 'abcdef'
|
||||
create_boot_iso_mock.return_value = '/path/to/boot-iso'
|
||||
capability_mock.return_value = 'uefi'
|
||||
copy_file_mock.return_value = "http://10.10.1.30/httpboot/abcdef"
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
boot_iso_actual = ilo_deploy._get_boot_iso(task, 'root-uuid')
|
||||
deploy_info_mock.assert_called_once_with(task.node)
|
||||
image_props_mock.assert_called_once_with(
|
||||
task.context, 'image-uuid',
|
||||
['boot_iso', 'kernel_id', 'ramdisk_id'])
|
||||
boot_object_name_mock.assert_called_once_with(task.node)
|
||||
create_boot_iso_mock.assert_called_once_with(task.context,
|
||||
'tmpfile',
|
||||
kernel_href,
|
||||
ramdisk_href,
|
||||
'deploy_iso_uuid',
|
||||
'root-uuid',
|
||||
'kernel-params',
|
||||
'uefi')
|
||||
boot_iso_expected = 'http://10.10.1.30/httpboot/abcdef'
|
||||
self.assertEqual(boot_iso_expected, boot_iso_actual)
|
||||
copy_file_mock.assert_called_once_with(fileobj_mock.name,
|
||||
'abcdef')
|
||||
|
||||
@mock.patch.object(ilo_deploy, '_get_boot_iso_object_name', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(swift, 'SwiftAPI', spec_set=True, autospec=True)
|
||||
|
@ -235,6 +297,20 @@ class IloDeployPrivateMethodsTestCase(db_base.DbTestCase):
|
|||
swift_obj_mock.delete_object.assert_called_once_with('ilo-cont',
|
||||
'boot-object')
|
||||
|
||||
@mock.patch.object(utils, 'unlink_without_raise', spec_set=True,
|
||||
autospec=True)
|
||||
def test__clean_up_boot_iso_for_instance_on_webserver(self, unlink_mock):
|
||||
|
||||
CONF.ilo.use_web_server_for_images = True
|
||||
CONF.deploy.http_root = "/webserver"
|
||||
i_info = self.node.instance_info
|
||||
i_info['ilo_boot_iso'] = 'http://x.y.z.a/webserver/boot-object'
|
||||
self.node.instance_info = i_info
|
||||
self.node.save()
|
||||
boot_iso_path = "/webserver/boot-object"
|
||||
ilo_deploy._clean_up_boot_iso_for_instance(self.node)
|
||||
unlink_mock.assert_called_once_with(boot_iso_path)
|
||||
|
||||
@mock.patch.object(ilo_deploy, '_get_boot_iso_object_name', spec_set=True,
|
||||
autospec=True)
|
||||
def test__clean_up_boot_iso_for_instance_no_boot_iso(
|
||||
|
@ -687,6 +763,19 @@ class IloVirtualMediaIscsiDeployTestCase(db_base.DbTestCase):
|
|||
destroy_images_mock.assert_called_once_with(task.node.uuid)
|
||||
clean_up_boot_mock.assert_called_once_with(task.node)
|
||||
|
||||
@mock.patch.object(ilo_deploy, '_clean_up_boot_iso_for_instance',
|
||||
spec_set=True, autospec=True)
|
||||
@mock.patch.object(ilo_common, 'destroy_floppy_image_from_web_server',
|
||||
spec_set=True, autospec=True)
|
||||
def test_clean_up_of_webserver_images(self, destroy_images_mock,
|
||||
clean_up_boot_mock):
|
||||
CONF.ilo.use_web_server_for_images = True
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.deploy.clean_up(task)
|
||||
destroy_images_mock.assert_called_once_with(task.node)
|
||||
clean_up_boot_mock.assert_called_once_with(task.node)
|
||||
|
||||
@mock.patch.object(ilo_deploy, '_prepare_node_for_deploy', spec_set=True,
|
||||
autospec=True)
|
||||
def test_prepare(self, func_prepare_node_for_deploy):
|
||||
|
|
Loading…
Reference in New Issue