~ubuntu-branches/ubuntu/vivid/ironic/vivid-updates

« back to all changes in this revision

Viewing changes to ironic/conductor/manager.py

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2015-01-05 12:21:37 UTC
  • mfrom: (1.2.4)
  • Revision ID: package-import@ubuntu.com-20150105122137-171bqrdpcxqipunk
Tags: 2015.1~b1-0ubuntu1
* New upstream beta release:
  - d/control: Align version requirements with upstream release.
* d/watch: Update uversionmangle to deal with kilo beta versioning
  changes.
* d/control: Bumped Standards-Version to 3.9.6, no changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
47
47
 
48
48
import eventlet
49
49
from eventlet import greenpool
50
 
 
51
50
from oslo.config import cfg
 
51
from oslo.db import exception as db_exception
52
52
from oslo import messaging
53
53
from oslo.utils import excutils
 
54
from oslo_concurrency import lockutils
54
55
 
55
56
from ironic.common import dhcp_factory
56
57
from ironic.common import driver_factory
70
71
from ironic.db import api as dbapi
71
72
from ironic import objects
72
73
from ironic.openstack.common import context as ironic_context
73
 
from ironic.openstack.common import lockutils
74
74
from ironic.openstack.common import log
75
75
from ironic.openstack.common import periodic_task
76
76
 
162
162
    """Ironic Conductor manager main class."""
163
163
 
164
164
    # NOTE(rloo): This must be in sync with rpcapi.ConductorAPI's.
165
 
    RPC_API_VERSION = '1.17'
 
165
    RPC_API_VERSION = '1.21'
166
166
 
167
167
    target = messaging.Target(version=RPC_API_VERSION)
168
168
 
270
270
            raise exception.IronicException(_(
271
271
                "Invalid method call: update_node can not change node state."))
272
272
 
 
273
        # NOTE(jroll) clear maintenance_reason if node.update sets
 
274
        # maintenance to False for backwards compatibility, for tools
 
275
        # not using the maintenance endpoint.
 
276
        if 'maintenance' in delta and not node_obj.maintenance:
 
277
            node_obj.maintenance_reason = None
 
278
 
273
279
        driver_name = node_obj.driver if 'driver' in delta else None
274
280
        with task_manager.acquire(context, node_id, shared=False,
275
281
                                  driver_name=driver_name) as task:
278
284
            #             instance_uuid needs to be unset, and handle it.
279
285
            if 'instance_uuid' in delta:
280
286
                task.driver.power.validate(task)
281
 
                node_obj.power_state = \
282
 
                        task.driver.power.get_power_state(task)
 
287
                node_obj.power_state = task.driver.power.get_power_state(task)
283
288
 
284
289
                if node_obj.power_state != states.POWER_OFF:
285
290
                    raise exception.NodeInWrongPowerState(
309
314
            node.save()
310
315
            LOG.warning(_LW("No free conductor workers available to perform "
311
316
                            "an action on node %(node)s, setting node's "
312
 
                            "power state back to %(power_state)s.",
313
 
                            {'node': node.uuid, 'power_state': power_state}))
 
317
                            "power state back to %(power_state)s."),
 
318
                            {'node': node.uuid, 'power_state': power_state})
314
319
 
315
320
    @messaging.expected_exceptions(exception.InvalidParameterValue,
316
321
                                   exception.MissingParameterValue,
357
362
                                   exception.InvalidParameterValue,
358
363
                                   exception.UnsupportedDriverExtension,
359
364
                                   exception.MissingParameterValue)
360
 
    def vendor_passthru(self, context, node_id, driver_method, info):
 
365
    def vendor_passthru(self, context, node_id, driver_method,
 
366
                        http_method, info):
361
367
        """RPC method to encapsulate vendor action.
362
368
 
363
369
        Synchronously validate driver specific info or get driver status,
364
 
        and if successful, start background worker to perform vendor action
365
 
        asynchronously.
 
370
        and if successful invokes the vendor method. If the method mode
 
371
        is 'async' the conductor will start background worker to perform
 
372
        vendor action.
366
373
 
367
374
        :param context: an admin context.
368
375
        :param node_id: the id or uuid of a node.
369
376
        :param driver_method: the name of the vendor method.
 
377
        :param http_method: the HTTP method used for the request.
370
378
        :param info: vendor method args.
371
379
        :raises: InvalidParameterValue if supplied info is not valid.
372
380
        :raises: MissingParameterValue if missing supplied info
374
382
                 vendor interface or method is unsupported.
375
383
        :raises: NoFreeConductorWorker when there is no free worker to start
376
384
                 async task.
377
 
 
 
385
        :raises: NodeLocked if node is locked by another conductor.
 
386
        :returns: A tuple containing the response of the invoked method
 
387
                  and a boolean value indicating whether the method was
 
388
                  invoked asynchronously (True) or synchronously (False).
 
389
                  If invoked asynchronously the response field will be
 
390
                  always None.
378
391
        """
379
392
        LOG.debug("RPC vendor_passthru called for node %s." % node_id)
380
393
        # NOTE(max_lobur): Even though not all vendor_passthru calls may
385
398
            if not getattr(task.driver, 'vendor', None):
386
399
                raise exception.UnsupportedDriverExtension(
387
400
                    driver=task.node.driver,
388
 
                    extension='vendor passthru')
389
 
 
390
 
            task.driver.vendor.validate(task, method=driver_method,
391
 
                                        **info)
392
 
            task.spawn_after(self._spawn_worker,
393
 
                             task.driver.vendor.vendor_passthru, task,
394
 
                             method=driver_method, **info)
395
 
 
396
 
    @messaging.expected_exceptions(exception.InvalidParameterValue,
 
401
                    extension='vendor interface')
 
402
 
 
403
            vendor_iface = task.driver.vendor
 
404
 
 
405
            # NOTE(lucasagomes): Before the vendor_passthru() method was
 
406
            # a self-contained method and each driver implemented their own
 
407
            # version of it, now we have a common mechanism that drivers
 
408
            # should use to expose their vendor methods. If a driver still
 
409
            # have their own vendor_passthru() method we call it to be
 
410
            # backward compat. This code should be removed once L opens.
 
411
            if hasattr(vendor_iface, 'vendor_passthru'):
 
412
                LOG.warning(_LW("Drivers implementing their own version "
 
413
                                "of vendor_passthru() has been deprecated. "
 
414
                                "Please update the code to use the "
 
415
                                "@passthru decorator."))
 
416
                vendor_iface.validate(task, method=driver_method,
 
417
                                            **info)
 
418
                task.spawn_after(self._spawn_worker,
 
419
                                 vendor_iface.vendor_passthru, task,
 
420
                                 method=driver_method, **info)
 
421
                # NodeVendorPassthru was always async
 
422
                return (None, True)
 
423
 
 
424
            try:
 
425
                vendor_opts = vendor_iface.vendor_routes[driver_method]
 
426
                vendor_func = vendor_opts['func']
 
427
            except KeyError:
 
428
                raise exception.InvalidParameterValue(
 
429
                    _('No handler for method %s') % driver_method)
 
430
 
 
431
            http_method = http_method.upper()
 
432
            if http_method not in vendor_opts['http_methods']:
 
433
                raise exception.InvalidParameterValue(
 
434
                    _('The method %(method)s does not support HTTP %(http)s') %
 
435
                    {'method': driver_method, 'http': http_method})
 
436
 
 
437
            vendor_iface.validate(task, method=driver_method,
 
438
                                  http_method=http_method, **info)
 
439
 
 
440
            # Inform the vendor method which HTTP method it was invoked with
 
441
            info['http_method'] = http_method
 
442
 
 
443
            # Invoke the vendor method accordingly with the mode
 
444
            is_async = vendor_opts['async']
 
445
            ret = None
 
446
            if is_async:
 
447
                task.spawn_after(self._spawn_worker, vendor_func, task, **info)
 
448
            else:
 
449
                ret = vendor_func(task, **info)
 
450
 
 
451
            return (ret, is_async)
 
452
 
 
453
    @messaging.expected_exceptions(exception.NoFreeConductorWorker,
 
454
                                   exception.InvalidParameterValue,
397
455
                                   exception.MissingParameterValue,
398
456
                                   exception.UnsupportedDriverExtension,
399
457
                                   exception.DriverNotFound)
400
458
    def driver_vendor_passthru(self, context, driver_name, driver_method,
401
 
                                  info):
402
 
        """RPC method which synchronously handles driver-level vendor passthru
403
 
        calls. These calls don't require a node UUID and are executed on a
404
 
        random conductor with the specified driver.
 
459
                               http_method, info):
 
460
        """Handle top-level vendor actions.
 
461
 
 
462
        RPC method which handles driver-level vendor passthru calls. These
 
463
        calls don't require a node UUID and are executed on a random
 
464
        conductor with the specified driver. If the method mode is
 
465
        async the conductor will start background worker to perform
 
466
        vendor action.
405
467
 
406
468
        :param context: an admin context.
407
469
        :param driver_name: name of the driver on which to call the method.
408
470
        :param driver_method: name of the vendor method, for use by the driver.
 
471
        :param http_method: the HTTP method used for the request.
409
472
        :param info: user-supplied data to pass through to the driver.
410
473
        :raises: MissingParameterValue if missing supplied info
411
474
        :raises: InvalidParameterValue if supplied info is not valid.
414
477
                 driver-level vendor passthru or if the passthru method is
415
478
                 unsupported.
416
479
        :raises: DriverNotFound if the supplied driver is not loaded.
417
 
 
 
480
        :raises: NoFreeConductorWorker when there is no free worker to start
 
481
                 async task.
 
482
        :returns: A tuple containing the response of the invoked method
 
483
                  and a boolean value indicating whether the method was
 
484
                  invoked asynchronously (True) or synchronously (False).
 
485
                  If invoked asynchronously the response field will be
 
486
                  always None.
418
487
        """
419
488
        # Any locking in a top-level vendor action will need to be done by the
420
489
        # implementation, as there is little we could reasonably lock on here.
425
494
                driver=driver_name,
426
495
                extension='vendor interface')
427
496
 
428
 
        return driver.vendor.driver_vendor_passthru(context,
429
 
                                                    method=driver_method,
430
 
                                                    **info)
 
497
        # NOTE(lucasagomes): Before the driver_vendor_passthru()
 
498
        # method was a self-contained method and each driver implemented
 
499
        # their own version of it, now we have a common mechanism that
 
500
        # drivers should use to expose their vendor methods. If a driver
 
501
        # still have their own driver_vendor_passthru() method we call
 
502
        # it to be backward compat. This code should be removed
 
503
        # once L opens.
 
504
        if hasattr(driver.vendor, 'driver_vendor_passthru'):
 
505
            LOG.warning(_LW("Drivers implementing their own version "
 
506
                            "of driver_vendor_passthru() has been "
 
507
                            "deprecated. Please update the code to use "
 
508
                            "the @driver_passthru decorator."))
 
509
 
 
510
            driver.vendor.driver_validate(method=driver_method, **info)
 
511
            ret = driver.vendor.driver_vendor_passthru(
 
512
                            context, method=driver_method, **info)
 
513
            # DriverVendorPassthru was always sync
 
514
            return (ret, False)
 
515
 
 
516
        try:
 
517
            vendor_opts = driver.vendor.driver_routes[driver_method]
 
518
            vendor_func = vendor_opts['func']
 
519
        except KeyError:
 
520
            raise exception.InvalidParameterValue(
 
521
                _('No handler for method %s') % driver_method)
 
522
 
 
523
        http_method = http_method.upper()
 
524
        if http_method not in vendor_opts['http_methods']:
 
525
            raise exception.InvalidParameterValue(
 
526
                _('The method %(method)s does not support HTTP %(http)s') %
 
527
                {'method': driver_method, 'http': http_method})
 
528
 
 
529
        # Inform the vendor method which HTTP method it was invoked with
 
530
        info['http_method'] = http_method
 
531
 
 
532
        # Invoke the vendor method accordingly with the mode
 
533
        is_async = vendor_opts['async']
 
534
        ret = None
 
535
        driver.vendor.driver_validate(method=driver_method, **info)
 
536
 
 
537
        if is_async:
 
538
            self._spawn_worker(vendor_func, context, **info)
 
539
        else:
 
540
            ret = vendor_func(context, **info)
 
541
 
 
542
        return (ret, is_async)
 
543
 
 
544
    def _get_vendor_passthru_metadata(self, route_dict):
 
545
        d = {}
 
546
        for method, metadata in route_dict.iteritems():
 
547
            # 'func' is the vendor method reference, ignore it
 
548
            d[method] = {k: metadata[k] for k in metadata if k != 'func'}
 
549
        return d
 
550
 
 
551
    @messaging.expected_exceptions(exception.UnsupportedDriverExtension)
 
552
    def get_node_vendor_passthru_methods(self, context, node_id):
 
553
        """Retrieve information about vendor methods of the given node.
 
554
 
 
555
        :param context: an admin context.
 
556
        :param node_id: the id or uuid of a node.
 
557
        :returns: dictionary of <method name>:<method metadata> entries.
 
558
 
 
559
        """
 
560
        LOG.debug("RPC get_node_vendor_passthru_methods called for node %s"
 
561
                  % node_id)
 
562
        with task_manager.acquire(context, node_id, shared=True) as task:
 
563
            if not getattr(task.driver, 'vendor', None):
 
564
                raise exception.UnsupportedDriverExtension(
 
565
                    driver=task.node.driver,
 
566
                    extension='vendor interface')
 
567
 
 
568
            return self._get_vendor_passthru_metadata(
 
569
                       task.driver.vendor.vendor_routes)
 
570
 
 
571
    @messaging.expected_exceptions(exception.UnsupportedDriverExtension,
 
572
                                   exception.DriverNotFound)
 
573
    def get_driver_vendor_passthru_methods(self, context, driver_name):
 
574
        """Retrieve information about vendor methods of the given driver.
 
575
 
 
576
        :param context: an admin context.
 
577
        :param driver_name: name of the driver.
 
578
        :returns: dictionary of <method name>:<method metadata> entries.
 
579
 
 
580
        """
 
581
        # Any locking in a top-level vendor action will need to be done by the
 
582
        # implementation, as there is little we could reasonably lock on here.
 
583
        LOG.debug("RPC get_driver_vendor_passthru_methods for driver %s"
 
584
                  % driver_name)
 
585
        driver = self._get_driver(driver_name)
 
586
        if not getattr(driver, 'vendor', None):
 
587
            raise exception.UnsupportedDriverExtension(
 
588
                driver=driver_name,
 
589
                extension='vendor interface')
 
590
 
 
591
        return self._get_vendor_passthru_metadata(driver.vendor.driver_routes)
431
592
 
432
593
    def _provisioning_error_handler(self, e, node, provision_state,
433
594
                                    target_provision_state):
491
652
        # want to add retries or extra synchronization here.
492
653
        with task_manager.acquire(context, node_id, shared=False) as task:
493
654
            node = task.node
494
 
            # Only rebuild a node in ACTIVE, ERROR, or DEPLOYFAIL state
495
 
            rebuild_states = [states.ACTIVE,
496
 
                              states.ERROR,
497
 
                              states.DEPLOYFAIL]
498
 
            if rebuild and (node.provision_state not in rebuild_states):
499
 
                valid_states_string = ', '.join(rebuild_states)
500
 
                raise exception.InstanceDeployFailure(_(
501
 
                    "RPC do_node_deploy called to rebuild %(node)s, but "
502
 
                    "provision state is %(curstate)s. State must be one "
503
 
                    "of : %(states)s.") % {'node': node.uuid,
504
 
                     'curstate': node.provision_state,
505
 
                     'states': valid_states_string})
506
 
            elif node.provision_state != states.NOSTATE and not rebuild:
507
 
                raise exception.InstanceDeployFailure(_(
508
 
                    "RPC do_node_deploy called for %(node)s, but provision "
509
 
                    "state is already %(state)s.") %
510
 
                    {'node': node.uuid, 'state': node.provision_state})
511
 
 
512
655
            if node.maintenance:
513
656
                raise exception.NodeInMaintenance(op=_('provisioning'),
514
657
                                                  node=node.uuid)
515
 
 
516
658
            try:
 
659
                task.driver.power.validate(task)
517
660
                task.driver.deploy.validate(task)
518
661
            except (exception.InvalidParameterValue,
519
662
                    exception.MissingParameterValue) as e:
520
663
                raise exception.InstanceDeployFailure(_(
521
 
                    "RPC do_node_deploy failed to validate deploy info. "
522
 
                    "Error: %(msg)s") % {'msg': e})
 
664
                    "RPC do_node_deploy failed to validate deploy or "
 
665
                    "power info. Error: %(msg)s") % {'msg': e})
 
666
 
 
667
            if rebuild:
 
668
                event = 'rebuild'
 
669
            else:
 
670
                event = 'deploy'
523
671
 
524
672
            # Save the previous states so we can rollback the node to a
525
673
            # consistent state in case there's no free workers to do the
527
675
            previous_prov_state = node.provision_state
528
676
            previous_tgt_provision_state = node.target_provision_state
529
677
 
530
 
            # Set target state to expose that work is in progress
531
 
            node.provision_state = states.DEPLOYING
532
 
            node.target_provision_state = states.DEPLOYDONE
533
 
            node.last_error = None
534
 
            node.save()
535
 
 
536
 
            task.set_spawn_error_hook(self._provisioning_error_handler,
537
 
                                      node, previous_prov_state,
538
 
                                      previous_tgt_provision_state)
539
 
            task.spawn_after(self._spawn_worker, self._do_node_deploy, task)
 
678
            try:
 
679
                task.process_event(event)
 
680
                node.last_error = None
 
681
                node.save()
 
682
            except exception.InvalidState:
 
683
                raise exception.InstanceDeployFailure(_(
 
684
                    "Request received to %(what)s %(node)s, but "
 
685
                    "this is not possible in the current state of "
 
686
                    "'%(state)s'. ") % {'what': event,
 
687
                                        'node': node.uuid,
 
688
                                        'state': node.provision_state})
 
689
            else:
 
690
                task.set_spawn_error_hook(self._provisioning_error_handler,
 
691
                                          node, previous_prov_state,
 
692
                                          previous_tgt_provision_state)
 
693
                task.spawn_after(self._spawn_worker,
 
694
                                 self._do_node_deploy, task)
540
695
 
541
696
    def _do_node_deploy(self, task):
542
697
        """Prepare the environment and deploy a node."""
549
704
            # since there may be local persistent state
550
705
            node.conductor_affinity = self.conductor.id
551
706
        except Exception as e:
 
707
            # NOTE(deva): there is no need to clear conductor_affinity
552
708
            with excutils.save_and_reraise_exception():
 
709
                task.process_event('fail')
553
710
                LOG.warning(_LW('Error in deploy of node %(node)s: %(err)s'),
554
711
                            {'node': task.node.uuid, 'err': e})
555
712
                node.last_error = _("Failed to deploy. Error: %s") % e
556
 
                node.provision_state = states.DEPLOYFAIL
557
 
                node.target_provision_state = states.NOSTATE
558
 
                # NOTE(deva): there is no need to clear conductor_affinity
559
713
        else:
560
714
            # NOTE(deva): Some drivers may return states.DEPLOYWAIT
561
715
            #             eg. if they are waiting for a callback
562
716
            if new_state == states.DEPLOYDONE:
563
 
                node.target_provision_state = states.NOSTATE
564
 
                node.provision_state = states.ACTIVE
 
717
                task.process_event('done')
565
718
                LOG.info(_LI('Successfully deployed node %(node)s with '
566
719
                             'instance %(instance)s.'),
567
720
                         {'node': node.uuid, 'instance': node.instance_uuid})
 
721
            elif new_state == states.DEPLOYWAIT:
 
722
                task.process_event('wait')
568
723
            else:
569
 
                node.provision_state = new_state
 
724
                LOG.error(_LE('Unexpected state %(state)s returned while '
 
725
                              'deploying node %(node)s.'),
 
726
                              {'state': new_state, 'node': node.uuid})
570
727
        finally:
571
728
            node.save()
572
729
 
592
749
 
593
750
        with task_manager.acquire(context, node_id, shared=False) as task:
594
751
            node = task.node
595
 
            if node.provision_state not in [states.ACTIVE,
596
 
                                            states.DEPLOYFAIL,
597
 
                                            states.ERROR,
598
 
                                            states.DEPLOYWAIT]:
599
 
                raise exception.InstanceDeployFailure(_(
600
 
                    "RPC do_node_tear_down "
601
 
                    "not allowed for node %(node)s in state %(state)s")
602
 
                    % {'node': node_id, 'state': node.provision_state})
603
 
 
604
752
            try:
605
753
                # NOTE(ghe): Valid power driver values are needed to perform
606
754
                # a tear-down. Deploy info is useful to purge the cache but not
607
755
                # required for this method.
608
 
 
609
756
                task.driver.power.validate(task)
610
757
            except (exception.InvalidParameterValue,
611
758
                    exception.MissingParameterValue) as e:
619
766
            previous_prov_state = node.provision_state
620
767
            previous_tgt_provision_state = node.target_provision_state
621
768
 
622
 
            # set target state to expose that work is in progress
623
 
            node.provision_state = states.DELETING
624
 
            node.target_provision_state = states.DELETED
625
 
            node.last_error = None
626
 
            node.save()
627
 
 
628
 
            task.set_spawn_error_hook(self._provisioning_error_handler,
629
 
                                      node, previous_prov_state,
630
 
                                      previous_tgt_provision_state)
631
 
            task.spawn_after(self._spawn_worker, self._do_node_tear_down, task)
 
769
            try:
 
770
                task.process_event('delete')
 
771
                node.last_error = None
 
772
                node.save()
 
773
            except exception.InvalidState:
 
774
                raise exception.InstanceDeployFailure(_(
 
775
                    "RPC do_node_tear_down "
 
776
                    "not allowed for node %(node)s in state %(state)s")
 
777
                    % {'node': node_id, 'state': node.provision_state})
 
778
            else:
 
779
                task.set_spawn_error_hook(self._provisioning_error_handler,
 
780
                                          node, previous_prov_state,
 
781
                                          previous_tgt_provision_state)
 
782
                task.spawn_after(self._spawn_worker,
 
783
                                self._do_node_tear_down, task)
632
784
 
633
785
    def _do_node_tear_down(self, task):
634
786
        """Internal RPC method to tear down an existing node deployment."""
635
787
        node = task.node
636
788
        try:
637
789
            task.driver.deploy.clean_up(task)
638
 
            new_state = task.driver.deploy.tear_down(task)
 
790
            task.driver.deploy.tear_down(task)
639
791
        except Exception as e:
640
792
            with excutils.save_and_reraise_exception():
641
793
                LOG.warning(_LW('Error in tear_down of node %(node)s: '
642
794
                                '%(err)s'),
643
795
                            {'node': task.node.uuid, 'err': e})
644
796
                node.last_error = _("Failed to tear down. Error: %s") % e
645
 
                node.provision_state = states.ERROR
646
 
                node.target_provision_state = states.NOSTATE
 
797
                task.process_event('error')
647
798
        else:
648
 
            # NOTE(deva): Some drivers may return states.DELETING
649
 
            #             eg. if they are waiting for a callback
650
 
            if new_state == states.DELETED:
651
 
                node.target_provision_state = states.NOSTATE
652
 
                node.provision_state = states.NOSTATE
653
 
                LOG.info(_LI('Successfully unprovisioned node %(node)s with '
654
 
                             'instance %(instance)s.'),
655
 
                         {'node': node.uuid, 'instance': node.instance_uuid})
656
 
            else:
657
 
                node.provision_state = new_state
 
799
            # NOTE(deva): When tear_down finishes, the deletion is done
 
800
            task.process_event('done')
 
801
            LOG.info(_LI('Successfully unprovisioned node %(node)s with '
 
802
                         'instance %(instance)s.'),
 
803
                     {'node': node.uuid, 'instance': node.instance_uuid})
 
804
            # NOTE(deva): Currently, NOSTATE is represented as None
 
805
            #             However, FSM class treats a target_state of None as
 
806
            #             the lack of a target state -- not a target of NOSTATE
 
807
            #             Thus, until we migrate to an explicit AVAILABLE state
 
808
            #             we need to clear the target_state here manually.
 
809
            node.target_provision_state = None
658
810
        finally:
659
811
            # NOTE(deva): there is no need to unset conductor_affinity
660
812
            # because it is a reference to the most recent conductor which
665
817
 
666
818
    def _conductor_service_record_keepalive(self):
667
819
        while not self._keepalive_evt.is_set():
668
 
            self.dbapi.touch_conductor(self.host)
 
820
            try:
 
821
                self.dbapi.touch_conductor(self.host)
 
822
            except db_exception.DBConnectionError:
 
823
                LOG.warning(_LW('Conductor could not connect to database '
 
824
                                'while heartbeating.'))
669
825
            self._keepalive_evt.wait(CONF.conductor.heartbeat_interval)
670
826
 
671
827
    def _handle_sync_power_state_max_retries_exceeded(self, task,
681
837
        node.power_state = actual_power_state
682
838
        node.last_error = msg
683
839
        node.maintenance = True
 
840
        node.maintenance_reason = msg
684
841
        node.save()
685
842
        LOG.error(msg)
686
843
 
703
860
                raise exception.PowerStateFailure(_("Driver returns ERROR"
704
861
                                                    " state."))
705
862
        except Exception as e:
706
 
            # TODO(rloo): change to IronicException, after
707
 
            #             https://bugs.launchpad.net/ironic/+bug/1267693
708
863
            LOG.warning(_LW("During sync_power_state, could not get power "
709
864
                            "state for node %(node)s. Error: %(err)s."),
710
865
                            {'node': node.uuid, 'err': e})
756
911
            # so don't do that again here.
757
912
            utils.node_power_action(task, node.power_state)
758
913
        except Exception as e:
759
 
            # TODO(rloo): change to IronicException after
760
 
            # https://bugs.launchpad.net/ironic/+bug/1267693
761
914
            LOG.error(_LE("Failed to change power state of node %(node)s "
762
915
                          "to '%(state)s'."), {'node': node.uuid,
763
916
                                               'state': node.power_state})
991
1144
                    ret_dict[iface_name]['reason'] = reason
992
1145
        return ret_dict
993
1146
 
994
 
    @messaging.expected_exceptions(exception.NodeLocked,
995
 
                                   exception.NodeMaintenanceFailure)
996
 
    def change_node_maintenance_mode(self, context, node_id, mode):
997
 
        """Set node maintenance mode on or off.
998
 
 
999
 
        :param context: request context.
1000
 
        :param node_id: node id or uuid.
1001
 
        :param mode: True or False.
1002
 
        :raises: NodeMaintenanceFailure
1003
 
 
1004
 
        """
1005
 
        LOG.debug("RPC change_node_maintenance_mode called for node %(node)s"
1006
 
                  " with maintenance mode: %(mode)s" % {'node': node_id,
1007
 
                                                        'mode': mode})
1008
 
 
1009
 
        with task_manager.acquire(context, node_id, shared=True) as task:
1010
 
            node = task.node
1011
 
            if mode is not node.maintenance:
1012
 
                node.maintenance = mode
1013
 
                node.save()
1014
 
            else:
1015
 
                msg = _("The node is already in maintenance mode") if mode \
1016
 
                        else _("The node is not in maintenance mode")
1017
 
                raise exception.NodeMaintenanceFailure(node=node_id,
1018
 
                                                       reason=msg)
1019
 
 
1020
 
            return node
1021
 
 
1022
1147
    @lockutils.synchronized(WORKER_SPAWN_lOCK, 'ironic-')
1023
1148
    def _spawn_worker(self, func, *args, **kwargs):
1024
1149
 
1241
1366
                       'event_type': 'hardware.ipmi.metrics.update'}
1242
1367
 
1243
1368
            try:
1244
 
                with task_manager.acquire(context, node_uuid, shared=True) \
1245
 
                         as task:
 
1369
                with task_manager.acquire(context,
 
1370
                                          node_uuid,
 
1371
                                          shared=True) as task:
1246
1372
                    task.driver.management.validate(task)
1247
1373
                    sensors_data = task.driver.management.get_sensors_data(
1248
1374
                        task)
1271
1397
                if message['payload']:
1272
1398
                    self.notifier.info(context, "hardware.ipmi.metrics",
1273
1399
                                       message)
 
1400
            finally:
 
1401
                # Yield on every iteration
 
1402
                eventlet.sleep(0)
1274
1403
 
1275
1404
    def _filter_out_unsupported_types(self, sensors_data):
1276
1405
        # support the CONF.send_sensor_data_types sensor types only