Merge pull request #25 from JoProvost/power_state_in_core
Move the outlet state responsibility to the core
This commit is contained in:
commit
a7f2d92cd0
@ -19,32 +19,51 @@ REBOOT = 'REBOOT'
|
||||
|
||||
|
||||
class Core(object):
|
||||
def __init__(self, driver, mapping):
|
||||
def __init__(self, driver, mapping, store, default_state):
|
||||
self.driver = driver
|
||||
self.mapping = mapping
|
||||
self.store = store
|
||||
self.default_state = default_state
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
||||
def pdu_outlet_state_changed(self, name, outlet_number, state):
|
||||
def pdu_outlet_state_changed(self, pdu, outlet, state):
|
||||
self.store[(pdu, outlet)] = state
|
||||
|
||||
self.logger.info("PDU '{}', outlet '{}' has new state: '{}'".format(
|
||||
name, outlet_number, state)
|
||||
pdu, outlet, state)
|
||||
)
|
||||
try:
|
||||
server_name = self._get_server_name(name, outlet_number)
|
||||
device = self._get_device(pdu, outlet)
|
||||
|
||||
self.logger.debug(
|
||||
"Found server '{}' on PDU '{}' outlet '{}'".format(
|
||||
server_name, name, outlet_number)
|
||||
device, pdu, outlet)
|
||||
)
|
||||
except KeyError:
|
||||
return
|
||||
|
||||
if state == POWER_ON:
|
||||
self.driver.power_on(server_name)
|
||||
self.driver.power_on(device)
|
||||
elif state == POWER_OFF:
|
||||
self.driver.power_off(server_name)
|
||||
self.driver.power_off(device)
|
||||
elif state == REBOOT:
|
||||
self.driver.power_off(server_name)
|
||||
self.driver.power_on(server_name)
|
||||
self.driver.power_off(device)
|
||||
self.driver.power_on(device)
|
||||
|
||||
def _get_server_name(self, pdu_name, outlet_number):
|
||||
return self.mapping[(pdu_name, outlet_number)]
|
||||
def get_pdu_outlet_state(self, pdu, outlet):
|
||||
try:
|
||||
return self.store[(pdu, outlet)]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
try:
|
||||
device = self._get_device(pdu, outlet)
|
||||
power_state = self.driver.get_power_state(device)
|
||||
except KeyError:
|
||||
power_state = self.default_state
|
||||
|
||||
self.store[(pdu, outlet)] = power_state
|
||||
return power_state
|
||||
|
||||
def _get_device(self, pdu, outlet):
|
||||
return self.mapping[(pdu, outlet)]
|
||||
|
@ -40,7 +40,9 @@ def main():
|
||||
config.read(config_file)
|
||||
driver = get_driver_from_config(config)
|
||||
mapping = get_mapping_for_config(config)
|
||||
core = virtualpdu.core.Core(driver=driver, mapping=mapping)
|
||||
outlet_default_state = get_default_state_from_config(config)
|
||||
core = virtualpdu.core.Core(driver=driver, mapping=mapping, store={},
|
||||
default_state=outlet_default_state)
|
||||
pdu_threads = []
|
||||
for pdu in [s for s in config.sections() if s != 'global']:
|
||||
|
||||
@ -48,14 +50,7 @@ def main():
|
||||
port = int(config.get(pdu, 'listen_port'))
|
||||
community = config.get(pdu, 'community')
|
||||
|
||||
try:
|
||||
default_state = config.get(pdu, 'outlet_default_state')
|
||||
except configparser.NoOptionError:
|
||||
default_state = 'ON'
|
||||
|
||||
outlet_default_state = parse_default_state_config(default_state)
|
||||
|
||||
apc_pdu = apc_rackpdu.APCRackPDU(pdu, core, outlet_default_state)
|
||||
apc_pdu = apc_rackpdu.APCRackPDU(pdu, core)
|
||||
|
||||
pdu_threads.append(pysnmp_handler.SNMPPDUHarness(
|
||||
apc_pdu,
|
||||
@ -115,5 +110,13 @@ def get_mapping_for_config(conf):
|
||||
return mapping
|
||||
|
||||
|
||||
def get_default_state_from_config(conf):
|
||||
try:
|
||||
default_state = conf.get('global', 'outlet_default_state')
|
||||
except (configparser.NoSectionError, configparser.NoOptionError):
|
||||
default_state = 'ON'
|
||||
return parse_default_state_config(default_state)
|
||||
|
||||
|
||||
class UnableToParseConfig(Exception):
|
||||
pass
|
||||
|
@ -45,20 +45,18 @@ class PDUOutletStates(BasePDUOutletStates):
|
||||
class PDUOutlet(object):
|
||||
states = PDUOutletStates()
|
||||
|
||||
def __init__(self, outlet_number, pdu, default_state):
|
||||
def __init__(self, outlet_number, pdu):
|
||||
self.outlet_number = outlet_number
|
||||
self.pdu = pdu
|
||||
self._state = default_state
|
||||
self.oid = None
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
return self._state
|
||||
return self.pdu.get_outlet_state(self.outlet_number)
|
||||
|
||||
@state.setter
|
||||
def state(self, state):
|
||||
self._state = state
|
||||
self.pdu.outlet_state_changed(self.outlet_number, self._state)
|
||||
self.pdu.set_outlet_state(self.outlet_number, state)
|
||||
|
||||
|
||||
class PDU(object):
|
||||
@ -66,24 +64,26 @@ class PDU(object):
|
||||
outlet_index_start = 1
|
||||
outlet_class = PDUOutlet
|
||||
|
||||
def __init__(self, name, core, outlet_default_state=core.POWER_ON):
|
||||
def __init__(self, name, core):
|
||||
self.name = name
|
||||
self.core = core
|
||||
|
||||
outlet_native_default_state = \
|
||||
self.outlet_class.states.from_core(outlet_default_state)
|
||||
|
||||
self.oids = [
|
||||
self.outlet_class(outlet_number=o + self.outlet_index_start,
|
||||
pdu=self,
|
||||
default_state=outlet_native_default_state
|
||||
) for o in range(self.outlet_count)
|
||||
]
|
||||
|
||||
self.oid_mapping = {oid.oid: oid for oid in self.oids}
|
||||
|
||||
def outlet_state_changed(self, outlet_number, value):
|
||||
def set_outlet_state(self, outlet_number, value):
|
||||
self.core.pdu_outlet_state_changed(
|
||||
name=self.name,
|
||||
outlet_number=outlet_number,
|
||||
pdu=self.name,
|
||||
outlet=outlet_number,
|
||||
state=self.outlet_class.states.to_core(value))
|
||||
|
||||
def get_outlet_state(self, outlet_number):
|
||||
return self.outlet_class.states.from_core(
|
||||
self.core.get_pdu_outlet_state(
|
||||
pdu=self.name,
|
||||
outlet=outlet_number))
|
||||
|
@ -42,9 +42,9 @@ class APCRackPDUOutletStates(BasePDUOutletStates):
|
||||
class APCRackPDUOutlet(PDUOutlet):
|
||||
states = APCRackPDUOutletStates()
|
||||
|
||||
def __init__(self, outlet_number, pdu, default_state):
|
||||
def __init__(self, outlet_number, pdu):
|
||||
super(APCRackPDUOutlet, self).__init__(
|
||||
outlet_number, pdu, default_state)
|
||||
outlet_number, pdu)
|
||||
self.oid = rPDU_outlet_control_outlet_command + (self.outlet_number, )
|
||||
|
||||
|
||||
|
@ -40,9 +40,9 @@ class BaytechMRP27PDUOutletStates(BasePDUOutletStates):
|
||||
class BaytechMRP27PDUOutlet(PDUOutlet):
|
||||
states = BaytechMRP27PDUOutletStates()
|
||||
|
||||
def __init__(self, outlet_number, pdu, default_state):
|
||||
def __init__(self, outlet_number, pdu):
|
||||
super(BaytechMRP27PDUOutlet, self).__init__(
|
||||
outlet_number, pdu, default_state)
|
||||
outlet_number, pdu)
|
||||
self.oid = sBTA_modules_RPC_outlet_state + (1, self.outlet_number)
|
||||
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
# 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 virtualpdu import core
|
||||
from virtualpdu.pdu.apc_rackpdu import APCRackPDU
|
||||
|
||||
from virtualpdu.tests.integration.pdu import PDUTestCase
|
||||
@ -21,6 +21,8 @@ class TestAPCRackPDU(PDUTestCase):
|
||||
pdu_class = APCRackPDU
|
||||
|
||||
def test_all_ports_are_on_by_default(self):
|
||||
self.core_mock.get_pdu_outlet_state.return_value = core.POWER_ON
|
||||
|
||||
enterprises = (1, 3, 6, 1, 4, 1)
|
||||
rPDUControl = (318, 1, 1, 12, 3, 3, 1, 1, 4)
|
||||
|
||||
@ -40,10 +42,14 @@ class TestAPCRackPDU(PDUTestCase):
|
||||
rPDUControl = (318, 1, 1, 12, 3, 3, 1, 1, 4)
|
||||
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.snmp_get(outlet_1))
|
||||
|
||||
self.snmp_set(outlet_1, self.pdu.outlet_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.snmp_get(outlet_1))
|
||||
|
@ -11,7 +11,7 @@
|
||||
# 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 virtualpdu import core
|
||||
from virtualpdu.pdu.baytech_mrp27 import BaytechMRP27PDU
|
||||
|
||||
from virtualpdu.tests.integration.pdu import PDUTestCase
|
||||
@ -21,6 +21,8 @@ class TestBaytechMRP27PDU(PDUTestCase):
|
||||
pdu_class = BaytechMRP27PDU
|
||||
|
||||
def test_all_ports_are_on_by_default(self):
|
||||
self.core_mock.get_pdu_outlet_state.return_value = core.POWER_ON
|
||||
|
||||
outlet_state_oid = (1, 3, 6, 1, 4, 1) + (4779, 1, 3, 5, 3, 1, 3)
|
||||
|
||||
self.assertEqual(1, self.snmp_get(outlet_state_oid + (1, 1)))
|
||||
@ -40,10 +42,14 @@ class TestBaytechMRP27PDU(PDUTestCase):
|
||||
outlet_state_oid = (1, 3, 6, 1, 4, 1) + (4779, 1, 3, 5, 3, 1, 3)
|
||||
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.snmp_get(outlet_1))
|
||||
|
||||
self.snmp_set(outlet_1, self.pdu.outlet_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.snmp_get(outlet_1))
|
||||
|
@ -15,6 +15,7 @@
|
||||
from pyasn1.type import univ
|
||||
from pysnmp.proto.rfc1905 import NoSuchInstance
|
||||
|
||||
from virtualpdu import core
|
||||
from virtualpdu import pdu
|
||||
from virtualpdu.tests.integration.pdu import PDUTestCase
|
||||
from virtualpdu.tests.snmp_error_indications import RequestTimedOut
|
||||
@ -34,11 +35,10 @@ class TestPDU(PDUTestCase):
|
||||
self.snmp_set(enterprises + (42,), univ.Integer(7)))
|
||||
|
||||
def test_get_valid_oid_wrong_community(self):
|
||||
default_state = self.pdu.outlet_class.states.ON
|
||||
self.core_mock.get_pdu_outlet_state.return_value = core.POWER_ON
|
||||
self.pdu.oid_mapping[enterprises + (88, 1)] = \
|
||||
pdu.PDUOutlet(outlet_number=1,
|
||||
pdu=self.pdu,
|
||||
default_state=default_state)
|
||||
pdu=self.pdu)
|
||||
|
||||
self.assertEqual(self.pdu.outlet_class.states.ON,
|
||||
self.snmp_get(enterprises + (88, 1)))
|
||||
|
@ -32,7 +32,9 @@ class TestCoreIntegration(base.TestCase):
|
||||
self.core = core.Core(driver=self.driver,
|
||||
mapping={
|
||||
('my_pdu', 5): 'test'
|
||||
})
|
||||
},
|
||||
store={},
|
||||
default_state=core.POWER_OFF)
|
||||
|
||||
self.outlet_oid = apc_rackpdu.rPDU_outlet_control_outlet_command + (5,)
|
||||
|
||||
@ -73,14 +75,22 @@ class TestCoreIntegration(base.TestCase):
|
||||
self.driver.get_power_state('test'))
|
||||
|
||||
def test_initial_outlet_power_state_on(self):
|
||||
pdu = apc_rackpdu.APCRackPDU('my_pdu', self.core, core.POWER_ON)
|
||||
my_core = core.Core(driver=self.driver,
|
||||
mapping={},
|
||||
store={},
|
||||
default_state=core.POWER_ON)
|
||||
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))
|
||||
|
||||
def test_initial_outlet_power_state_off(self):
|
||||
pdu = apc_rackpdu.APCRackPDU('my_pdu', self.core, core.POWER_OFF)
|
||||
my_core = core.Core(driver=self.driver,
|
||||
mapping={},
|
||||
store={},
|
||||
default_state=core.POWER_OFF)
|
||||
pdu = apc_rackpdu.APCRackPDU('my_pdu', my_core)
|
||||
snmp_client_ = self.get_harness_client(pdu)
|
||||
|
||||
self.assertEqual(pdu.outlet_class.states.IMMEDIATE_OFF,
|
||||
|
@ -128,8 +128,10 @@ class TestEntryPointIntegration(base.TestCase):
|
||||
|
||||
def test_entrypoint_raise_on_invalid_mode(self):
|
||||
with tempfile.NamedTemporaryFile() as f:
|
||||
test_config2 = TEST_CONFIG + """outlet_default_state = invalid_mode
|
||||
"""
|
||||
test_config2 = """[global]
|
||||
libvirt_uri=test:///default
|
||||
outlet_default_state = invalid_mode
|
||||
"""
|
||||
f.write(bytearray(test_config2, encoding='utf-8'))
|
||||
f.flush()
|
||||
try:
|
||||
|
@ -21,8 +21,8 @@ class BasePDUTests(object):
|
||||
self.pdu.outlet_class.states.from_core(core.POWER_ON)
|
||||
|
||||
self.core_mock.pdu_outlet_state_changed.assert_called_with(
|
||||
name='my_pdu',
|
||||
outlet_number=1,
|
||||
pdu='my_pdu',
|
||||
outlet=1,
|
||||
state=core.POWER_ON)
|
||||
|
||||
def test_reboot_notifies_core(self):
|
||||
@ -30,8 +30,8 @@ class BasePDUTests(object):
|
||||
self.pdu.outlet_class.states.from_core(core.REBOOT)
|
||||
|
||||
self.core_mock.pdu_outlet_state_changed.assert_called_with(
|
||||
name='my_pdu',
|
||||
outlet_number=1,
|
||||
pdu='my_pdu',
|
||||
outlet=1,
|
||||
state=core.REBOOT)
|
||||
|
||||
def test_power_off_notifies_core(self):
|
||||
@ -39,6 +39,30 @@ class BasePDUTests(object):
|
||||
self.pdu.outlet_class.states.from_core(core.POWER_OFF)
|
||||
|
||||
self.core_mock.pdu_outlet_state_changed.assert_called_with(
|
||||
name='my_pdu',
|
||||
outlet_number=1,
|
||||
pdu='my_pdu',
|
||||
outlet=1,
|
||||
state=core.POWER_OFF)
|
||||
|
||||
def test_read_power_on(self):
|
||||
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
|
||||
)
|
||||
|
||||
self.core_mock.get_pdu_outlet_state.assert_called_with(
|
||||
pdu='my_pdu',
|
||||
outlet=1)
|
||||
|
||||
def test_read_power_off(self):
|
||||
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
|
||||
)
|
||||
|
||||
self.core_mock.get_pdu_outlet_state.assert_called_with(
|
||||
pdu='my_pdu',
|
||||
outlet=1)
|
||||
|
@ -27,34 +27,54 @@ class TestCore(base.TestCase):
|
||||
('my_pdu', 1): 'server_one'
|
||||
}
|
||||
|
||||
self.core = core.Core(driver=self.driver_mock, mapping=mapping)
|
||||
self.store = {}
|
||||
self.core = core.Core(driver=self.driver_mock, mapping=mapping,
|
||||
store=self.store, default_state=core.POWER_ON)
|
||||
|
||||
def test_pdu_outlet_state_changed_on_power_off(self):
|
||||
self.core.pdu_outlet_state_changed(name='my_pdu',
|
||||
outlet_number=1,
|
||||
self.core.pdu_outlet_state_changed(pdu='my_pdu',
|
||||
outlet=1,
|
||||
state=core.POWER_OFF)
|
||||
|
||||
self.driver_mock.power_off.assert_called_with('server_one')
|
||||
|
||||
def test_pdu_outlet_state_changed_machine_not_in_mapping_noop(self):
|
||||
self.core.pdu_outlet_state_changed(name='my_pdu',
|
||||
outlet_number=2,
|
||||
self.core.pdu_outlet_state_changed(pdu='my_pdu',
|
||||
outlet=2,
|
||||
state=core.POWER_OFF)
|
||||
|
||||
self.assertFalse(self.driver_mock.power_off.called)
|
||||
self.assertFalse(self.driver_mock.power_on.called)
|
||||
|
||||
def test_pdu_outlet_state_changed_on_power_on(self):
|
||||
self.core.pdu_outlet_state_changed(name='my_pdu',
|
||||
outlet_number=1,
|
||||
self.core.pdu_outlet_state_changed(pdu='my_pdu',
|
||||
outlet=1,
|
||||
state=core.POWER_ON)
|
||||
|
||||
self.driver_mock.power_on.assert_called_with('server_one')
|
||||
|
||||
def test_pdu_outlet_state_changed_on_reboot(self):
|
||||
self.core.pdu_outlet_state_changed(name='my_pdu',
|
||||
outlet_number=1,
|
||||
self.core.pdu_outlet_state_changed(pdu='my_pdu',
|
||||
outlet=1,
|
||||
state=core.REBOOT)
|
||||
|
||||
self.driver_mock.assert_has_calls([mock.call.power_off('server_one'),
|
||||
mock.call.power_on('server_one')])
|
||||
|
||||
def test_pdu_outlet_state_on_cached_state(self):
|
||||
self.store[('my_pdu', 1)] = core.POWER_OFF
|
||||
self.assertEqual(
|
||||
core.POWER_OFF,
|
||||
self.core.get_pdu_outlet_state(pdu='my_pdu', outlet=1))
|
||||
|
||||
def test_pdu_outlet_state_on_connected_device(self):
|
||||
self.driver_mock.get_power_state.return_value = core.POWER_OFF
|
||||
self.assertEqual(
|
||||
core.POWER_OFF,
|
||||
self.core.get_pdu_outlet_state(pdu='my_pdu', outlet=1))
|
||||
self.driver_mock.get_power_state.assert_called_with('server_one')
|
||||
|
||||
def test_pdu_outlet_state_on_disconnected_outlet(self):
|
||||
self.assertEqual(
|
||||
core.POWER_ON,
|
||||
self.core.get_pdu_outlet_state(pdu='my_pdu', outlet=2))
|
||||
|
Loading…
x
Reference in New Issue
Block a user