Add IPMI driver

Change-Id: I850974c707842683d660d9fd820c1cca98d4a21b
This commit is contained in:
Anton Studenov 2016-08-25 16:19:43 +03:00
parent 5941f0b117
commit e1f57a21ce
9 changed files with 195 additions and 3 deletions

View File

@ -0,0 +1,71 @@
# 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 logging
import os_failures
def main():
# cloud config schema is an extension to os-client-config
cloud_config = {
'auth': {
'username': 'admin',
'password': 'admin',
'project_name': 'admin',
},
'region_name': 'RegionOne',
'cloud_management': {
'driver': 'fuel',
'address': 'fuel.local',
'username': 'root',
},
'power_management': {
'driver': 'ipmi',
'mac_to_bmc': {
'00:00:00:00:00:00': {
'address': '55.55.55.55',
'username': 'foo',
'password': 'bar',
}
}
}
}
logging.info('Create connection to the cluster')
destructor = os_failures.connect(cloud_config)
logging.info('Verify connection to the cluster')
destructor.verify()
logging.info('Get all cluster nodes')
nodes = destructor.get_nodes()
logging.info('All cluster nodes: %s', nodes)
computes = nodes.filter(role='compute')
one = computes.pick()
logging.info('Pick one of compute nodes: %s', one)
logging.info('Power off compute node')
one.poweroff()
logging.info('Power on compute node')
one.poweron()
logging.info('Done!')
if __name__ == '__main__':
logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s',
level=logging.DEBUG)
main()

View File

@ -14,6 +14,7 @@ import pbr.version
from os_failures.drivers import devstack
from os_failures.drivers import fuel
from os_failures.drivers import ipmi
from os_failures.drivers import libvirt_driver
__version__ = pbr.version.VersionInfo(
@ -35,6 +36,9 @@ def connect(cloud_config):
if power_management_params.get('driver') == 'libvirt':
power_management = libvirt_driver.LibvirtDriver(
power_management_params)
elif power_management_params.get('driver') == 'ipmi':
power_management = ipmi.IPMIDriver(
power_management_params)
cloud_management.set_power_management(power_management)

24
os_failures/api/error.py Normal file
View File

@ -0,0 +1,24 @@
# 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.
class OSFException(Exception):
"""Base Exception class"""
class OSFError(OSFException):
"""Base Error class"""
class PowerManagmentError(OSFError):
"""Base Error for Power Managment API"""

View File

@ -25,7 +25,6 @@ class NodeCollection(object):
:return: NodeCollection consisting just one node
"""
pass
def reboot(self):
"""Reboot all nodes gracefully
@ -45,6 +44,12 @@ class NodeCollection(object):
"""
raise NotImplementedError
def poweron(self):
"""Power on all nodes abruptly
"""
raise NotImplementedError
def reset(self):
"""Reset (cold restart) all nodes

View File

@ -52,8 +52,12 @@ class DevStackNode(node_collection.NodeCollection):
def poweroff(self):
self.power_management.poweroff(self.host.mac)
def poweron(self):
self.power_management.poweron(self.host.mac)
def reset(self):
logging.info('Reset nodes: %s', self)
self.power_management.reset(self.host.mac)
def enable_network(self, network_name):
logging.info('Enable network: %s on nodes: %s', network_name, self)

View File

@ -53,6 +53,7 @@ class FuelNodeCollection(node_collection.NodeCollection):
hosts=[random.choice(self.hosts)])
def reboot(self):
raise NotImplementedError
task = {
'command': 'ps aux'
}
@ -60,13 +61,18 @@ class FuelNodeCollection(node_collection.NodeCollection):
self.cloud_management.execute_on_cloud(ips, task)
def oom(self):
raise NotImplementedError
logging.info('Enforce nodes to run out of memory: %s', self)
def poweroff(self):
self.power_management.poweroff([n['mac'] for n in self.hosts])
def poweron(self):
self.power_management.poweron([n['mac'] for n in self.hosts])
def reset(self):
logging.info('Reset nodes: %s', self)
self.power_management.reset([n['mac'] for n in self.hosts])
def enable_network(self, network_name):
logging.info('Enable network: %s on nodes: %s', network_name, self)

View File

@ -0,0 +1,76 @@
# 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 logging
from pyghmi import exceptions as pyghmi_exception
from pyghmi.ipmi import command as ipmi_command
from os_failures.api import error
from os_failures.api import power_management
class IPMIDriver(power_management.PowerManagement):
def __init__(self, params):
self.mac_to_bmc = params['mac_to_bmc']
def _find_bmc_by_mac_address(self, mac_address):
if mac_address not in self.mac_to_bmc:
raise error.PowerManagmentError(
'BMC for Node(%s) not found!' % mac_address)
return self.mac_to_bmc[mac_address]
def _run_set_power_cmd(self, mac_address, cmd, expected_state=None):
bmc = self._find_bmc_by_mac_address(mac_address)
try:
ipmicmd = ipmi_command.Command(bmc=bmc['address'],
userid=bmc['username'],
password=bmc['password'])
ret = ipmicmd.set_power(cmd, wait=True)
except pyghmi_exception.IpmiException as e:
msg = 'IPMI cmd {!r} failed on bmc {!r}, Node({})'.format(
cmd, bmc['address'], mac_address)
logging.error(msg)
logging.exception(e)
raise error.PowerManagmentError(msg)
logging.debug('IPMI response: {}'.format(ret))
if ret.get('powerstate') != expected_state or 'error' in ret:
msg = ('Failed to change power state to {!r} on bmc {!r}, '
'Node({})'.format(expected_state,
bmc['address'],
mac_address))
raise error.PowerManagmentError(msg)
def poweroff(self, mac_addresses_list):
for mac_address in mac_addresses_list:
logging.info('Power off Node with MAC address: %s', mac_address)
self._run_set_power_cmd(
mac_address, cmd='off', expected_state='off')
logging.info('Node(%s) was powered off' % mac_address)
def poweron(self, mac_addresses_list):
for mac_address in mac_addresses_list:
logging.info('Power on Node with MAC address: %s', mac_address)
self._run_set_power_cmd(
mac_address, cmd='on', expected_state='on')
logging.info('Node(%s) was powered on' % mac_address)
def reset(self, mac_addresses_list):
for mac_address in mac_addresses_list:
logging.info('Reset Node with MAC address: %s', mac_address)
# boot -- If system is off, then 'on', else 'reset'
self._run_set_power_cmd(mac_address, cmd='boot')
# NOTE(astudenov): this command does not wait for node to boot
logging.info('Node(%s) was reset' % mac_address)

View File

@ -16,6 +16,7 @@ from xml.dom import minidom
import libvirt
from os_failures.api import error
from os_failures.api import power_management
@ -42,8 +43,8 @@ class LibvirtDriver(power_management.PowerManagement):
if mac_address == mac.getAttribute('address'):
return domain
# TODO(ylobankov): Use more specific exception here in the future
raise Exception('Node with MAC address %s not found!' % mac_address)
raise error.PowerManagmentError(
'Node with MAC address %s not found!' % mac_address)
def poweroff(self, mac_addresses_list):
for mac_address in mac_addresses_list:

View File

@ -11,4 +11,5 @@ oslo.log>=1.12.0 # Apache-2.0
oslo.serialization>=1.10.0 # Apache-2.0
oslo.utils!=2.6.0,>=2.4.0 # Apache-2.0
libvirt-python>=1.2.5 # LGPLv2+
pyghmi>=1.0.3 # Apache-2.0
six>=1.9.0