Introduce support for APC MasterSwitchPlus and Rack PDU

Provide 3 new snmp_driver values for different APC product families:
  - apc_masterswitch
  - apc_masterswitchplus
  - apc_rackpdu

The "apc" snmp_driver is still supported and maps to the MasterSwitch driver
to avoid breaking backwards compatibility.

Different APC product families support different OIDs for power control.
  - APC MasterSwitch uses sPDUOutletCtl
  - APC MasterSwitchPlus uses sPDUOutletControlMSPOutletCommand
  - APC Rack PDU uses rPDUOutletControlOutletCommand

Change-Id: I9d8724543d7da7b1c9cdc180c3396d131ed52615
Closes-Bug: #1471025
This commit is contained in:
Mathieu Mitchell 2015-07-02 17:59:39 -04:00
parent 7f88443bce
commit 61f2f07569
4 changed files with 236 additions and 57 deletions

View File

@ -42,60 +42,10 @@ AMT
SNMP
----
The SNMP power driver enables control of power distribution units of the type
frequently found in data centre racks. PDUs frequently have a management
ethernet interface and SNMP support enabling control of the power outlets.
.. toctree::
:maxdepth: 1
The SNMP power driver works with the PXE driver for network deployment and
network-configured boot.
Supported PDUs
^^^^^^^^^^^^^^
- American Power Conversion (APC)
- CyberPower (implemented according to MIB spec but not tested on hardware)
- EatonPower (implemented according to MIB spec but not tested on hardware)
- Teltronix
Software requirements
^^^^^^^^^^^^^^^^^^^^^
- The PySNMP package must be installed, variously referred to as ``pysnmp``
or ``python-pysnmp``
Enabling the SNMP power driver
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Add ``pxe_snmp`` to the list of ``enabled_drivers`` in
``/etc/ironic/ironic.conf``
- Ironic Conductor must be restarted for the new driver to be loaded.
Ironic node configuration
^^^^^^^^^^^^^^^^^^^^^^^^^
Nodes are configured for SNMP control by setting the ironic node object's
``driver`` property to be ``pxe_snmp``. Further configuration values are
added to ``driver_info``:
- ``snmp_address``: the IPv4 address of the PDU controlling this node.
- ``snmp_port``: (optional) A non-standard UDP port to use for SNMP operations.
If not specified, the default port (161) is used.
- ``snmp_outlet``: The power outlet on the PDU (1-based indexing).
- ``snmp_protocol``: (optional) SNMP protocol version
(permitted values ``1``, ``2c`` or ``3``). If not specified, SNMPv1
is chosen.
- ``snmp_community``: (Required for SNMPv1 and SNMPv2c) SNMP community
parameter for reads and writes to the PDU.
- ``snmp_security``: (Required for SNMPv3) SNMP security string.
PDU configuration
^^^^^^^^^^^^^^^^^
This version of the SNMP power driver does not support handling
PDU authentication credentials. When using SNMPv3, the PDU must be
configured for ``NoAuthentication`` and ``NoEncryption``. The
security name is used analogously to the SNMP community in early
SNMP versions.
../drivers/snmp
iLO driver
----------

View File

@ -0,0 +1,91 @@
===========
SNMP driver
===========
The SNMP power driver enables control of power distribution units of the type
frequently found in data centre racks. PDUs frequently have a management
ethernet interface and SNMP support enabling control of the power outlets.
The SNMP power driver works with the PXE driver for network deployment and
network-configured boot.
List of supported devices
=========================
This is a non-exhaustive list of supported devices. Any device not listed in
this table could possibly work using a similar driver.
Please report any device status.
============== ========== ========== =====================
Manufacturer Model Supported? Driver name
============== ========== ========== =====================
APC AP7920 Yes apc_masterswitch
APC AP9606 Yes apc_masterswitch
APC AP9225 Yes apc_masterswitchplus
APC AP7155 Yes apc_rackpdu
APC AP7900 Yes apc_rackpdu
APC AP7901 Yes apc_rackpdu
APC AP7902 Yes apc_rackpdu
APC AP7911a Yes apc_rackpdu
APC AP7930 Yes apc_rackpdu
APC AP7931 Yes apc_rackpdu
APC AP7932 Yes apc_rackpdu
APC AP7940 Yes apc_rackpdu
APC AP7941 Yes apc_rackpdu
APC AP7951 Yes apc_rackpdu
APC AP7960 Yes apc_rackpdu
APC AP7990 Yes apc_rackpdu
APC AP7998 Yes apc_rackpdu
APC AP8941 Yes apc_rackpdu
APC AP8953 Yes apc_rackpdu
APC AP8959 Yes apc_rackpdu
APC AP8961 Yes apc_rackpdu
APC AP8965 Yes apc_rackpdu
Aten all? Yes aten
CyberPower all? Untested cyberpower
EatonPower all? Untested eatonpower
Teltronix all? Yes teltronix
============== ========== ========== =====================
Software Requirements
=====================
- The PySNMP package must be installed, variously referred to as ``pysnmp``
or ``python-pysnmp``
Enabling the SNMP Power Driver
==============================
- Add ``pxe_snmp`` to the list of ``enabled_drivers`` in
``/etc/ironic/ironic.conf``
- Ironic Conductor must be restarted for the new driver to be loaded.
Ironic Node Configuration
=========================
Nodes are configured for SNMP control by setting the Ironic node object's
``driver`` property to be ``pxe_snmp``. Further configuration values are
added to ``driver_info``:
- ``snmp_driver``: PDU manufacturer driver
- ``snmp_address``: the IPv4 address of the PDU controlling this node.
- ``snmp_port``: (optional) A non-standard UDP port to use for SNMP operations.
If not specified, the default port (161) is used.
- ``snmp_outlet``: The power outlet on the PDU (1-based indexing).
- ``snmp_protocol``: (optional) SNMP protocol version
(permitted values ``1``, ``2c`` or ``3``). If not specified, SNMPv1
is chosen.
- ``snmp_community``: (Required for SNMPv1 and SNMPv2c) SNMP community
parameter for reads and writes to the PDU.
- ``snmp_security``: (Required for SNMPv3) SNMP security string.
PDU Configuration
=================
This version of the SNMP power driver does not support handling
PDU authentication credentials. When using SNMPv3, the PDU must be
configured for ``NoAuthentication`` and ``NoEncryption``. The
security name is used analogously to the SNMP community in early
SNMP versions.

View File

@ -398,10 +398,10 @@ class SNMPDriverAten(SNMPDriverSimple):
return self.oid_enterprise + self.oid_device + (outlet, 0,)
class SNMPDriverAPC(SNMPDriverSimple):
"""SNMP driver class for APC PDU devices.
class SNMPDriverAPCMasterSwitch(SNMPDriverSimple):
"""SNMP driver class for APC MasterSwitch PDU devices.
SNMP objects for APC PDU:
SNMP objects for APC SNMPDriverAPCMasterSwitch PDU:
1.3.6.1.4.1.318.1.1.4.4.2.1.3 sPDUOutletCtl
Values: 1=On, 2=Off, 3=PowerCycle, [...more options follow]
"""
@ -411,6 +411,32 @@ class SNMPDriverAPC(SNMPDriverSimple):
value_power_off = 2
class SNMPDriverAPCMasterSwitchPlus(SNMPDriverSimple):
"""SNMP driver class for APC MasterSwitchPlus PDU devices.
SNMP objects for APC SNMPDriverAPCMasterSwitchPlus PDU:
1.3.6.1.4.1.318.1.1.6.5.1.1.5 sPDUOutletControlMSPOutletCommand
Values: 1=On, 3=Off, [...more options follow]
"""
oid_device = (318, 1, 1, 6, 5, 1, 1, 5)
value_power_on = 1
value_power_off = 3
class SNMPDriverAPCRackPDU(SNMPDriverSimple):
"""SNMP driver class for APC RackPDU devices.
SNMP objects for APC SNMPDriverAPCMasterSwitch PDU:
# 1.3.6.1.4.1.318.1.1.12.3.3.1.1.4 rPDUOutletControlOutletCommand
Values: 1=On, 2=Off, 3=PowerCycle, [...more options follow]
"""
oid_device = (318, 1, 1, 12, 3, 3, 1, 1, 4)
value_power_on = 1
value_power_off = 2
class SNMPDriverCyberPower(SNMPDriverSimple):
"""SNMP driver class for CyberPower PDU devices.
@ -522,7 +548,10 @@ class SNMPDriverEatonPower(SNMPDriverBase):
# A dictionary of supported drivers keyed by snmp_driver attribute
DRIVER_CLASSES = {
'apc': SNMPDriverAPC,
'apc': SNMPDriverAPCMasterSwitch,
'apc_masterswitch': SNMPDriverAPCMasterSwitch,
'apc_masterswitchplus': SNMPDriverAPCMasterSwitchPlus,
'apc_rackpdu': SNMPDriverAPCRackPDU,
'aten': SNMPDriverAten,
'cyberpower': SNMPDriverCyberPower,
'eatonpower': SNMPDriverEatonPower,

View File

@ -189,6 +189,27 @@ class SNMPValidateParametersTestCase(db_base.DbTestCase):
info = snmp._parse_driver_info(node)
self.assertEqual('apc', info.get('driver'))
def test__parse_driver_info_apc_masterswitch(self):
# Make sure the APC driver type is parsed.
info = db_utils.get_test_snmp_info(snmp_driver='apc_masterswitch')
node = self._get_test_node(info)
info = snmp._parse_driver_info(node)
self.assertEqual('apc_masterswitch', info.get('driver'))
def test__parse_driver_info_apc_masterswitchplus(self):
# Make sure the APC driver type is parsed.
info = db_utils.get_test_snmp_info(snmp_driver='apc_masterswitchplus')
node = self._get_test_node(info)
info = snmp._parse_driver_info(node)
self.assertEqual('apc_masterswitchplus', info.get('driver'))
def test__parse_driver_info_apc_rackpdu(self):
# Make sure the APC driver type is parsed.
info = db_utils.get_test_snmp_info(snmp_driver='apc_rackpdu')
node = self._get_test_node(info)
info = snmp._parse_driver_info(node)
self.assertEqual('apc_rackpdu', info.get('driver'))
def test__parse_driver_info_aten(self):
# Make sure the Aten driver type is parsed.
info = db_utils.get_test_snmp_info(snmp_driver='aten')
@ -834,6 +855,94 @@ class SNMPDeviceDriverTestCase(db_base.DbTestCase):
def test_apc_power_reset(self, mock_get_client):
self._test_simple_device_power_reset('apc', mock_get_client)
def test_apc_masterswitch_snmp_objects(self, mock_get_client):
# Ensure the correct SNMP object OIDs and values are used by the APC
# masterswitch driver
self._update_driver_info(snmp_driver="apc_masterswitch",
snmp_outlet="6")
driver = snmp._get_driver(self.node)
oid = (1, 3, 6, 1, 4, 1, 318, 1, 1, 4, 4, 2, 1, 3, 6)
self.assertEqual(oid, driver._snmp_oid())
self.assertEqual(1, driver.value_power_on)
self.assertEqual(2, driver.value_power_off)
def test_apc_masterswitch_power_state_on(self, mock_get_client):
self._test_simple_device_power_state_on('apc_masterswitch',
mock_get_client)
def test_apc_masterswitch_power_state_off(self, mock_get_client):
self._test_simple_device_power_state_off('apc_masterswitch',
mock_get_client)
def test_apc_masterswitch_power_on(self, mock_get_client):
self._test_simple_device_power_on('apc_masterswitch', mock_get_client)
def test_apc_masterswitch_power_off(self, mock_get_client):
self._test_simple_device_power_off('apc_masterswitch', mock_get_client)
def test_apc_masterswitch_power_reset(self, mock_get_client):
self._test_simple_device_power_reset('apc_masterswitch',
mock_get_client)
def test_apc_masterswitchplus_snmp_objects(self, mock_get_client):
# Ensure the correct SNMP object OIDs and values are used by the APC
# masterswitchplus driver
self._update_driver_info(snmp_driver="apc_masterswitchplus",
snmp_outlet="6")
driver = snmp._get_driver(self.node)
oid = (1, 3, 6, 1, 4, 1, 318, 1, 1, 6, 5, 1, 1, 5, 6)
self.assertEqual(oid, driver._snmp_oid())
self.assertEqual(1, driver.value_power_on)
self.assertEqual(3, driver.value_power_off)
def test_apc_masterswitchplus_power_state_on(self, mock_get_client):
self._test_simple_device_power_state_on('apc_masterswitchplus',
mock_get_client)
def test_apc_masterswitchplus_power_state_off(self, mock_get_client):
self._test_simple_device_power_state_off('apc_masterswitchplus',
mock_get_client)
def test_apc_masterswitchplus_power_on(self, mock_get_client):
self._test_simple_device_power_on('apc_masterswitchplus',
mock_get_client)
def test_apc_masterswitchplus_power_off(self, mock_get_client):
self._test_simple_device_power_off('apc_masterswitchplus',
mock_get_client)
def test_apc_masterswitchplus_power_reset(self, mock_get_client):
self._test_simple_device_power_reset('apc_masterswitchplus',
mock_get_client)
def test_apc_rackpdu_snmp_objects(self, mock_get_client):
# Ensure the correct SNMP object OIDs and values are used by the APC
# rackpdu driver
self._update_driver_info(snmp_driver="apc_rackpdu",
snmp_outlet="6")
driver = snmp._get_driver(self.node)
oid = (1, 3, 6, 1, 4, 1, 318, 1, 1, 12, 3, 3, 1, 1, 4, 6)
self.assertEqual(oid, driver._snmp_oid())
self.assertEqual(1, driver.value_power_on)
self.assertEqual(2, driver.value_power_off)
def test_apc_rackpdu_power_state_on(self, mock_get_client):
self._test_simple_device_power_state_on('apc_rackpdu', mock_get_client)
def test_apc_rackpdu_power_state_off(self, mock_get_client):
self._test_simple_device_power_state_off('apc_rackpdu',
mock_get_client)
def test_apc_rackpdu_power_on(self, mock_get_client):
self._test_simple_device_power_on('apc_rackpdu', mock_get_client)
def test_apc_rackpdu_power_off(self, mock_get_client):
self._test_simple_device_power_off('apc_rackpdu', mock_get_client)
def test_apc_rackpdu_power_reset(self, mock_get_client):
self._test_simple_device_power_reset('apc_rackpdu', mock_get_client)
def test_aten_snmp_objects(self, mock_get_client):
# Ensure the correct SNMP object OIDs and values are used by the
# Aten driver