Add IPMI driver
Change-Id: I850974c707842683d660d9fd820c1cca98d4a21b
This commit is contained in:
parent
5941f0b117
commit
e1f57a21ce
71
examples/power_off_on_ipmi_node.py
Normal file
71
examples/power_off_on_ipmi_node.py
Normal 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()
|
@ -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
24
os_failures/api/error.py
Normal 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"""
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
76
os_failures/drivers/ipmi.py
Normal file
76
os_failures/drivers/ipmi.py
Normal 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)
|
@ -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:
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user