Example #1
0
    def rebuild(self, context, server):
        """Rebuild/redeploy a server.

        :param context: The security context.
        :param server: The server object.
        """
        LOG.debug('Rebuild called for server', server=server)

        node_uuid = server.node_uuid
        node = self._get_node(node_uuid)
        self._add_server_info_to_node(node, server)

        # trigger the node rebuild
        try:
            self.ironicclient.call("node.set_provision_state", node_uuid,
                                   ironic_states.REBUILD)
        except (ironic_exc.InternalServerError, ironic_exc.BadRequest) as e:
            msg = (_("Failed to request Ironic to rebuild server "
                     "%(server)s: %(reason)s") % {
                         'server': server.uuid,
                         'reason': six.text_type(e)
                     })
            raise exception.ServerDeployFailure(msg)

        # Although the target provision state is REBUILD, it will actually go
        # to ACTIVE once the redeploy is finished.
        timer = loopingcall.FixedIntervalLoopingCall(self._wait_for_active,
                                                     server)
        timer.start(interval=CONF.ironic.api_retry_interval).wait()
        LOG.info('Server was successfully rebuilt', server=server)
Example #2
0
    def _wait_for_active(self, server):
        """Wait for the node to be marked as ACTIVE in Ironic."""
        server.refresh()
        if server.status in (states.DELETING, states.ERROR, states.DELETED):
            raise exception.ServerDeployAborted(
                _("Server %s provisioning was aborted") % server.uuid)

        node = self._validate_server_and_node(server)
        if node.provision_state == ironic_states.ACTIVE:
            # job is done
            LOG.debug("Ironic node %(node)s is now ACTIVE",
                      dict(node=node.uuid),
                      server=server)
            raise loopingcall.LoopingCallDone()

        if node.target_provision_state in (ironic_states.DELETED,
                                           ironic_states.AVAILABLE):
            # ironic is trying to delete it now
            raise exception.ServerNotFound(server=server.uuid)

        if node.provision_state in (ironic_states.NOSTATE,
                                    ironic_states.AVAILABLE):
            # ironic already deleted it
            raise exception.ServerNotFound(server=server.uuid)

        if node.provision_state == ironic_states.DEPLOYFAIL:
            # ironic failed to deploy
            msg = (_("Failed to provision server %(server)s: %(reason)s") % {
                'server': server.uuid,
                'reason': node.last_error
            })
            raise exception.ServerDeployFailure(msg)

        _log_ironic_polling('become ACTIVE', node, server)
Example #3
0
    def _wait_for_servers_status(cls,
                                 server_id,
                                 wait_interval,
                                 wait_timeout,
                                 status=None,
                                 power_state=None,
                                 locked=None):
        """Waits for a Server to reach the given status, power_state,
        lock state.
        """

        server_status = None
        server_power_state = None
        server_locked = None
        start = int(time.time())

        def _condition():
            compare_pairs = ((status, server_status), (power_state,
                                                       server_power_state),
                             (locked, server_locked))
            return all([r == a for r, a in compare_pairs if r is not None])

        while not _condition():
            time.sleep(wait_interval)
            try:
                body = cls.baremetal_compute_client.show_server(server_id)
                server_status = body['status']
                server_power_state = body['power_state']
                server_locked = body['locked']
            except lib_exc.NotFound:
                if status == 'deleted':
                    break
                else:
                    raise
            if server_status == 'error' and status != 'error':
                msg = ('Failed to provision server %s' % server_id)
                raise exception.ServerDeployFailure(msg)

            if int(time.time()) - start >= wait_timeout:
                message = ('Server %s failed to reach %s status '
                           '(current %s) within the required time (%s s).' %
                           (server_id, status, server_status, wait_timeout))
                raise lib_exc.TimeoutException(message)
Example #4
0
    def manage(self, server, node_uuid):
        """Manage an existing bare metal node.

        :param server: The bare metal server object.
        :param node_uuid: The manageable bare metal node uuid.
        """
        # Associate the node with a server
        patch = [{'path': '/instance_uuid', 'op': 'add', 'value': server.uuid}]

        try:
            self.ironicclient.call('node.update',
                                   node_uuid,
                                   patch,
                                   retry_on_conflict=False)
        except ironic_exc.BadRequest:
            msg = (_("Failed to update parameters on node %(node)s "
                     "when provisioning the server %(server)s") % {
                         'node': node_uuid,
                         'server': server.uuid
                     })
            LOG.error(msg)
            raise exception.ServerDeployFailure(msg)
Example #5
0
    def _add_server_info_to_node(self, node, server):

        patch = list()
        # Associate the node with a server
        patch.append({
            'path': '/instance_uuid',
            'op': 'add',
            'value': server.uuid
        })
        # Add the required fields to deploy a node.
        patch.append({
            'path': '/instance_info/image_source',
            'op': 'add',
            'value': server.image_uuid
        })
        # TODO(zhenguo) Add partition support
        patch.append({
            'path': '/instance_info/root_gb',
            'op': 'add',
            'value': str(node.properties.get('local_gb', 0))
        })

        try:
            # FIXME(lucasagomes): The "retry_on_conflict" parameter was added
            # to basically causes the deployment to fail faster in case the
            # node picked by the scheduler is already associated with another
            # server due bug #1341420.
            self.ironicclient.call('node.update',
                                   node.uuid,
                                   patch,
                                   retry_on_conflict=False)
        except ironic_exc.BadRequest:
            msg = (_("Failed to add deploy parameters on node %(node)s "
                     "when provisioning the server %(server)s") % {
                         'node': node.uuid,
                         'server': server.uuid
                     })
            LOG.error(msg)
            raise exception.ServerDeployFailure(msg)
Example #6
0
    def _add_server_info_to_node(self,
                                 node,
                                 server,
                                 preserve_ephemeral=None,
                                 partitions=None):
        patch = list()
        # Associate the node with a server
        patch.append({
            'path': '/instance_uuid',
            'op': 'add',
            'value': server.uuid
        })
        # Add the required fields to deploy a node.
        patch.append({
            'path': '/instance_info/image_source',
            'op': 'add',
            'value': server.image_uuid
        })

        root_gb = node.properties.get('local_gb', 0)

        if preserve_ephemeral is not None:
            patch.append({
                'path': '/instance_info/preserve_ephemeral',
                'op': 'add',
                'value': str(preserve_ephemeral)
            })
        if partitions:
            patch.append({
                'path': '/instance_info/ephemeral_gb',
                'op': 'add',
                'value': str(partitions.get('ephemeral_gb', 0))
            })
            patch.append({
                'path': '/instance_info/swap_mb',
                'op': 'add',
                'value': str(partitions.get('swap_mb', 0))
            })
            # Local boot support with partition images, **must** contain
            # ``grub2`` installed within it
            patch.append({
                'path': '/instance_info/capabilities',
                'op': 'add',
                'value': '{"boot_option": "local"}'
            })

            # If partitions is not None, use the root_gb in partitions instead
            root_gb = partitions.get('root_gb', root_gb)

        # root_gb is required not optional
        patch.append({
            'path': '/instance_info/root_gb',
            'op': 'add',
            'value': str(root_gb)
        })

        try:
            # FIXME(lucasagomes): The "retry_on_conflict" parameter was added
            # to basically causes the deployment to fail faster in case the
            # node picked by the scheduler is already associated with another
            # server due bug #1341420.
            self.ironicclient.call('node.update',
                                   node.uuid,
                                   patch,
                                   retry_on_conflict=False)
        except ironic_exc.BadRequest:
            msg = (_("Failed to add deploy parameters on node %(node)s "
                     "when provisioning the server %(server)s") % {
                         'node': node.uuid,
                         'server': server.uuid
                     })
            LOG.error(msg)
            raise exception.ServerDeployFailure(msg)