Provide APC rack PDU outlet names

This commit introduce the possibility to define other kinds of PDU SNMP
registers.  The first to be implemented is the outlet name based on the
outlet number for APC rack PDU.
This commit is contained in:
Jonathan Provost 2016-10-24 09:42:08 -04:00
parent 022a9ecd9e
commit a6c69ddf50
16 changed files with 140 additions and 78 deletions

View File

@ -13,6 +13,7 @@ oslotest>=1.10.0 # Apache-2.0
testrepository>=0.0.18
testscenarios>=0.4
testtools>=1.4.0
cached-property>=1.3.0
# releasenotes
reno>=1.6.2 # Apache2

View File

@ -42,9 +42,7 @@ class PDUOutletStates(BasePDUOutletStates):
}
class PDUOutlet(object):
states = PDUOutletStates()
class PDUOutletRegister(object):
def __init__(self, pdu_name, outlet_number, core):
self.pdu_name = pdu_name
self.outlet_number = outlet_number
@ -52,14 +50,26 @@ class PDUOutlet(object):
self.oid = None
@property
def state(self):
def value(self):
return self._value
@value.setter
def value(self, value):
self._value = value
class PDUOutletControl(PDUOutletRegister):
states = PDUOutletStates()
@property
def value(self):
return self.states.from_core(
self.core.get_pdu_outlet_state(
pdu=self.pdu_name,
outlet=self.outlet_number))
@state.setter
def state(self, state):
@value.setter
def value(self, state):
self.core.pdu_outlet_state_changed(
pdu=self.pdu_name,
outlet=self.outlet_number,
@ -69,16 +79,18 @@ class PDUOutlet(object):
class PDU(object):
outlet_count = 1
outlet_index_start = 1
outlet_class = PDUOutlet
outlet_classes = [PDUOutletControl]
def __init__(self, name, core):
self.name = name
self.oids = [
self.outlet_class(pdu_name=self.name,
outlet_number=o + self.outlet_index_start,
core=core,
) for o in range(self.outlet_count)
]
mapping = {}
for outlet_number in range(self.outlet_count):
for outlet_class in self.outlet_classes:
outlet_register = outlet_class(
pdu_name=self.name,
outlet_number=outlet_number + self.outlet_index_start,
core=core)
mapping[outlet_register.oid] = outlet_register
self.oid_mapping = {oid.oid: oid for oid in self.oids}
self.oid_mapping = mapping

View File

@ -17,11 +17,15 @@ from pyasn1.type import univ
from virtualpdu import core
from virtualpdu.pdu import BasePDUOutletStates
from virtualpdu.pdu import PDU
from virtualpdu.pdu import PDUOutlet
from virtualpdu.pdu import PDUOutletControl
from virtualpdu.pdu import PDUOutletRegister
rPDU_outlet_control_outlet_command = \
(1, 3, 6, 1, 4, 1, 318, 1, 1, 12, 3, 3, 1, 1, 4)
rPDU_outlet_config_outlet_name = \
(1, 3, 6, 1, 4, 1, 318, 1, 1, 12, 3, 4, 1, 1, 2)
class APCRackPDUOutletStates(BasePDUOutletStates):
IMMEDIATE_ON = univ.Integer(1)
@ -39,16 +43,29 @@ class APCRackPDUOutletStates(BasePDUOutletStates):
}
class APCRackPDUOutlet(PDUOutlet):
class APCRackPDUOutletControl(PDUOutletControl):
states = APCRackPDUOutletStates()
def __init__(self, pdu_name, outlet_number, core):
super(APCRackPDUOutlet, self).__init__(
super(APCRackPDUOutletControl, self).__init__(
pdu_name, outlet_number, core)
self.oid = rPDU_outlet_control_outlet_command + (self.outlet_number, )
class APCRackPDUOutletName(PDUOutletRegister):
states = APCRackPDUOutletStates()
def __init__(self, pdu_name, outlet_number, core):
super(APCRackPDUOutletName, self).__init__(
pdu_name, outlet_number, core)
self.oid = rPDU_outlet_config_outlet_name + (self.outlet_number, )
@property
def value(self):
return univ.OctetString('Outlet #{}'.format(self.outlet_number))
class APCRackPDU(PDU):
outlet_count = 8
outlet_index_start = 1
outlet_class = APCRackPDUOutlet
outlet_classes = [APCRackPDUOutletControl, APCRackPDUOutletName]

View File

@ -17,7 +17,7 @@ from pyasn1.type import univ
from virtualpdu import core
from virtualpdu.pdu import BasePDUOutletStates
from virtualpdu.pdu import PDU
from virtualpdu.pdu import PDUOutlet
from virtualpdu.pdu import PDUOutletControl
sBTA_modules_RPC_outlet_state = (1, 3, 6, 1, 4, 1, 4779, 1, 3, 5, 3, 1, 3)
@ -37,11 +37,11 @@ class BaytechMRP27PDUOutletStates(BasePDUOutletStates):
}
class BaytechMRP27PDUOutlet(PDUOutlet):
class BaytechMRP27PDUOutletControl(PDUOutletControl):
states = BaytechMRP27PDUOutletStates()
def __init__(self, pdu_name, outlet_number, core):
super(BaytechMRP27PDUOutlet, self).__init__(
super(BaytechMRP27PDUOutletControl, self).__init__(
pdu_name, outlet_number, core)
self.oid = sBTA_modules_RPC_outlet_state + (1, self.outlet_number)
@ -49,4 +49,4 @@ class BaytechMRP27PDUOutlet(PDUOutlet):
class BaytechMRP27PDU(PDU):
outlet_count = 24
outlet_index_start = 1
outlet_class = BaytechMRP27PDUOutlet
outlet_classes = [BaytechMRP27PDUOutletControl]

View File

@ -65,14 +65,14 @@ class SNMPPDUHandler(object):
for oid, val in protocol.apiPDU.getVarBinds(request_pdus):
if oid in self.pdu.oid_mapping:
var_binds.append(
(oid, self.pdu.oid_mapping[oid].state))
(oid, self.pdu.oid_mapping[oid].value))
else:
return
elif request_pdus.isSameTypeWith(protocol.SetRequestPDU()):
for oid, val in protocol.apiPDU.getVarBinds(request_pdus):
error_index += 1
if oid in self.pdu.oid_mapping:
self.pdu.oid_mapping[oid].state = val
self.pdu.oid_mapping[oid].value = val
var_binds.append((oid, val))
else:
var_binds.append((oid, val))

View File

@ -23,6 +23,7 @@ from virtualpdu.tests import snmp_client
class PDUTestCase(base.TestCase):
pdu_class = NotImplementedError
outlet_control_class = NotImplementedError
def setUp(self):
super(PDUTestCase, self).setUp()

View File

@ -13,12 +13,14 @@
# limitations under the License.
from virtualpdu import core
from virtualpdu.pdu.apc_rackpdu import APCRackPDU
from virtualpdu.pdu.apc_rackpdu import APCRackPDUOutletControl
from virtualpdu.tests.integration.pdu import PDUTestCase
class TestAPCRackPDU(PDUTestCase):
pdu_class = APCRackPDU
outlet_control_class = APCRackPDUOutletControl
def test_all_ports_are_on_by_default(self):
self.core_mock.get_pdu_outlet_state.return_value = core.POWER_ON
@ -43,13 +45,13 @@ class TestAPCRackPDU(PDUTestCase):
outlet_1 = enterprises + rPDUControl + (1,)
self.core_mock.get_pdu_outlet_state.return_value = core.POWER_ON
self.assertEqual(self.pdu.outlet_class.states.IMMEDIATE_ON,
self.assertEqual(self.outlet_control_class.states.IMMEDIATE_ON,
self.snmp_get(outlet_1))
self.snmp_set(outlet_1, self.pdu.outlet_class.states.IMMEDIATE_OFF)
self.snmp_set(outlet_1, self.outlet_control_class.states.IMMEDIATE_OFF)
self.core_mock.pdu_outlet_state_changed.assert_called_with(
pdu=self.pdu.name, outlet=1, state=core.POWER_OFF)
self.core_mock.get_pdu_outlet_state.return_value = core.POWER_OFF
self.assertEqual(self.pdu.outlet_class.states.IMMEDIATE_OFF,
self.assertEqual(self.outlet_control_class.states.IMMEDIATE_OFF,
self.snmp_get(outlet_1))

View File

@ -13,12 +13,14 @@
# limitations under the License.
from virtualpdu import core
from virtualpdu.pdu.baytech_mrp27 import BaytechMRP27PDU
from virtualpdu.pdu.baytech_mrp27 import BaytechMRP27PDUOutletControl
from virtualpdu.tests.integration.pdu import PDUTestCase
class TestBaytechMRP27PDU(PDUTestCase):
pdu_class = BaytechMRP27PDU
outlet_control_class = BaytechMRP27PDUOutletControl
def test_all_ports_are_on_by_default(self):
self.core_mock.get_pdu_outlet_state.return_value = core.POWER_ON
@ -43,13 +45,13 @@ class TestBaytechMRP27PDU(PDUTestCase):
outlet_1 = outlet_state_oid + (1, 1)
self.core_mock.get_pdu_outlet_state.return_value = core.POWER_ON
self.assertEqual(self.pdu.outlet_class.states.ON,
self.assertEqual(self.outlet_control_class.states.ON,
self.snmp_get(outlet_1))
self.snmp_set(outlet_1, self.pdu.outlet_class.states.OFF)
self.snmp_set(outlet_1, self.outlet_control_class.states.OFF)
self.core_mock.pdu_outlet_state_changed.assert_called_with(
pdu=self.pdu.name, outlet=1, state=core.POWER_OFF)
self.core_mock.get_pdu_outlet_state.return_value = core.POWER_OFF
self.assertEqual(self.pdu.outlet_class.states.OFF,
self.assertEqual(self.outlet_control_class.states.OFF,
self.snmp_get(outlet_1))

View File

@ -25,6 +25,7 @@ enterprises = (1, 3, 6, 1, 4, 1)
class TestPDU(PDUTestCase):
pdu_class = pdu.PDU
outlet_control_class = pdu.PDUOutletControl
def test_get_unknown_oid(self):
self.assertRaises(RequestTimedOut,
@ -37,11 +38,11 @@ class TestPDU(PDUTestCase):
def test_get_valid_oid_wrong_community(self):
self.core_mock.get_pdu_outlet_state.return_value = core.POWER_ON
self.pdu.oid_mapping[enterprises + (88, 1)] = \
pdu.PDUOutlet(pdu_name=self.pdu.name,
outlet_number=1,
core=self.core_mock)
pdu.PDUOutletControl(pdu_name=self.pdu.name,
outlet_number=1,
core=self.core_mock)
self.assertEqual(self.pdu.outlet_class.states.ON,
self.assertEqual(self.outlet_control_class.states.ON,
self.snmp_get(enterprises + (88, 1)))
self.assertRaises(RequestTimedOut,
@ -51,5 +52,5 @@ class TestPDU(PDUTestCase):
def test_set_wrong_community(self):
self.assertRaises(RequestTimedOut,
self.snmp_set, enterprises + (42,),
self.pdu.outlet_class.states.ON,
self.outlet_control_class.states.ON,
community='wrong')

View File

@ -44,7 +44,7 @@ class TestSNMPPDUHarness(base.TestCase):
mock_pdu.oid_mapping = dict()
mock_pdu.oid_mapping[(1, 3, 6, 99)] = mock.Mock()
mock_pdu.oid_mapping[(1, 3, 6, 99)].state = univ.Integer(42)
mock_pdu.oid_mapping[(1, 3, 6, 99)].value = univ.Integer(42)
self.assertEqual(42, client.get_one((1, 3, 6, 99)))
@ -74,6 +74,6 @@ class TestSNMPPDUHarness(base.TestCase):
client.set((1, 3, 6, 98), univ.Integer(99))
self.assertEqual(univ.Integer(99),
mock_pdu.oid_mapping[(1, 3, 6, 98)].state)
mock_pdu.oid_mapping[(1, 3, 6, 98)].value)
harness.stop()

View File

@ -64,13 +64,15 @@ class TestCoreIntegration(base.TestCase):
pdu = apc_rackpdu.APCRackPDU('my_pdu', self.core)
snmp_client_ = self.get_harness_client(pdu)
snmp_client_.set(self.outlet_oid,
pdu.outlet_class.states.IMMEDIATE_OFF)
snmp_client_.set(
self.outlet_oid,
apc_rackpdu.APCRackPDUOutletControl.states.IMMEDIATE_OFF)
self.assertEqual(drivers.POWER_OFF,
self.driver.get_power_state('test'))
snmp_client_.set(self.outlet_oid,
pdu.outlet_class.states.IMMEDIATE_ON)
snmp_client_.set(
self.outlet_oid,
apc_rackpdu.APCRackPDUOutletControl.states.IMMEDIATE_ON)
self.assertEqual(drivers.POWER_ON,
self.driver.get_power_state('test'))
@ -82,8 +84,9 @@ class TestCoreIntegration(base.TestCase):
pdu = apc_rackpdu.APCRackPDU('my_pdu', my_core)
snmp_client_ = self.get_harness_client(pdu)
self.assertEqual(pdu.outlet_class.states.IMMEDIATE_ON,
snmp_client_.get_one(self.outlet_oid))
self.assertEqual(
apc_rackpdu.APCRackPDUOutletControl.states.IMMEDIATE_ON,
snmp_client_.get_one(self.outlet_oid))
def test_initial_outlet_power_state_off(self):
my_core = core.Core(driver=self.driver,
@ -93,5 +96,6 @@ class TestCoreIntegration(base.TestCase):
pdu = apc_rackpdu.APCRackPDU('my_pdu', my_core)
snmp_client_ = self.get_harness_client(pdu)
self.assertEqual(pdu.outlet_class.states.IMMEDIATE_OFF,
snmp_client_.get_one(self.outlet_oid))
self.assertEqual(
apc_rackpdu.APCRackPDUOutletControl.states.IMMEDIATE_OFF,
snmp_client_.get_one(self.outlet_oid))

View File

@ -22,7 +22,7 @@ from pysnmp.entity.rfc3413.oneliner import cmdgen
from retrying import retry
from virtualpdu.pdu import apc_rackpdu
from virtualpdu.pdu.apc_rackpdu import APCRackPDUOutlet
from virtualpdu.pdu.apc_rackpdu import APCRackPDUOutletControl
from virtualpdu.tests import base
from virtualpdu.tests import snmp_client
@ -151,7 +151,7 @@ outlet_default_state = invalid_mode
except OSError:
pass
@retry(stop_max_attempt_number=10)
@retry(stop_max_attempt_number=10, wait_fixed=1000)
def _poll_process_for_done(self, process):
return self.assertIsNotNone(process.poll())
@ -165,7 +165,7 @@ def _turn_off_outlet(community, listen_address, outlet, port):
timeout=1,
retries=1)
snmp_client_.set(outlet_oid, APCRackPDUOutlet.states.IMMEDIATE_OFF)
snmp_client_.set(outlet_oid, APCRackPDUOutletControl.states.IMMEDIATE_OFF)
def _get_entry_point_path(entry_point):

View File

@ -11,14 +11,27 @@
# 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.
from cached_property import cached_property
from mock import mock
from virtualpdu import core
class BasePDUTests(object):
pdu_class = None
outlet_control_oid = None
@cached_property
def core_mock(self):
return mock.Mock()
@cached_property
def pdu(self):
return self.pdu_class(name='my_pdu', core=self.core_mock)
def test_power_on_notifies_core(self):
self.pdu.oids[0].state = \
self.pdu.outlet_class.states.from_core(core.POWER_ON)
outlet_control = self.pdu.oid_mapping[self.outlet_control_oid]
outlet_control.value = \
outlet_control.states.from_core(core.POWER_ON)
self.core_mock.pdu_outlet_state_changed.assert_called_with(
pdu='my_pdu',
@ -26,8 +39,9 @@ class BasePDUTests(object):
state=core.POWER_ON)
def test_reboot_notifies_core(self):
self.pdu.oids[0].state = \
self.pdu.outlet_class.states.from_core(core.REBOOT)
outlet_control = self.pdu.oid_mapping[self.outlet_control_oid]
outlet_control.value = \
outlet_control.states.from_core(core.REBOOT)
self.core_mock.pdu_outlet_state_changed.assert_called_with(
pdu='my_pdu',
@ -35,8 +49,9 @@ class BasePDUTests(object):
state=core.REBOOT)
def test_power_off_notifies_core(self):
self.pdu.oids[0].state = \
self.pdu.outlet_class.states.from_core(core.POWER_OFF)
outlet_control = self.pdu.oid_mapping[self.outlet_control_oid]
outlet_control.value = \
outlet_control.states.from_core(core.POWER_OFF)
self.core_mock.pdu_outlet_state_changed.assert_called_with(
pdu='my_pdu',
@ -44,11 +59,12 @@ class BasePDUTests(object):
state=core.POWER_OFF)
def test_read_power_on(self):
outlet_control = self.pdu.oid_mapping[self.outlet_control_oid]
self.core_mock.get_pdu_outlet_state.return_value = core.POWER_ON
self.assertEqual(
self.pdu.outlet_class.states.from_core(core.POWER_ON),
self.pdu.oids[0].state
outlet_control.states.from_core(core.POWER_ON),
outlet_control.value
)
self.core_mock.get_pdu_outlet_state.assert_called_with(
@ -56,11 +72,12 @@ class BasePDUTests(object):
outlet=1)
def test_read_power_off(self):
outlet_control = self.pdu.oid_mapping[self.outlet_control_oid]
self.core_mock.get_pdu_outlet_state.return_value = core.POWER_OFF
self.assertEqual(
self.pdu.outlet_class.states.from_core(core.POWER_OFF),
self.pdu.oids[0].state
outlet_control.states.from_core(core.POWER_OFF),
outlet_control.value
)
self.core_mock.get_pdu_outlet_state.assert_called_with(

View File

@ -11,15 +11,25 @@
# 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.
from mock import mock
from virtualpdu.pdu.apc_rackpdu import APCRackPDU
from pyasn1.type import univ
from virtualpdu.pdu import apc_rackpdu
from virtualpdu.tests import base
from virtualpdu.tests.unit.pdu.base_pdu_test_cases import BasePDUTests
class TestAPCRackPDU(base.TestCase, BasePDUTests):
def setUp(self):
super(TestAPCRackPDU, self).setUp()
self.core_mock = mock.Mock()
self.pdu = APCRackPDU(name='my_pdu', core=self.core_mock)
pdu_class = apc_rackpdu.APCRackPDU
outlet_control_oid = \
apc_rackpdu.rPDU_outlet_control_outlet_command \
+ (apc_rackpdu.APCRackPDU.outlet_index_start,)
outlet_name_oid = \
apc_rackpdu.rPDU_outlet_config_outlet_name \
+ (apc_rackpdu.APCRackPDU.outlet_index_start,)
def test_read_outlet_name(self):
outlet_name = self.pdu.oid_mapping[self.outlet_name_oid]
self.assertEqual(
univ.OctetString('Outlet #1'),
outlet_name.value
)

View File

@ -12,14 +12,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from mock import mock
from virtualpdu.pdu import PDU
from virtualpdu.tests import base
from virtualpdu.tests.unit.pdu.base_pdu_test_cases import BasePDUTests
class TestPDU(base.TestCase, BasePDUTests):
def setUp(self):
super(TestPDU, self).setUp()
self.core_mock = mock.Mock()
self.pdu = PDU(name='my_pdu', core=self.core_mock)
pdu_class = PDU
outlet_control_oid = None

View File

@ -12,15 +12,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from mock import mock
from virtualpdu.pdu.baytech_mrp27 import BaytechMRP27PDU
from virtualpdu.pdu import baytech_mrp27
from virtualpdu.tests import base
from virtualpdu.tests.unit.pdu.base_pdu_test_cases import BasePDUTests
class TestBaytechMRP27PDU(base.TestCase, BasePDUTests):
def setUp(self):
super(TestBaytechMRP27PDU, self).setUp()
self.core_mock = mock.Mock()
self.pdu = BaytechMRP27PDU(name='my_pdu', core=self.core_mock)
pdu_class = baytech_mrp27.BaytechMRP27PDU
outlet_control_oid = \
baytech_mrp27.sBTA_modules_RPC_outlet_state \
+ (1, baytech_mrp27.BaytechMRP27PDU.outlet_index_start,)