"""
Fault Inject
~~~~~~~~~~~~
A module that enables easy injection of faults into a JSON link exchange, useful
for both our own unittests and when testing other implementations
Classes:
* :class:`FaultInjectMixIn`
* :class:`MasterFaultInject`
* :class:`SlaveFaultInject`
Related Functions:
Notes:
@author: Bryan Palmintier, NREL 2013
"""
# __future__ imports must occur at top of file. They enable using features from
# python 3.0 in older (2.x) versions are are ignored in newer (3.x) versions
# New print function
from __future__ import print_function
# Use Python 3.0 import semantics
from __future__ import absolute_import
__author__ = "Bryan Palmintier"
__copyright__ = "Copyright (c) 2013 Bryan Palmintier"
__license__ = """
@copyright: Copyright (c) 2013, Bryan Palmintier
@license: BSD 3-clause --
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1) Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2) Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3) Neither the name of the National Renewable Energy Lab nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."""
## ===== History ====
# [Current] version date time who Comments
# ------- ---------- ----- ---------- -------------
__version__, __date__ = "0.1.0a","2013-05-01" #21:40 BryanP Initial Version
# [Older, in reverse order]
# version date time who Comments
# ------- ---------- ----- ---------- ---------------------------------------
#Standard Library imports
import sys
import json
#local imports
from .. import json_link
#===============================================================================
# Module level constants
#===============================================================================
#===============================================================================
# Error (Exception) Class definitions
#===============================================================================
#===============================================================================
# FaultInjectMixIn (module internal)
#===============================================================================
[docs]class FaultInjectMixIn(json_link._BaseLink):
""" Mix in class to enable JSON fault injection by overloading message sending functions"""
fault = {}
# Create a dictionary with send counts for each message type initialized to zero
send_count = dict(zip(json_link.MsgTypeList, [0] * len(json_link.MsgTypeList)))
[docs] def sendMsg(self, action, data, data_name='data'):
"""
Injects a fault into either the JSON link or raw exchange message
by using the fault data field instead of the proper message
"""
# Yes, this nested if could be replaced by assigning inject_fault to a
# long boolean expression, BUT we keep the if structure for future
# expansion
if action in self.fault:
#Enable shorthand for simple faults
if type(self.fault[action]) is not dict \
or 'data' not in self.fault[action]:
self.fault[action]={'data':self.fault[action]}
#by default, we expect to inject a fault
inject_fault = True
#But if the delay parameter is set, we only inject the fault for
#listed number attempts
if ('delay' in self.fault[action]) and (
self.send_count[action] not in self.fault[action]['delay']):
inject_fault = False
else:
inject_fault = False
if inject_fault:
if 'raw' in self.fault[action] and self.fault[action]['raw']:
output = json_link._BaseLink.sendMsg(self, action,
self.fault[action]['data'],
data_name, raw_packet=True)
else:
output = json_link._BaseLink.sendMsg(self, action,
self.fault[action]['data'],
data_name, raw_json=True)
else:
output = json_link._BaseLink.sendMsg(self, action, data, data_name)
self.send_count[action] += 1
return output
#===============================================================================
# MasterFaultInject
#===============================================================================
[docs]class MasterFaultInject(json_link.MasterLink, FaultInjectMixIn):
""" JSON Link Master/client with fault injection"""
description_dict = {'application':"JSON_Link_Master_with_Fault_Inject",
'version':__version__,
'modelname':sys.argv[0]
}
#===============================================================================
# SlaveLink
#===============================================================================
[docs]class SlaveFaultInject(json_link.SlaveLink, FaultInjectMixIn):
""" JSON Link Slave/server with fault injection"""
#Passed via the init link message, must include 'remote'
description_dict = {'remote':"JSON Link Slave/Server with Fault Inject", 'version':__version__}