- Fix amf_demo to make it work.
- Modify README for the latest amf_demo.
---
python/samples/README | 35 ++--
python/samples/amf_demo | 436 +++++++++++++++++++++++++++++++-----------------
2 files changed, 303 insertions(+), 168 deletions(-)
diff --git a/python/samples/README b/python/samples/README
index 84aa99c..1ba472a 100644
--- a/python/samples/README
+++ b/python/samples/README
@@ -1,28 +1,29 @@
Sample code in this directory:
amf_demo:
- Drop-in replacement for C-compiled version; written in python.
- You can use the same clc-cli script as is used for the C-version:
- opensaf-staging/samples/avsv/amf_demo_script
+ Drop-in replacement for C-compiled version; written in python.
+ You can use the same clc-cli script and model as are used for the
C-version:
+ opensaf-code/samples/amf/sa_aware/amf_demo_script
+ opensaf-code/samples/amf/sa_aware/AppConfig-2N.xml
immadm:
- CLI utility that simulates the C-version of the utility.
- Run 'immadm --help' for specific options and arguments.
+ CLI utility that simulates the C-version of the utility.
+ Run 'immadm --help' for specific options and arguments.
immlist:
- CLI utility that simulates the C-version of the utility.
- Run 'immlist --help' for specific options and arguments.
+ CLI utility that simulates the C-version of the utility.
+ Run 'immlist --help' for specific options and arguments.
immbase.py:
- Module required by immadm and immlist; contains logging interface and
- other services.
+ Module required by immadm and immlist; contains logging interface and
+ other services.
clm-tool:
CLI utility that can be used that demonstrates how to fetch a list of
- current members of the cluster and how to monitor changes to the
+ current members of the cluster and how to monitor changes to the
cluster. Run 'clm-tool --help' for specific options and arguments.
-imm-listener:
+imm-listener:
The Imm Lister OI demonstrates how to build an
applier. It listens to changes to SampleClass1 and simply
prints them on stdout. It's an applier which means that it
@@ -50,7 +51,7 @@ time-reporter:
from an OI and inclusion of the OI logic into a select loop
owned by the application. It creates a runtime object timeId=1
of the class TimeSample and updates its hours, minutes and
- seconds attributes once every second.
+ seconds attributes once every second.
Run 'time-reporter --help' for specific options and arguments
ping-pong:
@@ -80,13 +81,13 @@ ntfsend:
'ntfsend --help' for specific options and arguments.
ntfsubscribe:
- The ntfsubscribe sample application is a copy of the
- ntfsubscribe tool, implemented in Python. It listens for
- notifications and logs received notifications in the
- terminal. Run 'ntfsubscribe --help'.
+ The ntfsubscribe sample application is a copy of the
+ ntfsubscribe tool, implemented in Python. It listens for
+ notifications and logs received notifications in the
+ terminal. Run 'ntfsubscribe --help'.
Logging for all apps/utils goes to /var/log/opensaf/saflog/saLogSystem*.log
-The IMM OI samples use the classes defined in the classes.xml file. Load it
with immcfg -f classes.xml before trying them.
+The IMM OI samples use the classes defined in the classes.xml file. Load it
with immcfg -f classes.xml before trying them.
Each sample OI is implemented in two versions, one using straight callbacks
and one using a class that subclasses Implementer or Applier. The latter is
named <oi-name>-inheritance-impl
diff --git a/python/samples/amf_demo b/python/samples/amf_demo
index edeeff0..60b37fe 100644
--- a/python/samples/amf_demo
+++ b/python/samples/amf_demo
@@ -2,6 +2,7 @@
############################################################################
#
# (C) Copyright 2011 The OpenSAF Foundation
+# (C) Copyright 2017 Ericsson AB. All rights reserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
@@ -13,160 +14,293 @@
# licensing terms.
#
# Author(s): Wind River Systems, Inc.
+# Ericsson
#
############################################################################
-
-from pyosaf.utils import log
-from pyosaf.utils.log.logger import SafLogger
-from pyosaf.saAmf import *
-from pyosaf.saLog import *
-import select
-import sys
+# pylint: disable=unused-argument
+"""
+This is a small demo which illustrates how to use AMF Python interfaces.
+"""
import os
+import sys
+import errno
+import signal
+import socket
+import hashlib
+from select import select, error
+
+from pyosaf.utils import log_info, log_err
+from pyosaf.saAis import eSaAisErrorT, eSaDispatchFlagsT, SaNameT, SaVersionT
+from pyosaf import saAmf
+
+SA_AMF_CSI_ADD_ONE = saAmf.saAmf.SA_AMF_CSI_ADD_ONE
+SA_AMF_CSI_TARGET_ONE = saAmf.saAmf.SA_AMF_CSI_TARGET_ONE
+SA_AMF_CSI_TARGET_ALL = saAmf.saAmf.SA_AMF_CSI_TARGET_ALL
+
+
+class AmfDemo(object):
+ """ This class encapsulates functions provided by AMF. """
+ ha_state_map = {1: 'ACTIVE',
+ 2: 'STANDBY',
+ 3: 'QUIESCED',
+ 4: 'QUIESCING'}
+
+ def __init__(self, health_check_key, signal_handler):
+ self.handle = saAmf.SaAmfHandleT()
+ self.version = SaVersionT('B', 4, 1)
+ self.callbacks = saAmf.SaAmfCallbacksT_4()
+ self.sel_obj = saAmf.SaSelectionObjectT()
+ self.comp_name = SaNameT()
+ self.health_check_key = saAmf.SaAmfHealthcheckKeyT(health_check_key)
+ self.healthcheck_count = 1
+ self.term_handler = signal_handler
+
+ def initialize(self):
+ """ Initialize with AMF service and register as AMF component. """
+ self.callbacks.saAmfHealthcheckCallback = \
+ saAmf.SaAmfHealthcheckCallbackT(self.health_check_callback)
+ self.callbacks.saAmfComponentTerminateCallback = \
+ saAmf.SaAmfComponentTerminateCallbackT(
+ self.component_terminate_callback)
+ self.callbacks.saAmfCSISetCallback = \
+ saAmf.SaAmfCSISetCallbackT(self.csi_set_callback)
+ self.callbacks.saAmfCSIRemoveCallback = \
+ saAmf.SaAmfCSIRemoveCallbackT(self.csi_remove_callback)
+
+ log_info("Start initialization.")
+
+ rc = saAmf.saAmfInitialize_4(self.handle, self.callbacks, self.version)
+ if not self.check_rc("saAmfInitialize", rc):
+ return False
+
+ rc = saAmf.saAmfSelectionObjectGet(self.handle, self.sel_obj)
+ if not self.check_rc("saAmfSelectionObjectGet", rc):
+ return False
+
+ rc = saAmf.saAmfComponentNameGet(self.handle, self.comp_name)
+ if not self.check_rc("saAmfComponentNameGet", rc):
+ return False
+
+ rc = saAmf.saAmfComponentRegister(self.handle, self.comp_name, None)
+ if not self.check_rc("saAmfComponentRegister", rc):
+ return False
+
+ invocation_type = saAmf.eSaAmfHealthcheckInvocationT.\
+ SA_AMF_HEALTHCHECK_AMF_INVOKED
+ recovery_type = saAmf.eSaAmfRecommendedRecoveryT.\
+ SA_AMF_COMPONENT_FAILOVER
+ rc = saAmf.saAmfHealthcheckStart(self.handle, self.comp_name,
+ self.health_check_key,
+ invocation_type,
+ recovery_type)
+ if not self.check_rc("saAmfHealthcheckStart", rc):
+ return False
+
+ log_info("Initialization finished successfully.")
+ return True
+
+ def finalize(self):
+ """ Finalize amf handle """
+ rc = saAmf.saAmfFinalize(self.handle)
+ return self.check_rc("saAmfFinalize", rc)
+
+ def dispatch(self):
+ """ Dispatch message to appropriate callback """
+ rc = saAmf.saAmfDispatch(self.handle,
+ eSaDispatchFlagsT.SA_DISPATCH_ALL)
+ return self.check_rc("saAmfDispatch", rc)
+
+ def health_check_callback(self, invocation, comp_name, health_check_key):
+ """ Callback to handle health-check event.
+
+ Args:
+ invocation (SaInvocationT): A particular invocation of the
+ callback function.
+ comp_name (POINTER(SaNameT)): A pointer to the name of the
+ component that must undergo the particular healthcheck.
+ health_check_key (POINTER(SaAmfHealthcheckKeyT)): A pointer to the
+ key of the healthcheck to be executed.
+ """
+ log_info("Health check callback is invoked [%s]" %
+ self.healthcheck_count)
+ rc = saAmf.saAmfResponse_4(self.handle, invocation, None,
+ eSaAisErrorT.SA_AIS_OK)
+ self.check_rc("saAmfResponse_4", rc)
+ self.healthcheck_count += 1
+
+ def csi_set_callback(self, invocation, comp_name, ha_state, csi_desc):
+ """ Callback to handle CSI set event.
+
+ Args:
+ invocation (SaInvocationT): A particular invocation of the
+ callback function.
+ comp_name (POINTER(SaNameT)): A pointer to the name of the
+ component which CSI assigns to.
+ ha_state (SaAmfHAStateT): The new HA state.
+ csi_desc (SaAmfCSIDescriptorT): Information about the
+ CSI targeted by this callback invocation.
+ """
+ log_info("CSI set callback is invoked")
+ next_ha_state = AmfDemo.ha_state_map[ha_state]
+ csi_name = csi_desc.csiName
+ csi_flags = csi_desc.csiFlags
+
+ if csi_flags in [SA_AMF_CSI_ADD_ONE, SA_AMF_CSI_TARGET_ONE]:
+ log_info("%s got %s assignment for %s" %
+ (str(comp_name[0]), next_ha_state, str(csi_name)))
+ elif csi_flags == SA_AMF_CSI_TARGET_ALL:
+ log_info("%s got %s assignment for all CSI" %
+ (str(comp_name[0]), next_ha_state))
+
+ if ha_state == saAmf.eSaAmfHAStateT.SA_AMF_HA_QUIESCING:
+ rc = saAmf.saAmfCSIQuiescingComplete(self.handle, invocation,
+ eSaAisErrorT.SA_AIS_OK)
+ self.check_rc("saAmfCSIQuiescingComplete", rc)
+
+ rc = saAmf.saAmfResponse_4(self.handle, invocation, None,
+ eSaAisErrorT.SA_AIS_OK)
+ self.check_rc("saAmfResponse_4", rc)
+
+ def csi_remove_callback(self, invocation, comp_name, csi_name, csi_flags):
+ """ Callback to handle CSI remove event.
+
+ Args:
+ invocation (SaInvocationT): A particular invocation of the
+ callback function.
+ comp_name (POINTER(SaNameT)): A pointer to the name of the
+ component which CSI will be remove.
+ csi_name (POINTER(SaNameT)): A pointer to the name of the CSI
+ which must be removed from component.
+ csi_flags (SaAmfCSIFlagsT): The flag specifies whether one or more
+ CSIs are affected.
+ """
+ log_info("CSI remove callback is invoked")
+ if csi_flags == SA_AMF_CSI_TARGET_ONE:
+ log_info("Removing assignment of %s from %s" %
+ (str(csi_name[0]), str(comp_name[0])))
+ elif csi_flags == SA_AMF_CSI_TARGET_ALL:
+ log_info("Removing ALL assignments from %s" % str(comp_name[0]))
+
+ rc = saAmf.saAmfResponse_4(self.handle, invocation, None,
+ eSaAisErrorT.SA_AIS_OK)
+ self.check_rc("saAmfResponse_4", rc)
+
+ def component_terminate_callback(self, invocation, comp_name):
+ """ Callback to handle component terminate event.
+
+ Args:
+ invocation (SaInvocationT): A particular invocation of the
+ callback function.
+ comp_name (POINTER(SaNameT)): A pointer to the name of the
+ component to be terminated.
+ """
+ log_info("Component terminate callback is invoked")
+ log_info("Component %s is terminating" % str(comp_name[0]))
+ rc = saAmf.saAmfResponse_4(self.handle, invocation, None,
+ eSaAisErrorT.SA_AIS_OK)
+ self.check_rc("saAmfResponse_4", rc)
+ self.term_handler.send_terminate_msg()
+
+ @staticmethod
+ def check_rc(function_name, rc):
+ """ Check return code if it ok or not.
+
+ Args:
+ function_name (str): The name of the function.
+ rc (eSaAisErrorT): Return code which is checked.
+
+ Returns:
+ bool: True if rc is SA_AIS_OK. Otherwise, it is False.
+ """
+ if rc != eSaAisErrorT.SA_AIS_OK:
+ err_str = eSaAisErrorT.whatis(rc)
+ log_err("%s FAILED, rc: %s" % (function_name, err_str))
+ return False
+
+ return True
+
+
+class SignalHandler(object):
+ """ Util class for signal handling """
+ def __init__(self):
+ self.term_socket = socket.socketpair()
+ self.write_fd = self.term_socket[0].fileno()
+ self.listen_fd = self.term_socket[1].fileno()
+ signal.signal(signal.SIGTERM, self.handle_signal)
+ signal.signal(signal.SIGINT, self.handle_signal)
+
+ def handle_signal(self, signum, frame):
+ """ Handle terminate or interrupt signal """
+ if signum == signal.SIGINT:
+ log_info("SIGINT is caught!")
+ elif signum == signal.SIGTERM:
+ log_info("SIGTERM is caught!")
+ self.send_terminate_msg()
+
+ def send_terminate_msg(self):
+ os.write(self.write_fd, "Terminate request")
+
+
+def create_pid_file(pid, comp_name):
+ """ Create pid file which is handled by amf_demo_script.
+
+ Args:
+ pid (int): PID of the child process.
+ comp_name(str): Component name which is specified by
+ SA_AMF_COMPONENT_NAME environment variable.
+ """
+ md5 = hashlib.md5(comp_name + '\n').hexdigest()
+ pid_file_path = "/tmp/%s.pid" % md5
+ with open(pid_file_path, 'w') as md5_file:
+ md5_file.write(str(pid))
+
+
+def create_daemon(comp_name):
+ """ Create a child process and detach it from main process.
+
+ Args:
+ comp_name (str): Component name which is specified by
+ SA_AMF_COMPONENT_NAME environment variable.
+ """
+ pid = os.fork()
+ if pid != 0:
+ create_pid_file(pid, comp_name)
+ sys.exit(0)
-exec_name = os.path.basename(__file__)
-
-def chexit(funcName, rc):
- if rc != eSaAisErrorT.SA_AIS_OK:
- errstr = '%s FAILED (%s)!' % (funcName, eSaAisErrorT.whatis(rc))
- raise ValueError(errstr)
-
-class AmfIface(object):
- @staticmethod
- def log(logstr):
- if AmfIface.logger:
- AmfIface.logger.log(logstr)
- else:
- print logstr
-
- @staticmethod
- def healthcheckCallback(invocation, compName, healthcheckKey):
- saAmfResponse(AmfIface.amfHandle,
- invocation, eSaAisErrorT.SA_AIS_OK)
-
- @staticmethod
- def componentTerminateCallback(invocation, compName):
- AmfIface.log('%s: componentTerminateCallback' %
- compName[0].value)
- saAmfResponse(AmfIface.amfHandle,
- invocation, eSaAisErrorT.SA_AIS_OK)
- AmfIface.isRunning = False
-
- @staticmethod
- def csiSetCallback(invocation, compName, haState, csiDescriptor):
- AmfIface.log('%s: csiSetCallback: %s: %s' %
- (compName[0].value,
- csiDescriptor.csiName.value,
- eSaAmfHAStateT.whatis(haState)))
- saAmfResponse(AmfIface.amfHandle,
- invocation, eSaAisErrorT.SA_AIS_OK)
- if haState == eSaAmfHAStateT.SA_AMF_HA_QUIESCING:
- saAmfCSIQuiescingComplete(AmfIface.amfHandle,
- invocation, eSaAisErrorT.SA_AIS_OK)
-
- @staticmethod
- def csiRemoveCallback(invocation, compName, csiName, csi_flags):
- AmfIface.log('%s: csiRemoveCallback: %s' %
- (compName[0].value, csiName[0].value))
- saAmfResponse(AmfIface.amfHandle,
- invocation, eSaAisErrorT.SA_AIS_OK)
-
- @staticmethod
- def protectionGroupTrackCallback(csiName, buffer, memberCount, error):
- pass
-
- @staticmethod
- def proxiedComponentInstantiateCallback(invocation, proxiedCompName):
- pass
-
- @staticmethod
- def proxiedComponentCleanupCallback(invocation, proxiedCompName):
- pass
-
- @staticmethod
- def initialize(logger):
- AmfIface.logger = logger
- AmfIface.isRunning = False
- AmfIface.amfHandle = SaAmfHandleT()
- AmfIface.callbacks = SaAmfCallbacksT()
- AmfIface.fd = SaSelectionObjectT()
- AmfIface.compName = SaNameT()
-
- AmfIface.callbacks.saAmfHealthcheckCallback = \
- SaAmfHealthcheckCallbackT(
- AmfIface.healthcheckCallback)
- AmfIface.callbacks.saAmfComponentTerminateCallback = \
- SaAmfComponentTerminateCallbackT(
- AmfIface.componentTerminateCallback)
- AmfIface.callbacks.saAmfCSISetCallback = \
- SaAmfCSISetCallbackT(
- AmfIface.csiSetCallback)
- AmfIface.callbacks.saAmfCSIRemoveCallback = \
- SaAmfCSIRemoveCallbackT(
- AmfIface.csiRemoveCallback)
- AmfIface.callbacks.saAmfProtectionGroupTrackCallback = \
- SaAmfProtectionGroupTrackCallbackT(
- AmfIface.protectionGroupTrackCallback)
- AmfIface.callbacks.saAmfProxiedComponentInstantiateCallback = \
- SaAmfProxiedComponentInstantiateCallbackT(
- AmfIface.proxiedComponentInstantiateCallback)
- AmfIface.callbacks.saAmfProxiedComponentCleanupCallback = \
- SaAmfProxiedComponentCleanupCallbackT(
- AmfIface.proxiedComponentCleanupCallback)
-
- amf_version = SaVersionT('B', 1, 1)
- chexit('saAmfInitialize',
- saAmfInitialize(AmfIface.amfHandle, AmfIface.callbacks,
- amf_version))
- chexit('saAmfSelectionObjectGet',
- saAmfSelectionObjectGet(AmfIface.amfHandle,
- AmfIface.fd))
- chexit('saAmfComponentNameGet',
- saAmfComponentNameGet(AmfIface.amfHandle,
- AmfIface.compName))
- chexit('saAmfComponentRegister',
- saAmfComponentRegister(AmfIface.amfHandle,
- AmfIface.compName, None))
-
- AmfIface.log('%s: REGISTERED.' % AmfIface.compName.value)
- AmfIface.isRunning = True
-
- healthcheckKey = SaAmfHealthcheckKeyT('AmfDemo')
- it = eSaAmfHealthcheckInvocationT.SA_AMF_HEALTHCHECK_AMF_INVOKED
- rt = eSaAmfRecommendedRecoveryT.SA_AMF_COMPONENT_FAILOVER
-
- chexit('saAmfHealthcheckStart',
- saAmfHealthcheckStart(AmfIface.amfHandle,
- AmfIface.compName, healthcheckKey, it, rt))
-
- AmfIface.log('%s: STARTED HEALTHCHECK %s' %
- (AmfIface.compName.value, healthcheckKey.key))
-
- @staticmethod
- def finalize():
- saAmfComponentUnregister(AmfIface.amfHandle,
- AmfIface.compName, None)
- saAmfFinalize(AmfIface.amfHandle)
-
- @staticmethod
- def dispatch(readfds):
- if AmfIface.fd.value in readfds:
- chexit('saAmfDispatch',
- saAmfDispatch(AmfIface.amfHandle,
- eSaDispatchFlagsT.SA_DISPATCH_ALL))
-
-def main():
- logger = SafLogger()
- AmfIface.initialize(logger)
-
- readfds = [logger.fd.value, AmfIface.fd.value]
- while AmfIface.isRunning:
- input, output, excepts = select.select(readfds, [], [])
- logger.dispatch(input)
- AmfIface.dispatch(input)
-
- AmfIface.finalize()
- logger.finalize()
if __name__ == '__main__':
- main()
+ env_comp_name = os.getenv("SA_AMF_COMPONENT_NAME")
+ if env_comp_name is None:
+ log_err("This process is not started by AMF. Exiting ...")
+ sys.exit(1)
+
+ create_daemon(env_comp_name)
+ terminate_handler = SignalHandler()
+
+ amf_demo = AmfDemo("AmfDemo", terminate_handler)
+ result = amf_demo.initialize()
+ if not result:
+ sys.exit(1)
+
+ fds = [amf_demo.sel_obj.value, amf_demo.term_handler.listen_fd]
+
+ stop_flag = False
+ while not stop_flag:
+ try:
+ ioe = select(fds, [], [])
+ except error as err:
+ if err[0] != errno.EINTR:
+ log_err("Something went wrong: %r" % err)
+ result = False
+ continue
+
+ for fd in ioe[0]:
+ if fd == amf_demo.term_handler.listen_fd:
+ stop_flag = True
+ elif fd == amf_demo.sel_obj.value:
+ result = amf_demo.dispatch()
+ if not result:
+ stop_flag = True
+
+ amf_demo.finalize()
+ sys.exit(0 if result else 1)
--
2.7.4
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Opensaf-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/opensaf-devel