A declarative host provisioning system.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

172 lines
6.3KB

  1. # Copyright 2018 AT&T Intellectual Property. All other rights reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """Driver for controlling OOB interface via Redfish.
  15. Based on Redfish Rest API specification.
  16. """
  17. import uuid
  18. import logging
  19. import concurrent.futures
  20. from oslo_config import cfg
  21. import drydock_provisioner.error as errors
  22. import drydock_provisioner.config as config
  23. import drydock_provisioner.objects.fields as hd_fields
  24. import drydock_provisioner.drivers.oob.driver as oob_driver
  25. import drydock_provisioner.drivers.driver as generic_driver
  26. from .actions.oob import ValidateOobServices
  27. from .actions.oob import ConfigNodePxe
  28. from .actions.oob import SetNodeBoot
  29. from .actions.oob import PowerOffNode
  30. from .actions.oob import PowerOnNode
  31. from .actions.oob import PowerCycleNode
  32. from .actions.oob import InterrogateOob
  33. class RedfishDriver(oob_driver.OobDriver):
  34. """Driver for executing OOB actions via Redfish library."""
  35. redfish_driver_options = [
  36. cfg.IntOpt(
  37. 'max_retries',
  38. default=10,
  39. min=1,
  40. help='Maximum number of connection retries to Redfish server'),
  41. cfg.IntOpt(
  42. 'power_state_change_max_retries',
  43. default=18,
  44. min=1,
  45. help='Maximum reties to wait for power state change'),
  46. cfg.IntOpt(
  47. 'power_state_change_retry_interval',
  48. default=10,
  49. help='Polling interval in seconds between retries for power state change'),
  50. cfg.BoolOpt(
  51. 'use_ssl',
  52. default=True,
  53. help='Use SSL to communicate with Redfish API server'),
  54. ]
  55. oob_types_supported = ['redfish']
  56. driver_name = "redfish_driver"
  57. driver_key = "redfish_driver"
  58. driver_desc = "Redfish OOB Driver"
  59. action_class_map = {
  60. hd_fields.OrchestratorAction.ValidateOobServices: ValidateOobServices,
  61. hd_fields.OrchestratorAction.ConfigNodePxe: ConfigNodePxe,
  62. hd_fields.OrchestratorAction.SetNodeBoot: SetNodeBoot,
  63. hd_fields.OrchestratorAction.PowerOffNode: PowerOffNode,
  64. hd_fields.OrchestratorAction.PowerOnNode: PowerOnNode,
  65. hd_fields.OrchestratorAction.PowerCycleNode: PowerCycleNode,
  66. hd_fields.OrchestratorAction.InterrogateOob: InterrogateOob,
  67. }
  68. def __init__(self, **kwargs):
  69. super().__init__(**kwargs)
  70. cfg.CONF.register_opts(
  71. RedfishDriver.redfish_driver_options, group=RedfishDriver.driver_key)
  72. self.logger = logging.getLogger(
  73. config.config_mgr.conf.logging.oobdriver_logger_name)
  74. def execute_task(self, task_id):
  75. task = self.state_manager.get_task(task_id)
  76. if task is None:
  77. self.logger.error("Invalid task %s" % (task_id))
  78. raise errors.DriverError("Invalid task %s" % (task_id))
  79. if task.action not in self.supported_actions:
  80. self.logger.error("Driver %s doesn't support task action %s" %
  81. (self.driver_desc, task.action))
  82. raise errors.DriverError("Driver %s doesn't support task action %s"
  83. % (self.driver_desc, task.action))
  84. task.set_status(hd_fields.TaskStatus.Running)
  85. task.save()
  86. target_nodes = self.orchestrator.get_target_nodes(task)
  87. with concurrent.futures.ThreadPoolExecutor(max_workers=16) as e:
  88. subtask_futures = dict()
  89. for n in target_nodes:
  90. sub_nf = self.orchestrator.create_nodefilter_from_nodelist([n])
  91. subtask = self.orchestrator.create_task(
  92. action=task.action,
  93. design_ref=task.design_ref,
  94. node_filter=sub_nf)
  95. task.register_subtask(subtask)
  96. self.logger.debug(
  97. "Starting Redfish subtask %s for action %s on node %s" %
  98. (str(subtask.get_id()), task.action, n.name))
  99. action_class = self.action_class_map.get(task.action, None)
  100. if action_class is None:
  101. self.logger.error(
  102. "Could not find action resource for action %s" %
  103. task.action)
  104. self.task.failure()
  105. break
  106. action = action_class(subtask, self.orchestrator,
  107. self.state_manager)
  108. subtask_futures[subtask.get_id().bytes] = e.submit(
  109. action.start)
  110. timeout = config.config_mgr.conf.timeouts.drydock_timeout
  111. finished, running = concurrent.futures.wait(
  112. subtask_futures.values(), timeout=(timeout * 60))
  113. for t, f in subtask_futures.items():
  114. if not f.done():
  115. task.add_status_msg(
  116. msg="Subtask %s timed out before completing.",
  117. error=True,
  118. ctx=str(uuid.UUID(bytes=t)),
  119. ctx_type='task')
  120. task.failure()
  121. else:
  122. if f.exception():
  123. self.logger.error(
  124. "Uncaught exception in subtask %s" % str(
  125. uuid.UUID(bytes=t)),
  126. exc_info=f.exception())
  127. task.align_result()
  128. task.bubble_results()
  129. task.set_status(hd_fields.TaskStatus.Complete)
  130. task.save()
  131. return
  132. class RedfishActionRunner(generic_driver.DriverActionRunner):
  133. """Threaded runner for a Redfish Action."""
  134. def __init__(self, **kwargs):
  135. super().__init__(**kwargs)
  136. self.logger = logging.getLogger(
  137. config.config_mgr.conf.logging.oobdriver_logger_name)
  138. def list_opts():
  139. return {RedfishDriver.driver_key: RedfishDriver.redfish_driver_options}