ソースを参照

Merge "Add Redfish as OOB driver"

changes/18/628418/1
Zuul 11ヶ月前
コミット
c39a4ede1a
10個のファイルの変更814行の追加1行の削除
  1. +1
    -0
      charts/drydock/values.yaml
  2. +19
    -0
      etc/drydock/drydock.conf.sample
  3. +1
    -1
      python/drydock_provisioner/drivers/node/maasdriver/models/machine.py
  4. +0
    -0
      python/drydock_provisioner/drivers/oob/redfish_driver/__init__.py
  5. +0
    -0
      python/drydock_provisioner/drivers/oob/redfish_driver/actions/__init__.py
  6. +443
    -0
      python/drydock_provisioner/drivers/oob/redfish_driver/actions/oob.py
  7. +177
    -0
      python/drydock_provisioner/drivers/oob/redfish_driver/client.py
  8. +171
    -0
      python/drydock_provisioner/drivers/oob/redfish_driver/driver.py
  9. +1
    -0
      python/requirements-direct.txt
  10. +1
    -0
      python/requirements-lock.txt

+ 1
- 0
charts/drydock/values.yaml ファイルの表示

@@ -325,6 +325,7 @@ conf:
ingester:
- 'drydock_provisioner.ingester.plugins.yaml.YamlIngester'
oob_driver:
- 'drydock_provisioner.drivers.oob.redfish_driver.driver.RedfishDriver'
- 'drydock_provisioner.drivers.oob.pyghmi_driver.driver.PyghmiDriver'
- 'drydock_provisioner.drivers.oob.manual_driver.driver.ManualDriver'
- 'drydock_provisioner.drivers.oob.libvirt_driver.driver.LibvirtDriver'

+ 19
- 0
etc/drydock/drydock.conf.sample ファイルの表示

@@ -370,6 +370,25 @@
#poll_interval = 10


[redfish_driver]

#
# From drydock_provisioner
#

# Maximum number of connection retries to Redfish server
#max_retries = 5

# Maximum reties to wait for power state change
#power_state_change_max_retries = 18

# Polling interval in seconds between retries for power state change
#power_state_change_retry_interval = 10

# Use SSL to communicate with Redfish API server (boolean value)
#use_ssl = true


[timeouts]

#

+ 1
- 1
python/drydock_provisioner/drivers/node/maasdriver/models/machine.py ファイルの表示

@@ -574,7 +574,7 @@ class Machines(model_base.ResourceCollectionBase):
"""
maas_node = None

if node_model.oob_type == 'ipmi':
if node_model.oob_type == 'ipmi' or node_model.oob_type == 'redfish':
node_oob_network = node_model.oob_parameters['network']
node_oob_ip = node_model.get_network_address(node_oob_network)


+ 0
- 0
python/drydock_provisioner/drivers/oob/redfish_driver/__init__.py ファイルの表示


+ 0
- 0
python/drydock_provisioner/drivers/oob/redfish_driver/actions/__init__.py ファイルの表示


+ 443
- 0
python/drydock_provisioner/drivers/oob/redfish_driver/actions/oob.py ファイルの表示

@@ -0,0 +1,443 @@
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Driver for controlling OOB interface via Redfish.

Based on Redfish Rest API specification.
"""

import time

from oslo_config import cfg

from drydock_provisioner.orchestrator.actions.orchestrator import BaseAction
from drydock_provisioner.drivers.oob.redfish_driver.client import RedfishException
from drydock_provisioner.drivers.oob.redfish_driver.client import RedfishSession

import drydock_provisioner.error as errors
import drydock_provisioner.objects.fields as hd_fields

class RedfishBaseAction(BaseAction):
"""Base action for Redfish executed actions."""

def get_redfish_session(self, node):
"""Initialize a Redfish session to the node.

:param node: instance of objects.BaremetalNode
:return: An instance of client.RedfishSession initialized to node's Redfish interface
"""
if node.oob_type != 'redfish':
raise errors.DriverError("Node OOB type is not Redfish")

oob_network = node.oob_parameters['network']
oob_address = node.get_network_address(oob_network)
if oob_address is None:
raise errors.DriverError(
"Node %s has no OOB Redfish address" % (node.name))

oob_account = node.oob_parameters['account']
oob_credential = node.oob_parameters['credential']

self.logger.debug("Starting Redfish session to %s with %s" %
(oob_address, oob_account))
try:
redfish_obj = RedfishSession(host=oob_address,
account=oob_account,
password=oob_credential,
use_ssl=cfg.CONF.redfish_driver.use_ssl,
connection_retries=cfg.CONF.redfish_driver.max_retries)
except (RedfishException, errors.DriverError) as iex:
self.logger.error(
"Error initializing Redfish session for node %s" % node.name)
self.logger.error("Redfish Exception: %s" % str(iex))
redfish_obj = None

return redfish_obj

def exec_redfish_command(self, node, session, func, *args):
"""Call a Redfish command after establishing a session.

:param node: Instance of objects.BaremetalNode to execute against
:param session: Redfish session
:param func: The redfish Command method to call
:param args: The args to pass the func
"""
try:
self.logger.debug("Calling Redfish command %s on %s" %
(func.__name__, node.name))
response = func(session, *args)
return response
except RedfishException as iex:
self.logger.error(
"Error executing Redfish command %s for node %s" % (func.__name__, node.name))
self.logger.error("Redfish Exception: %s" % str(iex))

raise errors.DriverError("Redfish command failed.")


class ValidateOobServices(RedfishBaseAction):
"""Action to validate OOB services are available."""

def start(self):
self.task.add_status_msg(
msg="OOB does not require services.",
error=False,
ctx='NA',
ctx_type='NA')
self.task.set_status(hd_fields.TaskStatus.Complete)
self.task.success()
self.task.save()

return


class ConfigNodePxe(RedfishBaseAction):
"""Action to configure PXE booting via OOB."""

def start(self):
self.task.set_status(hd_fields.TaskStatus.Running)
self.task.save()

node_list = self.orchestrator.get_target_nodes(self.task)

for n in node_list:
self.task.add_status_msg(
msg="Redfish doesn't configure PXE options.",
error=True,
ctx=n.name,
ctx_type='node')
self.task.set_status(hd_fields.TaskStatus.Complete)
self.task.failure()
self.task.save()
return


class SetNodeBoot(RedfishBaseAction):
"""Action to configure a node to PXE boot."""

def start(self):
self.task.set_status(hd_fields.TaskStatus.Running)
self.task.save()

node_list = self.orchestrator.get_target_nodes(self.task)

for n in node_list:
self.logger.debug("Setting bootdev to PXE for %s" % n.name)
self.task.add_status_msg(
msg="Setting node to PXE boot.",
error=False,
ctx=n.name,
ctx_type='node')

bootdev = None
try:
session = self.get_redfish_session(n)
bootdev = self.exec_redfish_command(n, session, RedfishSession.get_bootdev)
if bootdev.get('bootdev', '') != 'Pxe':
self.exec_redfish_command(n, session, RedfishSession.set_bootdev, 'Pxe')
bootdev = self.exec_redfish_command(n, session, RedfishSession.get_bootdev)
session.close_session()
except errors.DriverError:
pass

if bootdev is not None and (bootdev.get('bootdev',
'') == 'Pxe'):
self.task.add_status_msg(
msg="Set bootdev to PXE.",
error=False,
ctx=n.name,
ctx_type='node')
self.logger.debug("%s reports bootdev of network" % n.name)
self.task.success(focus=n.name)
else:
self.task.add_status_msg(
msg="Unable to set bootdev to PXE.",
error=True,
ctx=n.name,
ctx_type='node')
self.task.failure(focus=n.name)
self.logger.warning(
"Unable to set node %s to PXE boot." % (n.name))

self.task.set_status(hd_fields.TaskStatus.Complete)
self.task.save()
return


class PowerOffNode(RedfishBaseAction):
"""Action to power off a node via Redfish."""

def start(self):
self.task.set_status(hd_fields.TaskStatus.Running)
self.task.save()

node_list = self.orchestrator.get_target_nodes(self.task)

for n in node_list:
self.logger.debug("Sending set_power = off command to %s" % n.name)
self.task.add_status_msg(
msg="Sending set_power = off command.",
error=False,
ctx=n.name,
ctx_type='node')
session = self.get_redfish_session(n)

# If power is already off, continue with the next node
power_state = self.exec_redfish_command(n, RedfishSession.get_power)
if power_state is not None and (power_state.get(
'powerstate', '') == 'Off'):
self.task.add_status_msg(
msg="Node reports power off.",
error=False,
ctx=n.name,
ctx_type='node')
self.logger.debug(
"Node %s reports powerstate already off. No action required" % n.name)
self.task.success(focus=n.name)
continue

self.exec_redfish_command(n, session, RedfishSession.set_power, 'ForceOff')

attempts = cfg.CONF.redfish_driver.power_state_change_max_retries

while attempts > 0:
self.logger.debug("Polling powerstate waiting for success.")
power_state = self.exec_redfish_command(n, RedfishSession.get_power)
if power_state is not None and (power_state.get(
'powerstate', '') == 'Off'):
self.task.add_status_msg(
msg="Node reports power off.",
error=False,
ctx=n.name,
ctx_type='node')
self.logger.debug(
"Node %s reports powerstate of off" % n.name)
self.task.success(focus=n.name)
break
time.sleep(cfg.CONF.redfish_driver.power_state_change_retry_interval)
attempts = attempts - 1

if power_state is not None and (power_state.get('powerstate', '')
!= 'Off'):
self.task.add_status_msg(
msg="Node failed to power off.",
error=True,
ctx=n.name,
ctx_type='node')
self.logger.error("Giving up on Redfish command to %s" % n.name)
self.task.failure(focus=n.name)

session.close_session()

self.task.set_status(hd_fields.TaskStatus.Complete)
self.task.save()
return


class PowerOnNode(RedfishBaseAction):
"""Action to power on a node via Redfish."""

def start(self):
self.task.set_status(hd_fields.TaskStatus.Running)
self.task.save()

node_list = self.orchestrator.get_target_nodes(self.task)

for n in node_list:
self.logger.debug("Sending set_power = on command to %s" % n.name)
self.task.add_status_msg(
msg="Sending set_power = on command.",
error=False,
ctx=n.name,
ctx_type='node')
session = self.get_redfish_session(n)

# If power is already on, continue with the next node
power_state = self.exec_redfish_command(n, RedfishSession.get_power)
if power_state is not None and (power_state.get(
'powerstate', '') == 'On'):
self.task.add_status_msg(
msg="Node reports power on.",
error=False,
ctx=n.name,
ctx_type='node')
self.logger.debug(
"Node %s reports powerstate already on. No action required" % n.name)
self.task.success(focus=n.name)
continue

self.exec_redfish_command(n, session, RedfishSession.set_power, 'On')

attempts = cfg.CONF.redfish_driver.power_state_change_max_retries

while attempts > 0:
self.logger.debug("Polling powerstate waiting for success.")
power_state = self.exec_redfish_command(n, session, RedfishSession.get_power)
if power_state is not None and (power_state.get(
'powerstate', '') == 'On'):
self.logger.debug(
"Node %s reports powerstate of on" % n.name)
self.task.add_status_msg(
msg="Node reports power on.",
error=False,
ctx=n.name,
ctx_type='node')
self.task.success(focus=n.name)
break
time.sleep(cfg.CONF.redfish_driver.power_state_change_retry_interval)
attempts = attempts - 1

if power_state is not None and (power_state.get('powerstate', '')
!= 'On'):
self.task.add_status_msg(
msg="Node failed to power on.",
error=True,
ctx=n.name,
ctx_type='node')
self.logger.error("Giving up on Redfish command to %s" % n.name)
self.task.failure(focus=n.name)

session.close_session()

self.task.set_status(hd_fields.TaskStatus.Complete)
self.task.save()
return


class PowerCycleNode(RedfishBaseAction):
"""Action to hard powercycle a node via Redfish."""

def start(self):
self.task.set_status(hd_fields.TaskStatus.Running)
self.task.save()

node_list = self.orchestrator.get_target_nodes(self.task)

for n in node_list:
self.logger.debug("Sending set_power = off command to %s" % n.name)
self.task.add_status_msg(
msg="Power cycling node via Redfish.",
error=False,
ctx=n.name,
ctx_type='node')
session = self.get_redfish_session(n)
self.exec_redfish_command(n, session, RedfishSession.set_power, 'ForceOff')

# Wait for power state of off before booting back up
attempts = cfg.CONF.redfish_driver.power_state_change_max_retries

while attempts > 0:
power_state = self.exec_redfish_command(n, session, RedfishSession.get_power)
if power_state is not None and power_state.get(
'powerstate', '') == 'Off':
self.logger.debug("%s reports powerstate of off" % n.name)
break
elif power_state is None:
self.logger.debug(
"No response on Redfish power query to %s" % n.name)
time.sleep(cfg.CONF.redfish_driver.power_state_change_retry_interval)
attempts = attempts - 1

if power_state.get('powerstate', '') != 'Off':
self.task.add_status_msg(
msg="Failed to power down during power cycle.",
error=True,
ctx=n.name,
ctx_type='node')
self.logger.warning(
"Failed powering down node %s during power cycle task" %
n.name)
self.task.failure(focus=n.name)
break

self.logger.debug("Sending set_power = on command to %s" % n.name)
self.exec_redfish_command(n, session, RedfishSession.set_power, 'On')

attempts = cfg.CONF.redfish_driver.power_state_change_max_retries

while attempts > 0:
power_state = self.exec_redfish_command(n, session, RedfishSession.get_power)
if power_state is not None and power_state.get(
'powerstate', '') == 'On':
self.logger.debug("%s reports powerstate of on" % n.name)
break
elif power_state is None:
self.logger.debug(
"No response on Redfish power query to %s" % n.name)
time.sleep(cfg.CONF.redfish_driver.power_state_change_retry_interval)
attempts = attempts - 1

if power_state is not None and (power_state.get('powerstate',
'') == 'On'):
self.task.add_status_msg(
msg="Node power cycle complete.",
error=False,
ctx=n.name,
ctx_type='node')
self.task.success(focus=n.name)
else:
self.task.add_status_msg(
msg="Failed to power up during power cycle.",
error=True,
ctx=n.name,
ctx_type='node')
self.logger.warning(
"Failed powering up node %s during power cycle task" %
n.name)
self.task.failure(focus=n.name)

session.close_session()

self.task.set_status(hd_fields.TaskStatus.Complete)
self.task.save()
return


class InterrogateOob(RedfishBaseAction):
"""Action to complete a basic interrogation of the node Redfish interface."""

def start(self):
self.task.set_status(hd_fields.TaskStatus.Running)
self.task.save()

node_list = self.orchestrator.get_target_nodes(self.task)

for n in node_list:
try:
self.logger.debug(
"Interrogating node %s Redfish interface." % n.name)
session = self.get_redfish_session(n)
powerstate = self.exec_redfish_command(n, session, RedfishSession.get_power)
session.close_session()
if powerstate is None:
raise errors.DriverError()
self.task.add_status_msg(
msg="Redfish interface interrogation yielded powerstate %s" %
powerstate.get('powerstate'),
error=False,
ctx=n.name,
ctx_type='node')
self.task.success(focus=n.name)
except errors.DriverError:
self.logger.debug(
"Interrogating node %s Redfish interface failed." % n.name)
self.task.add_status_msg(
msg="Redfish interface interrogation failed.",
error=True,
ctx=n.name,
ctx_type='node')
self.task.failure(focus=n.name)

self.task.set_status(hd_fields.TaskStatus.Complete)
self.task.save()
return

+ 177
- 0
python/drydock_provisioner/drivers/oob/redfish_driver/client.py ファイルの表示

@@ -0,0 +1,177 @@
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Redfish object to provide commands.

Uses Redfish client to communicate to node.
"""

from redfish import AuthMethod, redfish_client
from redfish.rest.v1 import ServerDownOrUnreachableError
from redfish.rest.v1 import InvalidCredentialsError
from redfish.rest.v1 import RetriesExhaustedError

class RedfishSession(object):
"""Redfish Client to provide OOB commands"""

def __init__(self, host, account, password, use_ssl=True, connection_retries=10):
try:
if use_ssl:
redfish_url = 'https://' + host
else:
redfish_url = 'http://' + host
self.redfish_client = redfish_client(base_url=redfish_url,
username=account,
password=password)

self.redfish_client.MAX_RETRY = connection_retries
self.redfish_client.login(auth=AuthMethod.SESSION)
except RetriesExhaustedError:
raise RedfishException("Login failed: Retries exhausted")
except InvalidCredentialsError:
raise RedfishException("Login failed: Invalid credentials")
except ServerDownOrUnreachableError:
raise RedfishException("Login failed: Server unreachable")

def __del__(self):
self.redfish_client.logout()

def close_session(self):
self.redfish_client.logout()

def get_system_instance(self):
response = self.redfish_client.get("/redfish/v1/Systems")

if response.status != 200:
raise RedfishException(response._read)

# Assumption that only one system is available on Node
if response.dict["Members@odata.count"] != 1:
raise RedfishException("Number of systems are more than one in the node")
instance = response.dict["Members"][0]["@odata.id"]

return instance

def get_bootdev(self):
"""Get current boot type information from Node.

:raises: RedfishException on an error
:return: dict -- response will return as dict in format of
{'bootdev': bootdev}
"""
instance = self.get_system_instance()
response = self.redfish_client.get(path=instance)

if response.status != 200:
raise RedfishException(response._read)

bootdev = response.dict["Boot"]["BootSourceOverrideTarget"]
return {'bootdev': bootdev}

def set_bootdev(self, bootdev, **kwargs):
"""Set boot type on the Node for next boot.

:param bootdev: Boot source for the next boot
* None - Boot from the normal boot device.
* Pxe - Boot from network
* Cd - Boot from CD/DVD disc
* Usb - Boot from USB device specified by system BIOS
* Hdd - Boot from Hard drive
* BiosSetup - Boot to bios setup utility
* Utilities - Boot manufacurer utlities program
* UefiTarget - Boot to the UEFI Device specified in the
UefiTargetBootSourceOverride property
* UefiShell - Boot to the UEFI Shell
* UefiHttp - Boot from UEFI HTTP network location
:param **kwargs: To specify extra arguments for a given bootdev
Example to specify UefiTargetBootSourceOverride value
for bootdev UefiTarget
:raises: RedfishException on an error
:return: dict -- response will return as dict in format of
{'bootdev': bootdev}
"""
instance = self.get_system_instance()

payload = {
"Boot": {
"BootSourceOverrideEnabled": "Once",
"BootSourceOverrideTarget": bootdev,
}
}

if bootdev == 'UefiTarget':
payload['Boot']['UefiTargetBootSourceOverride'] = kwargs.get(
'UefiTargetBootSourceOverride', '')

response = self.redfish_client.patch(path=instance, body=payload)
if response.status != 200:
raise RedfishException(response._read)

return {'bootdev': bootdev}

def get_power(self):
"""Get current power state information from Node.

:raises: RedfishException on an error
:return: dict -- response will return as dict in format of
{'powerstate': powerstate}
"""
instance = self.get_system_instance()

response = self.redfish_client.get(path=instance)
if response.status != 200:
raise RedfishException(response._read)

powerstate = response.dict["PowerState"]
return {'powerstate': powerstate}

def set_power(self, powerstate):
"""Request power change on the node.

:param powerstate: set power change
* On - Power On the unit
* ForceOff - Turn off immediately (non graceful)
* PushPowerButton - Simulate pressing physical
power button
* GracefulRestart - Perform a graceful shutdown
and then start

:raises: RedfishException on an error
:return: dict -- response will return as dict in format of
{'powerstate': powerstate}
"""
instance = self.get_system_instance()

if powerstate not in ["On", "ForceOff", "PushPowerButton", "GracefulRestart"]:
raise RedfishException("Unsupported powerstate")

current_state = self.get_power()
if (powerstate == "On" and current_state["powerstate"] == "On") or \
(powerstate == "ForceOff" and current_state["powerstate"] == "Off"):
return {'powerstate': powerstate}

payload = {
"ResetType": powerstate
}

url = instance + "/Actions/ComputerSystem.Reset"
response = self.redfish_client.post(path=url, body=payload)
if response.status in [200, 201, 204]:
return {'powerstate': powerstate}
else:
raise RedfishException(response._read)


class RedfishException(Exception):
"""Redfish Exception with error in message"""
pass

+ 171
- 0
python/drydock_provisioner/drivers/oob/redfish_driver/driver.py ファイルの表示

@@ -0,0 +1,171 @@
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Driver for controlling OOB interface via Redfish.

Based on Redfish Rest API specification.
"""

import uuid
import logging
import concurrent.futures

from oslo_config import cfg

import drydock_provisioner.error as errors
import drydock_provisioner.config as config

import drydock_provisioner.objects.fields as hd_fields

import drydock_provisioner.drivers.oob.driver as oob_driver
import drydock_provisioner.drivers.driver as generic_driver

from .actions.oob import ValidateOobServices
from .actions.oob import ConfigNodePxe
from .actions.oob import SetNodeBoot
from .actions.oob import PowerOffNode
from .actions.oob import PowerOnNode
from .actions.oob import PowerCycleNode
from .actions.oob import InterrogateOob


class RedfishDriver(oob_driver.OobDriver):
"""Driver for executing OOB actions via Redfish library."""

redfish_driver_options = [
cfg.IntOpt(
'max_retries',
default=10,
min=1,
help='Maximum number of connection retries to Redfish server'),
cfg.IntOpt(
'power_state_change_max_retries',
default=18,
min=1,
help='Maximum reties to wait for power state change'),
cfg.IntOpt(
'power_state_change_retry_interval',
default=10,
help='Polling interval in seconds between retries for power state change'),
cfg.BoolOpt(
'use_ssl',
default=True,
help='Use SSL to communicate with Redfish API server'),
]

oob_types_supported = ['redfish']

driver_name = "redfish_driver"
driver_key = "redfish_driver"
driver_desc = "Redfish OOB Driver"

action_class_map = {
hd_fields.OrchestratorAction.ValidateOobServices: ValidateOobServices,
hd_fields.OrchestratorAction.ConfigNodePxe: ConfigNodePxe,
hd_fields.OrchestratorAction.SetNodeBoot: SetNodeBoot,
hd_fields.OrchestratorAction.PowerOffNode: PowerOffNode,
hd_fields.OrchestratorAction.PowerOnNode: PowerOnNode,
hd_fields.OrchestratorAction.PowerCycleNode: PowerCycleNode,
hd_fields.OrchestratorAction.InterrogateOob: InterrogateOob,
}

def __init__(self, **kwargs):
super().__init__(**kwargs)

cfg.CONF.register_opts(
RedfishDriver.redfish_driver_options, group=RedfishDriver.driver_key)

self.logger = logging.getLogger(
config.config_mgr.conf.logging.oobdriver_logger_name)

def execute_task(self, task_id):
task = self.state_manager.get_task(task_id)

if task is None:
self.logger.error("Invalid task %s" % (task_id))
raise errors.DriverError("Invalid task %s" % (task_id))

if task.action not in self.supported_actions:
self.logger.error("Driver %s doesn't support task action %s" %
(self.driver_desc, task.action))
raise errors.DriverError("Driver %s doesn't support task action %s"
% (self.driver_desc, task.action))

task.set_status(hd_fields.TaskStatus.Running)
task.save()

target_nodes = self.orchestrator.get_target_nodes(task)

with concurrent.futures.ThreadPoolExecutor(max_workers=16) as e:
subtask_futures = dict()
for n in target_nodes:
sub_nf = self.orchestrator.create_nodefilter_from_nodelist([n])
subtask = self.orchestrator.create_task(
action=task.action,
design_ref=task.design_ref,
node_filter=sub_nf)
task.register_subtask(subtask)
self.logger.debug(
"Starting Redfish subtask %s for action %s on node %s" %
(str(subtask.get_id()), task.action, n.name))

action_class = self.action_class_map.get(task.action, None)
if action_class is None:
self.logger.error(
"Could not find action resource for action %s" %
task.action)
self.task.failure()
break
action = action_class(subtask, self.orchestrator,
self.state_manager)
subtask_futures[subtask.get_id().bytes] = e.submit(
action.start)

timeout = config.config_mgr.conf.timeouts.drydock_timeout
finished, running = concurrent.futures.wait(
subtask_futures.values(), timeout=(timeout * 60))

for t, f in subtask_futures.items():
if not f.done():
task.add_status_msg(
msg="Subtask %s timed out before completing.",
error=True,
ctx=str(uuid.UUID(bytes=t)),
ctx_type='task')
task.failure()
else:
if f.exception():
self.logger.error(
"Uncaught exception in subtask %s" % str(
uuid.UUID(bytes=t)),
exc_info=f.exception())
task.align_result()
task.bubble_results()
task.set_status(hd_fields.TaskStatus.Complete)
task.save()

return


class RedfishActionRunner(generic_driver.DriverActionRunner):
"""Threaded runner for a Redfish Action."""

def __init__(self, **kwargs):
super().__init__(**kwargs)

self.logger = logging.getLogger(
config.config_mgr.conf.logging.oobdriver_logger_name)


def list_opts():
return {RedfishDriver.driver_key: RedfishDriver.redfish_driver_options}

+ 1
- 0
python/requirements-direct.txt ファイルの表示

@@ -24,3 +24,4 @@ ulid2==0.1.1
defusedxml===0.5.0
libvirt-python==3.10.0
beaker==1.9.1
redfish==2.0.1

+ 1
- 0
python/requirements-lock.txt ファイルの表示

@@ -61,6 +61,7 @@ python-keystoneclient==3.17.0
python-mimeparse==1.6.0
pytz==2018.5
PyYAML==3.12
redfish==2.0.1
repoze.lru==0.7
requests==2.19.1
rfc3986==1.1.0

読み込み中…
キャンセル
保存