Esempio n. 1
0
    def start_console(self, task):
        """Start a remote console for the node.

        :param task: a TaskManager instance containing the node to act on.
        :raises: MissingParameterValue when required ipmi credentials
                are missing.
        :raises: InvalidParameterValue when the IPMI terminal port is not an
                integer.
        :raises: ConsoleError if unable to start the console process.
        """
        driver_info = _parse_driver_info(task.node)

        path = _console_pwfile_path(driver_info['uuid'])
        pw_file = console_utils.make_persistent_password_file(
            path, driver_info['password'])

        console_cmd = ("/:%(uid)s:%(gid)s:HOME:pyghmicons %(bmc)s"
                       " %(user)s"
                       " %(passwd_file)s"
                       % {'uid': os.getuid(),
                          'gid': os.getgid(),
                          'bmc': driver_info['address'],
                          'user': driver_info['username'],
                          'passwd_file': pw_file})
        try:
            console_utils.start_shellinabox_console(driver_info['uuid'],
                                                    driver_info['port'],
                                                    console_cmd)
        except exception.ConsoleError:
            with excutils.save_and_reraise_exception():
                ironic_utils.unlink_without_raise(path)
Esempio n. 2
0
    def start_console(self, task):
        """Start a remote console for the node.

        :param task: a TaskManager instance containing the node to act on.
        :raises: MissingParameterValue when required ipmi credentials
                are missing.
        :raises: InvalidParameterValue when the IPMI terminal port is not an
                integer.
        :raises: ConsoleError if unable to start the console process.
        """
        driver_info = _parse_driver_info(task.node)

        path = _console_pwfile_path(driver_info['uuid'])
        pw_file = console_utils.make_persistent_password_file(
            path, driver_info['password'])

        console_cmd = ("/:%(uid)s:%(gid)s:HOME:pyghmicons %(bmc)s"
                       " %(user)s"
                       " %(passwd_file)s" % {
                           'uid': os.getuid(),
                           'gid': os.getgid(),
                           'bmc': driver_info['address'],
                           'user': driver_info['username'],
                           'passwd_file': pw_file
                       })
        try:
            console_utils.start_shellinabox_console(driver_info['uuid'],
                                                    driver_info['port'],
                                                    console_cmd)
        except exception.ConsoleError:
            with excutils.save_and_reraise_exception():
                ironic_utils.unlink_without_raise(path)
Esempio n. 3
0
 def clean_up(self, task):
     """Clean up the deployment environment for this node."""
     task.driver.boot.clean_up_ramdisk(task)
     provider = dhcp_factory.DHCPFactory()
     provider.clean_dhcp(task)
     irlib_utils.unlink_without_raise(
         _get_configdrive_path(task.node.uuid))
def _get_nm_address(task):
    """Get Intel Node Manager target channel and address.

    :param task: a TaskManager instance.
    :raises: IPMIFailure if Intel Node Manager is not detected on a node or if
             an error happens during detection.
    :returns: a tuple with IPMI channel and address of Intel Node Manager.
    """
    node = task.node
    driver_internal_info = node.driver_internal_info

    def _save_to_node(channel, address):
        driver_internal_info['intel_nm_channel'] = channel
        driver_internal_info['intel_nm_address'] = address
        node.driver_internal_info = driver_internal_info
        node.save()

    channel = driver_internal_info.get('intel_nm_channel')
    address = driver_internal_info.get('intel_nm_address')
    if channel and address:
        return channel, address
    if channel is False and address is False:
        raise exception.IPMIFailure(
            _('Driver data indicates that Intel '
              'Node Manager detection failed.'))
    LOG.info(_LI('Start detection of Intel Node Manager on node %s'),
             node.uuid)
    sdr_filename = os.path.join(CONF.tempdir, node.uuid + '.sdr')
    res = None
    try:
        ipmitool.dump_sdr(task, sdr_filename)
        res = nm_commands.parse_slave_and_channel(sdr_filename)
    finally:
        ironic_utils.unlink_without_raise(sdr_filename)
    if res is None:
        _save_to_node(False, False)
        raise exception.IPMIFailure(_('Intel Node Manager is not detected.'))
    address, channel = res
    LOG.debug(
        'Intel Node Manager sensors present in SDR on node %(node)s, '
        'channel %(channel)s address %(address)s.', {
            'node': node.uuid,
            'channel': channel,
            'address': address
        })
    # SDR can contain wrong info, try simple command
    node.driver_info['ipmi_bridging'] = 'single'
    node.driver_info['ipmi_target_channel'] = channel
    node.driver_info['ipmi_target_address'] = address
    try:
        ipmitool.send_raw(task,
                          _command_to_string(nm_commands.get_version(None)))
        _save_to_node(channel, address)
        return channel, address
    except exception.IPMIFailure:
        _save_to_node(False, False)
        raise exception.IPMIFailure(
            _('Intel Node Manager sensors record '
              'present in SDR but Node Manager is not '
              'responding.'))
Esempio n. 5
0
def _stop_console(node_uuid):
    """Close the serial console for a node

    Kills the console process and deletes the PID file.

    :param node_uuid: the UUID of the node
    :raises: NoConsolePid if no console PID was found
    :raises: ConsoleError if unable to stop the console process
    """

    try:
        console_pid = _get_console_pid(node_uuid)

        os.kill(console_pid, signal.SIGTERM)
    except OSError as exc:
        if exc.errno != errno.ESRCH:
            msg = (_("Could not stop the console for node '%(node)s'. "
                     "Reason: %(err)s.") % {
                         'node': node_uuid,
                         'err': exc
                     })
            raise exception.ConsoleError(message=msg)
        else:
            LOG.warning(
                _LW("Console process for node %s is not running "
                    "but pid file exists."), node_uuid)
    finally:
        ironic_utils.unlink_without_raise(_get_console_pid_file(node_uuid))
Esempio n. 6
0
    def _unpublish_image(cls, object_name):
        """Withdraw the image previously made downloadable.

        Depending on ironic settings, removes previously published file
        from where it has been published - Swift or local HTTP server's
        document root.

        :param object_name: name of the published file (optional)
        """
        if CONF.redfish.use_swift:
            container = CONF.redfish.swift_container

            swift_api = swift.SwiftAPI()

            LOG.debug("Cleaning up image %(name)s from Swift container "
                      "%(container)s", {'name': object_name,
                                        'container': container})

            try:
                swift_api.delete_object(container, object_name)

            except exception.SwiftOperationError as exc:
                LOG.warning("Failed to clean up image %(image)s. Error: "
                            "%(error)s.", {'image': object_name,
                                           'error': exc})

        else:
            published_file = os.path.join(
                CONF.deploy.http_root, cls.IMAGE_SUBDIR, object_name)

            ironic_utils.unlink_without_raise(published_file)
Esempio n. 7
0
def _stop_console(node_uuid):
    """Close the serial console for a node

    Kills the console process and deletes the PID file.

    :param node_uuid: the UUID of the node
    :raises: NoConsolePid if no console PID was found
    :raises: ConsoleError if unable to stop the console process
    """

    try:
        console_pid = _get_console_pid(node_uuid)

        os.kill(console_pid, signal.SIGTERM)
    except OSError as exc:
        if exc.errno != errno.ESRCH:
            msg = (_("Could not stop the console for node '%(node)s'. "
                     "Reason: %(err)s.") % {'node': node_uuid, 'err': exc})
            raise exception.ConsoleError(message=msg)
        else:
            LOG.warning(_LW("Console process for node %s is not running "
                            "but pid file exists while trying to stop "
                            "shellinabox console."), node_uuid)
    finally:
        ironic_utils.unlink_without_raise(_get_console_pid_file(node_uuid))
Esempio n. 8
0
def _link_ip_address_pxe_configs(task, hex_form):
    """Link each IP address with the PXE configuration file.

    :param task: A TaskManager instance.
    :param hex_form: Boolean value indicating if the conf file name should be
                     hexadecimal equivalent of supplied ipv4 address.
    :raises: FailedToGetIPAddressOnPort
    :raises: InvalidIPv4Address

    """
    pxe_config_file_path = get_pxe_config_file_path(task.node.uuid)

    api = dhcp_factory.DHCPFactory().provider
    ip_addrs = api.get_ip_addresses(task)
    if not ip_addrs:
        raise exception.FailedToGetIPAddressOnPort(_(
            "Failed to get IP address for any port on node %s.") %
            task.node.uuid)
    for port_ip_address in ip_addrs:
        ip_address_path = _get_pxe_ip_address_path(port_ip_address,
                                                   hex_form)
        ironic_utils.unlink_without_raise(ip_address_path)
        relative_source_path = os.path.relpath(
            pxe_config_file_path, os.path.dirname(ip_address_path))
        utils.create_link_without_raise(relative_source_path,
                                        ip_address_path)
Esempio n. 9
0
 def clean_up(self, task):
     """Clean up the deployment environment for this node."""
     task.driver.boot.clean_up_ramdisk(task)
     provider = dhcp_factory.DHCPFactory()
     provider.clean_dhcp(task)
     irlib_utils.unlink_without_raise(
         _get_configdrive_path(task.node.uuid))
Esempio n. 10
0
def _link_ip_address_pxe_configs(task, ipxe_enabled=False):
    """Link each IP address with the PXE configuration file.

    :param task: A TaskManager instance.
    :param ipxe_enabled: Default false boolean to indicate if ipxe
                         is in use by the caller.
    :raises: FailedToGetIPAddressOnPort
    :raises: InvalidIPv4Address

    """
    pxe_config_file_path = get_pxe_config_file_path(task.node.uuid,
                                                    ipxe_enabled=ipxe_enabled)

    api = dhcp_factory.DHCPFactory().provider
    ip_addrs = api.get_ip_addresses(task)
    if not ip_addrs:

        if ip_addrs == []:
            LOG.warning("No IP addresses assigned for node %(node)s.",
                        {'node': task.node.uuid})
        else:
            LOG.warning(
                "DHCP address management is not available for node "
                "%(node)s. Operators without Neutron can ignore this "
                "warning.", {'node': task.node.uuid})
        # Just in case, reset to empty list if we got nothing.
        ip_addrs = []
    for port_ip_address in ip_addrs:
        ip_address_path = _get_pxe_ip_address_path(port_ip_address)
        ironic_utils.unlink_without_raise(ip_address_path)
        relative_source_path = os.path.relpath(
            pxe_config_file_path, os.path.dirname(ip_address_path))
        utils.create_link_without_raise(relative_source_path, ip_address_path)
Esempio n. 11
0
def _clean_up_boot_iso_for_instance(node):
    """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:
        return
    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(
                "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)
        ironic_utils.unlink_without_raise(boot_iso_path)
Esempio n. 12
0
def _link_ip_address_pxe_configs(task, hex_form):
    """Link each IP address with the PXE configuration file.

    :param task: A TaskManager instance.
    :param hex_form: Boolean value indicating if the conf file name should be
                     hexadecimal equivalent of supplied ipv4 address.
    :raises: FailedToGetIPAddressOnPort
    :raises: InvalidIPv4Address

    """
    pxe_config_file_path = get_pxe_config_file_path(task.node.uuid)

    api = dhcp_factory.DHCPFactory().provider
    ip_addrs = api.get_ip_addresses(task)
    if not ip_addrs:
        raise exception.FailedToGetIPAddressOnPort(_(
            "Failed to get IP address for any port on node %s.") %
            task.node.uuid)
    for port_ip_address in ip_addrs:
        ip_address_path = _get_pxe_ip_address_path(port_ip_address,
                                                   hex_form)
        ironic_utils.unlink_without_raise(ip_address_path)
        relative_source_path = os.path.relpath(
            pxe_config_file_path, os.path.dirname(ip_address_path))
        utils.create_link_without_raise(relative_source_path,
                                        ip_address_path)
Esempio n. 13
0
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)
    ironic_utils.unlink_without_raise(share_fullpathname)
Esempio n. 14
0
def _link_ip_address_pxe_configs(task, ipxe_enabled=False):
    """Link each IP address with the PXE configuration file.

    :param task: A TaskManager instance.
    :param ipxe_enabled: Default false boolean to indicate if ipxe
                         is in use by the caller.
    :raises: FailedToGetIPAddressOnPort
    :raises: InvalidIPv4Address

    """
    pxe_config_file_path = get_pxe_config_file_path(task.node.uuid,
                                                    ipxe_enabled=ipxe_enabled)

    api = dhcp_factory.DHCPFactory().provider
    ip_addrs = api.get_ip_addresses(task)
    if not ip_addrs:
        raise exception.FailedToGetIPAddressOnPort(
            _("Failed to get IP address for any port on node %s.") %
            task.node.uuid)
    for port_ip_address in ip_addrs:
        ip_address_path = _get_pxe_ip_address_path(port_ip_address)
        ironic_utils.unlink_without_raise(ip_address_path)
        relative_source_path = os.path.relpath(
            pxe_config_file_path, os.path.dirname(ip_address_path))
        utils.create_link_without_raise(relative_source_path, ip_address_path)
Esempio n. 15
0
def destroy_images(node_uuid):
    """Delete instance's image file.

    :param node_uuid: the uuid of the ironic node.
    """
    ironic_utils.unlink_without_raise(_get_image_file_path(node_uuid))
    utils.rmtree_without_raise(_get_image_dir_path(node_uuid))
    InstanceImageCache().clean_up()
Esempio n. 16
0
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)
    ironic_utils.unlink_without_raise(share_fullpathname)
Esempio n. 17
0
def _clean_up_pxe(task):
    """Clean up left over PXE and DHCP files."""
    pxe_info = _get_tftp_image_info(task.node)
    for label in pxe_info:
        path = pxe_info[label][1]
        ironic_utils.unlink_without_raise(path)
    AgentTFTPImageCache().clean_up()
    pxe_utils.clean_up_pxe_config(task)
Esempio n. 18
0
def _clean_up_pxe(task):
    """Clean up left over PXE and DHCP files."""
    pxe_info = _get_tftp_image_info(task.node)
    for label in pxe_info:
        path = pxe_info[label][1]
        ironic_utils.unlink_without_raise(path)
    AgentTFTPImageCache().clean_up()
    pxe_utils.clean_up_pxe_config(task)
Esempio n. 19
0
def destroy_images(node_uuid):
    """Delete instance's image file.

    :param node_uuid: the uuid of the ironic node.
    """
    ironic_utils.unlink_without_raise(_get_image_file_path(node_uuid))
    utils.rmtree_without_raise(_get_image_dir_path(node_uuid))
    InstanceImageCache().clean_up()
Esempio n. 20
0
def _get_configdrive(configdrive, node_uuid, tempdir=None):
    """Get the information about size and location of the configdrive.

    :param configdrive: Base64 encoded Gzipped configdrive content or
        configdrive HTTP URL.
    :param node_uuid: Node's uuid. Used for logging.
    :param tempdir: temporary directory for the temporary configdrive file
    :raises: InstanceDeployFailure if it can't download or decode the
       config drive.
    :returns: A tuple with the size in MiB and path to the uncompressed
        configdrive file.

    """
    # Check if the configdrive option is a HTTP URL or the content directly
    is_url = utils.is_http_url(configdrive)
    if is_url:
        try:
            data = requests.get(configdrive).content
        except requests.exceptions.RequestException as e:
            raise exception.InstanceDeployFailure(
                _("Can't download the configdrive content for node %(node)s "
                  "from '%(url)s'. Reason: %(reason)s") %
                {'node': node_uuid, 'url': configdrive, 'reason': e})
    else:
        data = configdrive

    try:
        data = six.BytesIO(base64.decode_as_bytes(data))
    except TypeError:
        error_msg = (_('Config drive for node %s is not base64 encoded '
                       'or the content is malformed.') % node_uuid)
        if is_url:
            error_msg += _(' Downloaded from "%s".') % configdrive
        raise exception.InstanceDeployFailure(error_msg)

    configdrive_file = tempfile.NamedTemporaryFile(delete=False,
                                                   prefix='configdrive',
                                                   dir=tempdir)
    configdrive_mb = 0
    with gzip.GzipFile('configdrive', 'rb', fileobj=data) as gunzipped:
        try:
            shutil.copyfileobj(gunzipped, configdrive_file)
        except EnvironmentError as e:
            # Delete the created file
            utils.unlink_without_raise(configdrive_file.name)
            raise exception.InstanceDeployFailure(
                _('Encountered error while decompressing and writing '
                  'config drive for node %(node)s. Error: %(exc)s') %
                {'node': node_uuid, 'exc': e})
        else:
            # Get the file size and convert to MiB
            configdrive_file.seek(0, os.SEEK_END)
            bytes_ = configdrive_file.tell()
            configdrive_mb = int(math.ceil(float(bytes_) / units.Mi))
        finally:
            configdrive_file.close()

        return (configdrive_mb, configdrive_file.name)
Esempio n. 21
0
def _get_configdrive(configdrive, node_uuid, tempdir=None):
    """Get the information about size and location of the configdrive.

    :param configdrive: Base64 encoded Gzipped configdrive content or
        configdrive HTTP URL.
    :param node_uuid: Node's uuid. Used for logging.
    :param tempdir: temporary directory for the temporary configdrive file
    :raises: InstanceDeployFailure if it can't download or decode the
       config drive.
    :returns: A tuple with the size in MiB and path to the uncompressed
        configdrive file.

    """
    # Check if the configdrive option is a HTTP URL or the content directly
    is_url = utils.is_http_url(configdrive)
    if is_url:
        try:
            data = requests.get(configdrive).content
        except requests.exceptions.RequestException as e:
            raise exception.InstanceDeployFailure(
                _("Can't download the configdrive content for node %(node)s "
                  "from '%(url)s'. Reason: %(reason)s") %
                {'node': node_uuid, 'url': configdrive, 'reason': e})
    else:
        data = configdrive

    try:
        data = six.BytesIO(base64.b64decode(data))
    except TypeError:
        error_msg = (_('Config drive for node %s is not base64 encoded '
                       'or the content is malformed.') % node_uuid)
        if is_url:
            error_msg += _(' Downloaded from "%s".') % configdrive
        raise exception.InstanceDeployFailure(error_msg)

    configdrive_file = tempfile.NamedTemporaryFile(delete=False,
                                                   prefix='configdrive',
                                                   dir=tempdir)
    configdrive_mb = 0
    with gzip.GzipFile('configdrive', 'rb', fileobj=data) as gunzipped:
        try:
            shutil.copyfileobj(gunzipped, configdrive_file)
        except EnvironmentError as e:
            # Delete the created file
            utils.unlink_without_raise(configdrive_file.name)
            raise exception.InstanceDeployFailure(
                _('Encountered error while decompressing and writing '
                  'config drive for node %(node)s. Error: %(exc)s') %
                {'node': node_uuid, 'exc': e})
        else:
            # Get the file size and convert to MiB
            configdrive_file.seek(0, os.SEEK_END)
            bytes_ = configdrive_file.tell()
            configdrive_mb = int(math.ceil(float(bytes_) / units.Mi))
        finally:
            configdrive_file.close()

        return (configdrive_mb, configdrive_file.name)
Esempio n. 22
0
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)
    ironic_utils.unlink_without_raise(image_path)
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.remote_image_share_root,
                                      share_filename)
    LOG.debug(_translators.log_info("_remove_share_file: Unlinking %s"),
              share_fullpathname)
    ironic_utils.unlink_without_raise(share_fullpathname)
Esempio n. 24
0
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)
    ironic_utils.unlink_without_raise(image_path)
Esempio n. 25
0
    def stop_console(self, task):
        """Stop the remote console session for the node.

        :param task: a task from TaskManager
        :raises: ConsoleError if unable to stop the console
        """
        try:
            console_utils.stop_shellinabox_console(task.node.uuid)
        finally:
            ironic_utils.unlink_without_raise(
                _console_pwfile_path(task.node.uuid))
Esempio n. 26
0
    def stop_console(self, task):
        """Stop the remote console session for the node.

        :param task: a TaskManager instance containing the node to act on.
        :raises: ConsoleError if unable to stop the console process.
        """
        try:
            console_utils.stop_shellinabox_console(task.node.uuid)
        finally:
            password_file = _console_pwfile_path(task.node.uuid)
            ironic_utils.unlink_without_raise(password_file)
Esempio n. 27
0
    def stop_console(self, task):
        """Stop the remote console session for the node.

        :param task: a TaskManager instance containing the node to act on.
        :raises: ConsoleError if unable to stop the console process.
        """
        try:
            console_utils.stop_shellinabox_console(task.node.uuid)
        finally:
            password_file = _console_pwfile_path(task.node.uuid)
            ironic_utils.unlink_without_raise(password_file)
Esempio n. 28
0
    def stop_console(self, task):
        """Stop the remote console session for the node.

        :param task: a task from TaskManager
        :raises: ConsoleError if unable to stop the console
        """
        try:
            console_utils.stop_shellinabox_console(task.node.uuid)
        finally:
            ironic_utils.unlink_without_raise(
                _console_pwfile_path(task.node.uuid))
Esempio n. 29
0
def remove_image_from_web_server(object_name):
    """Removes the given image from the configured web server.

    This method removes the given image from the http_root location,
    if the image exists.

    :param object_name: The name of the image file which needs to be removed
                        from the web server root.
    """
    image_path = os.path.join(CONF.deploy.http_root, object_name)
    ironic_utils.unlink_without_raise(image_path)
def _get_nm_address(task):
    """Get Intel Node Manager target channel and address.

    :param task: a TaskManager instance.
    :raises: IPMIFailure if Intel Node Manager is not detected on a node or if
             an error happens during detection.
    :returns: a tuple with IPMI channel and address of Intel Node Manager.
    """
    node = task.node
    driver_internal_info = node.driver_internal_info

    def _save_to_node(channel, address):
        driver_internal_info['intel_nm_channel'] = channel
        driver_internal_info['intel_nm_address'] = address
        node.driver_internal_info = driver_internal_info
        node.save()

    channel = driver_internal_info.get('intel_nm_channel')
    address = driver_internal_info.get('intel_nm_address')
    if channel and address:
        return channel, address
    if channel is False and address is False:
        raise exception.IPMIFailure(_('Driver data indicates that Intel '
                                      'Node Manager detection failed.'))
    LOG.info(_LI('Start detection of Intel Node Manager on node %s'),
             node.uuid)
    sdr_filename = os.path.join(CONF.tempdir, node.uuid + '.sdr')
    res = None
    try:
        ipmitool.dump_sdr(task, sdr_filename)
        res = nm_commands.parse_slave_and_channel(sdr_filename)
    finally:
        ironic_utils.unlink_without_raise(sdr_filename)
    if res is None:
        _save_to_node(False, False)
        raise exception.IPMIFailure(_('Intel Node Manager is not detected.'))
    address, channel = res
    LOG.debug('Intel Node Manager sensors present in SDR on node %(node)s, '
              'channel %(channel)s address %(address)s.',
              {'node': node.uuid, 'channel': channel, 'address': address})
    # SDR can contain wrong info, try simple command
    node.driver_info['ipmi_bridging'] = 'single'
    node.driver_info['ipmi_target_channel'] = channel
    node.driver_info['ipmi_target_address'] = address
    try:
        ipmitool.send_raw(task,
                          _command_to_string(nm_commands.get_version(None)))
        _save_to_node(channel, address)
        return channel, address
    except exception.IPMIFailure:
        _save_to_node(False, False)
        raise exception.IPMIFailure(_('Intel Node Manager sensors record '
                                      'present in SDR but Node Manager is not '
                                      'responding.'))
Esempio n. 31
0
def remove_image_from_web_server(object_name):
    """Removes the given image from the configured web server.

    This method removes the given image from the http_root location,
    if the image exists.

    :param object_name: The name of the image file which needs to be removed
                        from the web server root.
    """
    image_path = os.path.join(CONF.deploy.http_root, object_name)
    ironic_utils.unlink_without_raise(image_path)
Esempio n. 32
0
def _inject_one(node, ports, fl, root_dev, http_get):
    """Inject one file.

    :param node: A dictionary of the node object
    :param ports: A list of dictionaries containing information
                  of ports for the node
    :param fl: File information.
    :param root_dev: Root device used for the current node.
    :param http_get: Context manager to get HTTP URLs.
    """
    with _find_and_mount_path(fl['path'], fl.get('partition'),
                              root_dev) as path:
        if fl.get('deleted'):
            ironic_utils.unlink_without_raise(path)
            return

        try:
            dirpath = os.path.dirname(path)
            try:
                os.makedirs(dirpath)
            except FileExistsError:
                pass
            else:
                # Use chmod here and below to avoid relying on umask
                if fl.get('dirmode'):
                    os.chmod(dirpath, fl['dirmode'])

            content = fl['content']
            with open(path, 'wb') as fp:
                if '://' in content:
                    # Allow node-specific URLs to be used in a deploy template
                    url = content.format(node=node, ports=ports)
                    with http_get(url) as resp:
                        for chunk in resp:
                            fp.write(chunk)
                else:
                    fp.write(base64.b64decode(content))

            if fl.get('mode'):
                os.chmod(path, fl['mode'])

            if fl.get('owner') is not None or fl.get('group') is not None:
                # -1 means do not change
                os.chown(path, fl.get('owner', -1), fl.get('group', -1))
        except Exception as exc:
            LOG.exception('Failed to process file %s', fl)
            raise errors.CommandExecutionError(
                'Failed to process file %s. %s: %s' %
                (fl, type(exc).__class__, exc))
Esempio n. 33
0
def _stop_console(node_uuid):
    """Close the serial console for a node

    Kills the console process and deletes the PID file.

    :param node_uuid: the UUID of the node
    :raises: NoConsolePid if no console PID was found
    :raises: ConsoleError if unable to stop the console process
    """

    try:
        console_pid = _get_console_pid(node_uuid)

        os.kill(console_pid, signal.SIGTERM)

        # make sure the process gets killed hard if required
        attempt = 0
        max_attempts = CONF.console.kill_timeout // 0.2

        while attempt < max_attempts:
            if psutil.pid_exists(console_pid):
                if attempt == max_attempts - 1:
                    os.kill(console_pid, signal.SIGKILL)
                LOG.debug(
                    "Waiting for the console process with PID %(pid)s "
                    "to exit. Node: %(node)s.", {
                        'pid': console_pid,
                        'node': node_uuid
                    })
                time.sleep(0.2)
                attempt += 1
            else:
                break

    except OSError as exc:
        if exc.errno != errno.ESRCH:
            msg = (_("Could not stop the console for node '%(node)s'. "
                     "Reason: %(err)s.") % {
                         'node': node_uuid,
                         'err': exc
                     })
            raise exception.ConsoleError(message=msg)
        else:
            LOG.warning(
                "Console process for node %s is not running "
                "but pid file exists.", node_uuid)
    finally:
        ironic_utils.unlink_without_raise(_get_console_pid_file(node_uuid))
Esempio n. 34
0
def clean_up_pxe_env(task, images_info, ipxe_enabled=False):
    """Cleanup PXE environment of all the images in images_info.

    Cleans up the PXE environment for the mentioned images in
    images_info.

    :param task: a TaskManager object
    :param images_info: A dictionary of images whose keys are the image names
        to be cleaned up (kernel, ramdisk, etc) and values are a tuple of
        identifier and absolute path.
    """
    for label in images_info:
        path = images_info[label][1]
        ironic_utils.unlink_without_raise(path)

    clean_up_pxe_config(task, ipxe_enabled=ipxe_enabled)
    TFTPImageCache().clean_up()
Esempio n. 35
0
def remove_single_or_list_of_files(file_location):
    """Removes (deletes) the file or list of files.

    This method only accepts single or list of files to delete.
    If single file is passed, this method removes (deletes) the file.
    If list of files is passed, this method removes (deletes) each of the
    files iteratively.

    :param file_location: a single or a list of file paths
    """
    # file_location is a list of files
    if isinstance(file_location, list):
        for location in file_location:
            ironic_utils.unlink_without_raise(location)
    # file_location is a single file path
    elif isinstance(file_location, six.string_types):
        ironic_utils.unlink_without_raise(file_location)
Esempio n. 36
0
def remove_single_or_list_of_files(file_location):
    """Removes (deletes) the file or list of files.

    This method only accepts single or list of files to delete.
    If single file is passed, this method removes (deletes) the file.
    If list of files is passed, this method removes (deletes) each of the
    files iteratively.

    :param file_location: a single or a list of file paths
    """
    # file_location is a list of files
    if isinstance(file_location, list):
        for location in file_location:
            ironic_utils.unlink_without_raise(location)
    # file_location is a single file path
    elif isinstance(file_location, six.string_types):
        ironic_utils.unlink_without_raise(file_location)
Esempio n. 37
0
def _clean_up_pxe_env(task, images_info):
    """Cleanup PXE environment of all the images in images_info.

    Cleans up the PXE environment for the mentioned images in
    images_info.

    :param task: a TaskManager object
    :param images_info: A dictionary of images whose keys are the image names
        to be cleaned up (kernel, ramdisk, etc) and values are a tuple of
        identifier and absolute path.
    """
    for label in images_info:
        path = images_info[label][1]
        ironic_utils.unlink_without_raise(path)

    pxe_utils.clean_up_pxe_config(task)
    TFTPImageCache().clean_up()
Esempio n. 38
0
    def write_netconfig(self, node, ports):
        # Run this first to validate the request
        configs = [(port, port_to_config(port)) for port in ports]

        with partition_with_path(PATH) as path:
            # Purge any existing configuration
            for current in os.listdir(path):
                if current.startswith('ifcfg-'):
                    LOG.debug('Removing %s', current)
                    utils.unlink_without_raise(os.path.join(path, current))

            for port, config in configs:
                # Write a new configuration
                fname = "ifcfg-%s" % find_device_by_mac(port['address'])
                fname = os.path.join(path, fname)
                LOG.info("Writing config to %s: %s", fname, config)
                with open(fname, "wt") as fp:
                    fp.write(config)
def clear_ics_console_log(node_uuid):
    """Clear the content of a console log (ironic console server).

    :param node_uuid: the uuid for the node.
    :raises: ConsoleError if unable to clear the console log
    """

    try:
        ironic_utils.unlink_without_raise(_get_console_log_file(node_uuid))
        _send_signal(node_uuid, 'HUP')
    except exception.NoConsolePid:
        LOG.warning(_LW("No console pid found for node %s while trying to "
                        "enable the console service."), node_uuid)
    except processutils.ProcessExecutionError as exc:
            msg = (_("Could not enable the console service for node "
                     "'%(node)s'. Reason: %(err)s.") % {
                   'node': node_uuid, 'err': exc})
            raise exception.ConsoleError(message=msg)
Esempio n. 40
0
    def clean_up(self, task):

        extra_info = task.node.extra
        pxe_boot_interface_mac = extra_info.get('boot_detailed').get(
            'pxe_interface')
        pxe_boot_interface_mac.replace('-', ':')
        for port in task.ports:
            if port.address == pxe_boot_interface_mac:
                client_id = port.extra.get('client-id')
                ironic_utils.unlink_without_raise(
                    self._get_pxe_mac_path(port.address, client_id=client_id))

        pxe_config_file_path = pxe_utils.get_pxe_config_file_path(
            task.node.uuid)
        fileutils.delete_if_exists(pxe_config_file_path)
        if os.path.exists(os.path.join(CONF.pxe.tftp_root, task.node.uuid)):
            rmtree(os.path.join(CONF.pxe.tftp_root, task.node.uuid))

        auto_file_name = task.node.uuid + '_auto.cfg'
        fileutils.delete_if_exists(AUTO_FILE_DIR + auto_file_name)
Esempio n. 41
0
def _stop_console(node_uuid):
    """Close the serial console for a node

    Kills the console process and deletes the PID file.

    :param node_uuid: the UUID of the node
    :raises: NoConsolePid if no console PID was found
    :raises: ConsoleError if unable to stop the console process
    """

    try:
        console_pid = _get_console_pid(node_uuid)

        os.kill(console_pid, signal.SIGTERM)

        # make sure the process gets killed hard if required
        attempt = 0
        max_attempts = CONF.console.kill_timeout // 0.2

        while attempt < max_attempts:
            if psutil.pid_exists(console_pid):
                if attempt == max_attempts - 1:
                    os.kill(console_pid, signal.SIGKILL)
                LOG.debug("Waiting for the console process with PID %(pid)s "
                          "to exit. Node: %(node)s.",
                          {'pid': console_pid, 'node': node_uuid})
                time.sleep(0.2)
                attempt += 1
            else:
                break

    except OSError as exc:
        if exc.errno != errno.ESRCH:
            msg = (_("Could not stop the console for node '%(node)s'. "
                     "Reason: %(err)s.") % {'node': node_uuid, 'err': exc})
            raise exception.ConsoleError(message=msg)
        else:
            LOG.warning("Console process for node %s is not running "
                        "but pid file exists.", node_uuid)
    finally:
        ironic_utils.unlink_without_raise(_get_console_pid_file(node_uuid))
def clear_ics_console_log(node_uuid):
    """Clear the content of a console log (ironic console server).

    :param node_uuid: the uuid for the node.
    :raises: ConsoleError if unable to clear the console log
    """

    try:
        ironic_utils.unlink_without_raise(_get_console_log_file(node_uuid))
        _send_signal(node_uuid, 'HUP')
    except exception.NoConsolePid:
        LOG.warning(
            _LW("No console pid found for node %s while trying to "
                "enable the console service."), node_uuid)
    except processutils.ProcessExecutionError as exc:
        msg = (_("Could not enable the console service for node "
                 "'%(node)s'. Reason: %(err)s.") % {
                     'node': node_uuid,
                     'err': exc
                 })
        raise exception.ConsoleError(message=msg)
Esempio n. 43
0
    def start_console(self, task):
        """Start a remote console for the node.

        :param task: a task from TaskManager
        :raises: InvalidParameterValue if required ipmi parameters are missing
        :raises: PasswordFileFailedToCreate if unable to create a file
                 containing the password
        :raises: ConsoleError if the directory for the PID file cannot be
                 created
        :raises: ConsoleSubprocessFailed when invoking the subprocess failed
        """
        driver_info = _parse_driver_info(task.node)

        path = _console_pwfile_path(driver_info['uuid'])
        pw_file = console_utils.make_persistent_password_file(
            path, driver_info['password'] or '\0')

        ipmi_cmd = ("/:%(uid)s:%(gid)s:HOME:ipmitool -H %(address)s"
                    " -I lanplus -U %(user)s -f %(pwfile)s"
                    % {'uid': os.getuid(),
                       'gid': os.getgid(),
                       'address': driver_info['address'],
                       'user': driver_info['username'],
                       'pwfile': pw_file})

        for name, option in BRIDGING_OPTIONS:
            if driver_info[name] is not None:
                ipmi_cmd = " ".join([ipmi_cmd,
                                     option, driver_info[name]])

        if CONF.debug:
            ipmi_cmd += " -v"
        ipmi_cmd += " sol activate"
        try:
            console_utils.start_shellinabox_console(driver_info['uuid'],
                                                    driver_info['port'],
                                                    ipmi_cmd)
        except (exception.ConsoleError, exception.ConsoleSubprocessFailed):
            with excutils.save_and_reraise_exception():
                ironic_utils.unlink_without_raise(path)
Esempio n. 44
0
    def start_console(self, task):
        """Start a remote console for the node.

        :param task: a task from TaskManager
        :raises: InvalidParameterValue if required ipmi parameters are missing
        :raises: PasswordFileFailedToCreate if unable to create a file
                 containing the password
        :raises: ConsoleError if the directory for the PID file cannot be
                 created
        :raises: ConsoleSubprocessFailed when invoking the subprocess failed
        """
        driver_info = _parse_driver_info(task.node)

        path = _console_pwfile_path(driver_info['uuid'])
        pw_file = console_utils.make_persistent_password_file(
            path, driver_info['password'] or '\0')

        ipmi_cmd = ("/:%(uid)s:%(gid)s:HOME:ipmitool -H %(address)s"
                    " -I lanplus -U %(user)s -f %(pwfile)s" % {
                        'uid': os.getuid(),
                        'gid': os.getgid(),
                        'address': driver_info['address'],
                        'user': driver_info['username'],
                        'pwfile': pw_file
                    })

        for name, option in BRIDGING_OPTIONS:
            if driver_info[name] is not None:
                ipmi_cmd = " ".join([ipmi_cmd, option, driver_info[name]])

        if CONF.debug:
            ipmi_cmd += " -v"
        ipmi_cmd += " sol activate"
        try:
            console_utils.start_shellinabox_console(driver_info['uuid'],
                                                    driver_info['port'],
                                                    ipmi_cmd)
        except (exception.ConsoleError, exception.ConsoleSubprocessFailed):
            with excutils.save_and_reraise_exception():
                ironic_utils.unlink_without_raise(path)
Esempio n. 45
0
def clean_up_pxe_config(task):
    """Clean up the TFTP environment for the task's node.

    :param task: A TaskManager instance.

    """
    LOG.debug("Cleaning up PXE config for node %s", task.node.uuid)

    is_uefi_boot_mode = (deploy_utils.get_boot_mode_for_deploy(
        task.node) == 'uefi')
    if is_uefi_boot_mode and not CONF.pxe.ipxe_enabled:
        api = dhcp_factory.DHCPFactory().provider
        ip_addresses = api.get_ip_addresses(task)
        if not ip_addresses:
            return

        for port_ip_address in ip_addresses:
            try:
                # Get xx.xx.xx.xx based grub config file
                ip_address_path = _get_pxe_ip_address_path(
                    port_ip_address, False)
                # Get 0AOAOAOA based elilo config file
                hex_ip_path = _get_pxe_ip_address_path(port_ip_address, True)
            except exception.InvalidIPv4Address:
                continue
            # Cleaning up config files created for grub2.
            ironic_utils.unlink_without_raise(ip_address_path)
            # Cleaning up config files created for elilo.
            ironic_utils.unlink_without_raise(hex_ip_path)
    else:
        for mac in driver_utils.get_node_mac_addresses(task):
            ironic_utils.unlink_without_raise(_get_pxe_mac_path(mac))

    utils.rmtree_without_raise(os.path.join(get_root_dir(), task.node.uuid))
Esempio n. 46
0
def clean_up_pxe_config(task, ipxe_enabled=False):
    """Clean up the TFTP environment for the task's node.

    :param task: A TaskManager instance.

    """
    LOG.debug("Cleaning up PXE config for node %s", task.node.uuid)

    is_uefi_boot_mode = (boot_mode_utils.get_boot_mode(task.node) == 'uefi')

    if is_uefi_boot_mode and not ipxe_enabled:
        api = dhcp_factory.DHCPFactory().provider
        ip_addresses = api.get_ip_addresses(task)
        if not ip_addresses:
            return

        for port_ip_address in ip_addresses:
            try:
                # Get xx.xx.xx.xx based grub config file
                ip_address_path = _get_pxe_ip_address_path(port_ip_address,
                                                           False)
                # NOTE(TheJulia): Remove elilo support after the deprecation
                # period, in the Queens release.
                # Get 0AOAOAOA based elilo config file
                hex_ip_path = _get_pxe_ip_address_path(port_ip_address,
                                                       True)
            except exception.InvalidIPv4Address:
                continue
            except exception.FailedToGetIPAddressOnPort:
                continue
            # Cleaning up config files created for grub2.
            ironic_utils.unlink_without_raise(ip_address_path)
            # Cleaning up config files created for elilo.
            ironic_utils.unlink_without_raise(hex_ip_path)

    for port in task.ports:
        client_id = port.extra.get('client-id')
        # syslinux, ipxe, etc.
        ironic_utils.unlink_without_raise(
            _get_pxe_mac_path(port.address, client_id=client_id,
                              ipxe_enabled=ipxe_enabled))
        # Grub2 MAC address based confiuration
        ironic_utils.unlink_without_raise(
            _get_pxe_grub_mac_path(port.address))
    if ipxe_enabled:
        utils.rmtree_without_raise(os.path.join(get_ipxe_root_dir(),
                                                task.node.uuid))
    else:
        utils.rmtree_without_raise(os.path.join(get_root_dir(),
                                                task.node.uuid))
Esempio n. 47
0
def clean_up_pxe_config(task):
    """Clean up the TFTP environment for the task's node.

    :param task: A TaskManager instance.

    """
    LOG.debug("Cleaning up PXE config for node %s", task.node.uuid)

    is_uefi_boot_mode = deploy_utils.get_boot_mode_for_deploy(task.node) == "uefi"
    if is_uefi_boot_mode and not CONF.pxe.ipxe_enabled:
        api = dhcp_factory.DHCPFactory().provider
        ip_addresses = api.get_ip_addresses(task)
        if not ip_addresses:
            return

        for port_ip_address in ip_addresses:
            try:
                # Get xx.xx.xx.xx based grub config file
                ip_address_path = _get_pxe_ip_address_path(port_ip_address, False)
                # Get 0AOAOAOA based elilo config file
                hex_ip_path = _get_pxe_ip_address_path(port_ip_address, True)
            except exception.InvalidIPv4Address:
                continue
            # Cleaning up config files created for grub2.
            ironic_utils.unlink_without_raise(ip_address_path)
            # Cleaning up config files created for elilo.
            ironic_utils.unlink_without_raise(hex_ip_path)
    else:
        for port in task.ports:
            client_id = port.extra.get("client-id")
            ironic_utils.unlink_without_raise(_get_pxe_mac_path(port.address, client_id=client_id))

    utils.rmtree_without_raise(os.path.join(get_root_dir(), task.node.uuid))
Esempio n. 48
0
def clean_up_pxe_config(task, ipxe_enabled=False):
    """Clean up the TFTP environment for the task's node.

    :param task: A TaskManager instance.

    """
    LOG.debug("Cleaning up PXE config for node %s", task.node.uuid)

    is_uefi_boot_mode = (boot_mode_utils.get_boot_mode(task.node) == 'uefi')

    if is_uefi_boot_mode and not ipxe_enabled:
        api = dhcp_factory.DHCPFactory().provider
        ip_addresses = api.get_ip_addresses(task)
        if not ip_addresses:
            return

        for port_ip_address in ip_addresses:
            try:
                # Get xx.xx.xx.xx based grub config file
                ip_address_path = _get_pxe_ip_address_path(port_ip_address,
                                                           False)
                # NOTE(TheJulia): Remove elilo support after the deprecation
                # period, in the Queens release.
                # Get 0AOAOAOA based elilo config file
                hex_ip_path = _get_pxe_ip_address_path(port_ip_address,
                                                       True)
            except exception.InvalidIPv4Address:
                continue
            except exception.FailedToGetIPAddressOnPort:
                continue
            # Cleaning up config files created for grub2.
            ironic_utils.unlink_without_raise(ip_address_path)
            # Cleaning up config files created for elilo.
            ironic_utils.unlink_without_raise(hex_ip_path)

    for port in task.ports:
        client_id = port.extra.get('client-id')
        # syslinux, ipxe, etc.
        ironic_utils.unlink_without_raise(
            _get_pxe_mac_path(port.address, client_id=client_id,
                              ipxe_enabled=ipxe_enabled))
        # Grub2 MAC address based confiuration
        ironic_utils.unlink_without_raise(
            _get_pxe_grub_mac_path(port.address))
    if ipxe_enabled:
        utils.rmtree_without_raise(os.path.join(get_ipxe_root_dir(),
                                                task.node.uuid))
    else:
        utils.rmtree_without_raise(os.path.join(get_root_dir(),
                                                task.node.uuid))
Esempio n. 49
0
def _clean_up_boot_iso_for_instance(node):
    """Deletes the boot ISO if it was created for the instance.

    :param node: an ironic node object.
    """
    boot_iso_object_name = virtual_media_base.get_iso_image_name(node)

    if CONF.ilo.use_web_server_for_images:
        boot_iso_path = os.path.join(CONF.deploy.http_root,
                                     boot_iso_object_name)
        ironic_utils.unlink_without_raise(boot_iso_path)
    else:
        swift_api = swift.SwiftAPI()
        container = CONF.ilo.swift_ilo_container
        try:
            swift_api.delete_object(container, boot_iso_object_name)
        except exception.SwiftOperationError as e:
            LOG.exception(
                "Failed to clean up boot ISO for node "
                "%(node)s. Error: %(error)s.", {
                    'node': node.uuid,
                    'error': e
                })
Esempio n. 50
0
def _clean_up_boot_iso_for_instance(node):
    """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:
        return
    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("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)
        ironic_utils.unlink_without_raise(boot_iso_path)
Esempio n. 51
0
def clean_up_pxe_config(task):
    """Clean up the TFTP environment for the task's node.

    :param task: A TaskManager instance.

    """
    LOG.debug("Cleaning up PXE config for node %s", task.node.uuid)

    is_uefi_boot_mode = (deploy_utils.get_boot_mode_for_deploy(task.node) ==
                         'uefi')
    if is_uefi_boot_mode and not CONF.pxe.ipxe_enabled:
        api = dhcp_factory.DHCPFactory().provider
        ip_addresses = api.get_ip_addresses(task)
        if not ip_addresses:
            return

        for port_ip_address in ip_addresses:
            try:
                # Get xx.xx.xx.xx based grub config file
                ip_address_path = _get_pxe_ip_address_path(port_ip_address,
                                                           False)
                # Get 0AOAOAOA based elilo config file
                hex_ip_path = _get_pxe_ip_address_path(port_ip_address,
                                                       True)
            except exception.InvalidIPv4Address:
                continue
            # Cleaning up config files created for grub2.
            ironic_utils.unlink_without_raise(ip_address_path)
            # Cleaning up config files created for elilo.
            ironic_utils.unlink_without_raise(hex_ip_path)
    else:
        for mac in driver_utils.get_node_mac_addresses(task):
            ironic_utils.unlink_without_raise(_get_pxe_mac_path(mac))
            # TODO(lucasagomes): Backward compatibility with :hexraw,
            # to be removed in Mitaka.
            # see: https://bugs.launchpad.net/ironic/+bug/1441710
            if CONF.pxe.ipxe_enabled:
                ironic_utils.unlink_without_raise(_get_pxe_mac_path(mac,
                                                  delimiter=''))

    utils.rmtree_without_raise(os.path.join(get_root_dir(),
                                            task.node.uuid))
Esempio n. 52
0
 def create_link(mac_path):
     ironic_utils.unlink_without_raise(mac_path)
     relative_source_path = os.path.relpath(
         pxe_config_file_path, os.path.dirname(mac_path))
     utils.create_link_without_raise(relative_source_path, mac_path)
Esempio n. 53
0
 def create_link(mac_path):
     ironic_utils.unlink_without_raise(mac_path)
     relative_source_path = os.path.relpath(pxe_config_file_path,
                                            os.path.dirname(mac_path))
     utils.create_link_without_raise(relative_source_path, mac_path)
Esempio n. 54
0
def create_config_drive_partition(node_uuid, device, configdrive):
    """Create a partition for config drive

    Checks if the device is GPT or MBR partitioned and creates config drive
    partition accordingly.

    :param node_uuid: UUID of the Node.
    :param device: The device path.
    :param configdrive: Base64 encoded Gzipped configdrive content or
        configdrive HTTP URL.
    :raises: InstanceDeployFailure if config drive size exceeds maximum limit
        or if it fails to create config drive.
    """
    confdrive_file = None
    try:
        config_drive_part = _get_labelled_partition(device,
                                                    CONFIGDRIVE_LABEL,
                                                    node_uuid)

        confdrive_mb, confdrive_file = _get_configdrive(configdrive,
                                                        node_uuid)
        if confdrive_mb > MAX_CONFIG_DRIVE_SIZE_MB:
                raise exception.InstanceDeployFailure(
                    _('Config drive size exceeds maximum limit of 64MiB. '
                      'Size of the given config drive is %(size)d MiB for '
                      'node %(node)s.')
                    % {'size': confdrive_mb, 'node': node_uuid})

        LOG.debug("Adding config drive partition %(size)d MiB to "
                  "device: %(dev)s for node %(node)s",
                  {'dev': device, 'size': confdrive_mb, 'node': node_uuid})

        if config_drive_part:
            LOG.debug("Configdrive for node %(node)s exists at "
                      "%(part)s",
                      {'node': node_uuid, 'part': config_drive_part})
        else:
            cur_parts = set(part['number'] for part in list_partitions(device))

            if _is_disk_gpt_partitioned(device, node_uuid):
                _fix_gpt_structs(device, node_uuid)
                create_option = '0:-%dMB:0' % MAX_CONFIG_DRIVE_SIZE_MB
                utils.execute('sgdisk', '-n', create_option, device,
                              run_as_root=True)
            else:
                # Check if the disk has 4 partitions. The MBR based disk
                # cannot have more than 4 partitions.
                # TODO(stendulker): One can use logical partitions to create
                # a config drive if there are 3 primary partitions.
                # https://bugs.launchpad.net/ironic/+bug/1561283
                try:
                    pp_count, lp_count = count_mbr_partitions(device)
                except ValueError as e:
                    raise exception.InstanceDeployFailure(
                        _('Failed to check the number of primary partitions '
                          'present on %(dev)s for node %(node)s. Error: '
                          '%(error)s') % {'dev': device, 'node': node_uuid,
                                          'error': e})
                if pp_count > 3:
                    raise exception.InstanceDeployFailure(
                        _('Config drive cannot be created for node %(node)s. '
                          'Disk (%(dev)s) uses MBR partitioning and already '
                          'has %(parts)d primary partitions.')
                        % {'node': node_uuid, 'dev': device,
                           'parts': pp_count})

                # Check if disk size exceeds 2TB msdos limit
                startlimit = '-%dMiB' % MAX_CONFIG_DRIVE_SIZE_MB
                endlimit = '-0'
                if _is_disk_larger_than_max_size(device, node_uuid):
                    # Need to create a small partition at 2TB limit
                    LOG.warning(_LW("Disk size is larger than 2TB for "
                                    "node %(node)s. Creating config drive "
                                    "at the end of the disk %(disk)s."),
                                {'node': node_uuid, 'disk': device})
                    startlimit = (MAX_DISK_SIZE_MB_SUPPORTED_BY_MBR -
                                  MAX_CONFIG_DRIVE_SIZE_MB - 1)
                    endlimit = MAX_DISK_SIZE_MB_SUPPORTED_BY_MBR - 1

                utils.execute('parted', '-a', 'optimal', '-s', '--', device,
                              'mkpart', 'primary', 'ext2', startlimit,
                              endlimit, run_as_root=True)

            upd_parts = set(part['number'] for part in list_partitions(device))
            new_part = set(upd_parts) - set(cur_parts)
            if len(new_part) != 1:
                raise exception.InstanceDeployFailure(
                    _('Disk partitioning failed on device %(device)s. '
                      'Unable to retrieve config drive partition information.')
                    % {'device': device})

            if is_iscsi_device(device, node_uuid):
                config_drive_part = '%s-part%s' % (device, new_part.pop())
            else:
                config_drive_part = '%s%s' % (device, new_part.pop())

            # NOTE(vdrok): the partition was created successfully, let's wait
            # for it to appear in /dev.
            LOG.debug('Waiting for the config drive partition %(part)s '
                      'on node %(node)s to be ready for writing.',
                      {'part': config_drive_part, 'node': node_uuid})
            utils.execute('udevadm', 'settle',
                          '--exit-if-exists=%s' % config_drive_part)

        dd(confdrive_file, config_drive_part)
        LOG.info(_LI("Configdrive for node %(node)s successfully "
                     "copied onto partition %(part)s"),
                 {'node': node_uuid, 'part': config_drive_part})

    except (processutils.UnknownArgumentError,
            processutils.ProcessExecutionError, OSError) as e:
        msg = (_('Failed to create config drive on disk %(disk)s '
                 'for node %(node)s. Error: %(error)s') %
               {'disk': device, 'node': node_uuid, 'error': e})
        LOG.error(msg)
        raise exception.InstanceDeployFailure(msg)
    finally:
        # If the configdrive was requested make sure we delete the file
        # after copying the content to the partition
        if confdrive_file:
            utils.unlink_without_raise(confdrive_file)
Esempio n. 55
0
 def test_unlink_ENOENT(self):
     with mock.patch.object(os, "unlink", autospec=True) as unlink_mock:
         unlink_mock.side_effect = OSError(errno.ENOENT)
         utils.unlink_without_raise("/fake/path")
         unlink_mock.assert_called_once_with("/fake/path")
Esempio n. 56
0
def work_on_disk(dev, root_mb, swap_mb, ephemeral_mb, ephemeral_format,
                 image_path, node_uuid, preserve_ephemeral=False,
                 configdrive=None, boot_option="netboot", boot_mode="bios",
                 tempdir=None, disk_label=None):
    """Create partitions and copy an image to the root partition.

    :param dev: Path for the device to work on.
    :param root_mb: Size of the root partition in megabytes.
    :param swap_mb: Size of the swap partition in megabytes.
    :param ephemeral_mb: Size of the ephemeral partition in megabytes. If 0,
        no ephemeral partition will be created.
    :param ephemeral_format: The type of file system to format the ephemeral
        partition.
    :param image_path: Path for the instance's disk image.
    :param node_uuid: node's uuid. Used for logging.
    :param preserve_ephemeral: If True, no filesystem is written to the
        ephemeral block device, preserving whatever content it had (if the
        partition table has not changed).
    :param configdrive: Optional. Base64 encoded Gzipped configdrive content
                        or configdrive HTTP URL.
    :param boot_option: Can be "local" or "netboot". "netboot" by default.
    :param boot_mode: Can be "bios" or "uefi". "bios" by default.
    :param tempdir: A temporary directory
    :param disk_label: The disk label to be used when creating the
        partition table. Valid values are: "msdos", "gpt" or None; If None
        Ironic will figure it out according to the boot_mode parameter.
    :returns: a dictionary containing the following keys:
        'root uuid': UUID of root partition
        'efi system partition uuid': UUID of the uefi system partition
                                     (if boot mode is uefi).
        NOTE: If key exists but value is None, it means partition doesn't
              exist.
    """
    # the only way for preserve_ephemeral to be set to true is if we are
    # rebuilding an instance with --preserve_ephemeral.
    commit = not preserve_ephemeral
    # now if we are committing the changes to disk clean first.
    if commit:
        destroy_disk_metadata(dev, node_uuid)

    try:
        # If requested, get the configdrive file and determine the size
        # of the configdrive partition
        configdrive_mb = 0
        configdrive_file = None
        if configdrive:
            configdrive_mb, configdrive_file = _get_configdrive(
                configdrive, node_uuid, tempdir=tempdir)

        part_dict = make_partitions(dev, root_mb, swap_mb, ephemeral_mb,
                                    configdrive_mb, node_uuid,
                                    commit=commit,
                                    boot_option=boot_option,
                                    boot_mode=boot_mode,
                                    disk_label=disk_label)
        LOG.info(_LI("Successfully completed the disk device"
                     " %(dev)s partitioning for node %(node)s"),
                 {'dev': dev, "node": node_uuid})

        ephemeral_part = part_dict.get('ephemeral')
        swap_part = part_dict.get('swap')
        configdrive_part = part_dict.get('configdrive')
        root_part = part_dict.get('root')

        if not is_block_device(root_part):
            raise exception.InstanceDeployFailure(
                _("Root device '%s' not found") % root_part)

        for part in ('swap', 'ephemeral', 'configdrive',
                     'efi system partition'):
            part_device = part_dict.get(part)
            LOG.debug("Checking for %(part)s device (%(dev)s) on node "
                      "%(node)s.", {'part': part, 'dev': part_device,
                                    'node': node_uuid})
            if part_device and not is_block_device(part_device):
                raise exception.InstanceDeployFailure(
                    _("'%(partition)s' device '%(part_device)s' not found") %
                    {'partition': part, 'part_device': part_device})

        # If it's a uefi localboot, then we have created the efi system
        # partition.  Create a fat filesystem on it.
        if boot_mode == "uefi" and boot_option == "local":
            efi_system_part = part_dict.get('efi system partition')
            mkfs(dev=efi_system_part, fs='vfat', label='efi-part')

        if configdrive_part:
            # Copy the configdrive content to the configdrive partition
            dd(configdrive_file, configdrive_part)
            LOG.info(_LI("Configdrive for node %(node)s successfully copied "
                         "onto partition %(partition)s"),
                     {'node': node_uuid, 'partition': configdrive_part})

    finally:
        # If the configdrive was requested make sure we delete the file
        # after copying the content to the partition
        if configdrive_file:
            utils.unlink_without_raise(configdrive_file)

    populate_image(image_path, root_part)
    LOG.info(_LI("Image for %(node)s successfully populated"),
             {'node': node_uuid})

    if swap_part:
        mkfs(dev=swap_part, fs='swap', label='swap1')
        LOG.info(_LI("Swap partition %(swap)s successfully formatted "
                     "for node %(node)s"),
                 {'swap': swap_part, 'node': node_uuid})

    if ephemeral_part and not preserve_ephemeral:
        mkfs(dev=ephemeral_part, fs=ephemeral_format, label="ephemeral0")
        LOG.info(_LI("Ephemeral partition %(ephemeral)s successfully "
                     "formatted for node %(node)s"),
                 {'ephemeral': ephemeral_part, 'node': node_uuid})

    uuids_to_return = {
        'root uuid': root_part,
        'efi system partition uuid': part_dict.get('efi system partition')
    }

    try:
        for part, part_dev in uuids_to_return.items():
            if part_dev:
                uuids_to_return[part] = block_uuid(part_dev)

    except processutils.ProcessExecutionError:
        with excutils.save_and_reraise_exception():
            LOG.error(_LE("Failed to detect %s"), part)

    return uuids_to_return
Esempio n. 57
0
 def test_unlink(self):
     with mock.patch.object(os, "unlink", autospec=True) as unlink_mock:
         unlink_mock.return_value = None
         utils.unlink_without_raise("/fake/path")
         unlink_mock.assert_called_once_with("/fake/path")