drydock/drydock_provisioner/drivers/oob/pyghmi_driver/__init__.py

303 lines
12 KiB
Python

# Copyright 2017 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.
import time
from pyghmi.ipmi.command import Command
import drydock_provisioner.error as errors
import drydock_provisioner.config as config
import drydock_provisioner.objects.fields as hd_fields
import drydock_provisioner.objects.task as task_model
import drydock_provisioner.drivers.oob as oob
import drydock_provisioner.drivers as drivers
class PyghmiDriver(oob.OobDriver):
def __init__(self, **kwargs):
super(PyghmiDriver, self).__init__(**kwargs)
self.driver_name = "pyghmi_driver"
self.driver_key = "pyghmi_driver"
self.driver_desc = "Pyghmi OOB Driver"
self.config = config.DrydockConfig.node_driver.get(self.driver_key, {})
def execute_task(self, task_id):
task = self.state_manager.get_task(task_id)
if task is None:
raise errors.DriverError("Invalid task %s" % (task_id))
if task.action not in self.supported_actions:
raise errors.DriverError("Driver %s doesn't support task action %s"
% (self.driver_desc, task.action))
design_id = getattr(task, 'design_id', None)
if design_id is None:
raise errors.DriverError("No design ID specified in task %s" %
(task_id))
if task.site_name is None:
raise errors.DriverError("Not site specified for task %s." %
(task_id))
self.orchestrator.task_field_update(task.get_id(),
status=hd_fields.TaskStatus.Running)
if task.action == hd_fields.OrchestratorAction.ValidateOobServices:
self.orchestrator.task_field_update(task.get_id(),
status=hd_fields.TaskStatus.Complete,
result=hd_fields.ActionResult.Success)
return
site_design = self.orchestrator.get_effective_site(design_id, task.site_name)
target_nodes = []
if len(task.node_list) > 0:
target_nodes.extend([x
for x in site_design.baremetal_nodes
if x.get_name() in task.node_list])
else:
target_nodes.extend(site_design.baremetal_nodes)
incomplete_subtasks = []
# For each target node, create a subtask and kick off a runner
for n in target_nodes:
subtask = self.orchestrator.create_task(task_model.DriverTask,
parent_task_id=task.get_id(), design_id=design_id,
action=task.action,
task_scope={'site': task.site_name,
'node_names': [n.get_name()]})
incomplete_subtasks.append(subtask.get_id())
runner = PyghmiTaskRunner(state_manager=self.state_manager,
orchestrator=self.orchestrator,
task_id=subtask.get_id(), node=n)
runner.start()
# Wait for subtasks to complete
# TODO need some kind of timeout
i = 0
while len(incomplete_subtasks) > 0:
for n in incomplete_subtasks:
t = self.state_manager.get_task(n)
if t.get_status() in [hd_fields.TaskStatus.Terminated,
hd_fields.TaskStatus.Complete,
hd_fields.TaskStatus.Errored]:
incomplete_subtasks.remove(n)
time.sleep(2)
i = i+1
if i == 5:
break
task = self.state_manager.get_task(task.get_id())
subtasks = map(self.state_manager.get_task, task.get_subtasks())
success_subtasks = [x
for x in subtasks
if x.get_result() == hd_fields.ActionResult.Success]
nosuccess_subtasks = [x
for x in subtasks
if x.get_result() in [hd_fields.ActionResult.PartialSuccess,
hd_fields.ActionResult.Failure]]
print("Task %s successful subtasks: %s" %
(task.get_id(), len(success_subtasks)))
print("Task %s unsuccessful subtasks: %s" %
(task.get_id(), len(nosuccess_subtasks)))
print("Task %s total subtasks: %s" %
(task.get_id(), len(task.get_subtasks())))
task_result = None
if len(success_subtasks) > 0 and len(nosuccess_subtasks) > 0:
task_result = hd_fields.ActionResult.PartialSuccess
elif len(success_subtasks) == 0 and len(nosuccess_subtasks) > 0:
task_result = hd_fields.ActionResult.Failure
elif len(success_subtasks) > 0 and len(nosuccess_subtasks) == 0:
task_result = hd_fields.ActionResult.Success
else:
task_result = hd_fields.ActionResult.Incomplete
self.orchestrator.task_field_update(task.get_id(),
result=task_result,
status=hd_fields.TaskStatus.Complete)
return
class PyghmiTaskRunner(drivers.DriverTaskRunner):
def __init__(self, node=None, **kwargs):
super(PyghmiTaskRunner, self).__init__(**kwargs)
# We cheat here by providing the Node model instead
# of making the runner source it from statemgmt
if node is None:
raise errors.DriverError("Did not specify target node")
self.node = node
def execute_task(self):
task_action = self.task.action
if len(self.task.node_list) != 1:
self.orchestrator.task_field_update(self.task.get_id(),
result=hd_fields.ActionResult.Incomplete,
status=hd_fields.TaskStatus.Errored)
raise errors.DriverError("Multiple names (%s) in task %s node_list"
% (len(self.task.node_list), self.task.get_id()))
target_node_name = self.task.node_list[0]
if self.node.get_name() != target_node_name:
self.orchestrator.task_field_update(self.task.get_id(),
result=hd_fields.ActionResult.Incomplete,
status=hd_fields.TaskStatus.Errored)
raise errors.DriverError("Runner node does not match " \
"task node scope")
ipmi_network = self.node.applied.get('oob_network')
ipmi_address = self.node.get_network_address(ipmi_network)
if ipmi_address is None:
self.orchestrator.task_field_update(self.task.get_id(),
result=hd_fields.ActionResult.Incomplete,
status=hd_fields.TaskStatus.Errored)
raise errors.DriverError("Node %s has no IPMI address" %
(target_node_name))
self.orchestrator.task_field_update(self.task.get_id(),
status=hd_fields.TaskStatus.Running)
ipmi_account = self.node.applied.get('oob_account', '')
ipmi_credential = self.node.applied.get('oob_credential', '')
ipmi_session = Command(bmc=ipmi_address, userid=ipmi_account,
password=ipmi_credential)
if task_action == hd_fields.OrchestratorAction.ConfigNodePxe:
self.orchestrator.task_field_update(self.task.get_id(),
result=hd_fields.ActionResult.Failure,
status=hd_fields.TaskStatus.Complete)
return
elif task_action == hd_fields.OrchestratorAction.SetNodeBoot:
ipmi_session.set_bootdev('pxe')
time.sleep(3)
bootdev = ipmi_session.get_bootdev()
if bootdev.get('bootdev', '') == 'network':
self.orchestrator.task_field_update(self.task.get_id(),
result=hd_fields.ActionResult.Success,
status=hd_fields.TaskStatus.Complete)
else:
self.orchestrator.task_field_update(self.task.get_id(),
result=hd_fields.ActionResult.Failure,
status=hd_fields.TaskStatus.Complete)
return
elif task_action == hd_fields.OrchestratorAction.PowerOffNode:
ipmi_session.set_power('off')
i = 18
while i > 0:
power_state = ipmi_session.get_power()
if power_state.get('powerstate', '') == 'off':
break
time.sleep(10)
i = i - 1
if power_state.get('powerstate', '') == 'off':
self.orchestrator.task_field_update(self.task.get_id(),
result=hd_fields.ActionResult.Success,
status=hd_fields.TaskStatus.Complete)
else:
self.orchestrator.task_field_update(self.task.get_id(),
result=hd_fields.ActionResult.Failure,
status=hd_fields.TaskStatus.Complete)
return
elif task_action == hd_fields.OrchestratorAction.PowerOnNode:
ipmi_session.set_power('on')
i = 18
while i > 0:
power_state = ipmi_session.get_power()
if power_state.get('powerstate', '') == 'on':
break
time.sleep(10)
i = i - 1
if power_state.get('powerstate', '') == 'on':
self.orchestrator.task_field_update(self.task.get_id(),
result=hd_fields.ActionResult.Success,
status=hd_fields.TaskStatus.Complete)
else:
self.orchestrator.task_field_update(self.task.get_id(),
result=hd_fields.ActionResult.Failure,
status=hd_fields.TaskStatus.Complete)
return
elif task_action == hd_fields.OrchestratorAction.PowerCycleNode:
ipmi_session.set_power('off')
# Wait for power state of off before booting back up
# We'll wait for up to 3 minutes to power off
i = 18
while i > 0:
power_state = ipmi_session.get_power()
if power_state.get('powerstate', '') == 'off':
break
time.sleep(10)
i = i - 1
if power_state.get('powerstate', '') == 'on':
self.orchestrator.task_field_update(self.task.get_id(),
result=hd_fields.ActionResult.Failure,
status=hd_fields.TaskStatus.Complete)
return
ipmi_session.set_power('on')
i = 18
while i > 0:
power_state = ipmi_session.get_power()
if power_state.get('powerstate', '') == 'on':
break
time.sleep(10)
i = i - 1
if power_state.get('powerstate', '') == 'on':
self.orchestrator.task_field_update(self.task.get_id(),
result=hd_fields.ActionResult.Success,
status=hd_fields.TaskStatus.Complete)
else:
self.orchestrator.task_field_update(self.task.get_id(),
result=hd_fields.ActionResult.Failure,
status=hd_fields.TaskStatus.Complete)
return
elif task_action == hd_fields.OrchestratorAction.InterrogateOob:
mci_id = ipmi_session.get_mci()
self.orchestrator.task_field_update(self.task.get_id(),
result=hd_fields.ActionResult.Success,
status=hd_fields.TaskStatus.Complete,
result_detail=mci_id)
return