Infrastructure of OpenClovis SAFplus
This chapter covers the following topics in detail:
Key Topics:
- Components of Infrastructure Layer
- Componentization
- Execution Entities
- SAFplus Platform Distributed Infrastructure
- Componentization Process
- Component Characteristics
- Component Initialization
- Using Availability Management Framework (AMF)
- Characteristics of a Sample Application
- Sample Code for Componentization
Components of Infrastructure Layer
OpenClovis Infrastructure Layer consists of libraries required for the system. It comprises the following components:
- Operating System Abstraction Layer (OSAL)
- Hardware Abstraction Layer (HAL)
- Hardware Platform Interface (HPI)
- Database Abstraction Layer (DBAL)
- Execution Object (EO)
- Log Service
- Transaction Manager (TM)
- Basic Infrastructure Core
- Interface Definition Language (IDL)
Operating System Abstraction Layer (OSAL)
The OpenClovis Operating System Abstraction Layer (OSAL) provides a standard interface to commonly used operating system functions. OSAL supports target operating systems like most variations of Carrier Grade Linux.
Features
- Easily adaptable to any proprietary target operating system.
How it works
All OpenClovis SAFplus Platform components are developed based on OSAL that provide an OS agnostic function to all system calls, such as memory management and thread management functions. Internally, OSAL maps such functions to the respective equivalent system calls provided by the underlying OS. This allows OpenClovis SAFplus Platform as well as all OpenClovis SAFplus-based applications to be ported to new operating systems with no modifications.
OpenClovis SAFplus Platform is delivered with a Posix-compliant adaptation module that allows it to operate to any Posix-compliant system, such as Linux and most UNIX systems.
Software components use the signal handlers provided by OSAL to handle critical UNIX signals. This can automatically inform OpenClovis Component Manager (CPM) about the signal and generate a fault, or trigger necessary recovery actions. OSAL is currently designed as a standalone library with no dependencies on any other OpenClovis SAFplus Platform components.
Hardware Platform Interface (HPI)
The OpenClovis Hardware Platform Interface (HPI) provides a programmatic interface to manage and monitor a system.
Features
- It manages only the lifecycle of the hardware platform and not the application for which the specific hardware is being used.
- In a OpenClovis SAFplus-enabled system, HPI is used for platform management and HAL is used for modeling and managing the application specifics of the hardware.
How it Works
HPI also allows the platform specific aspects of a blade to be discovered at run time without explicitly modeling it during the design process.
Database Abstraction Layer (DBAL)
The OpenClovis Database Abstraction Layer (DBAL) provides a standard interface for any OpenClovis SAFplus Platform infrastructure component or application to interface with the commonly used relational database.
Features
- DBAL currently supports only the GNU Database Manager (GDBM).
How it Works
The primary user of this interface is the COR component which can dump or read its object repository to and from a database for either persistent storage or offline processing. DBAL is also a standalone library with no dependencies on any other OpenClovis SAFplus Platform components.
Execution Object (EO)
The OpenClovis Execution Object (EO) encapsulates each distinct SAFplus-aware software component and provides an execution environment for the components.
Features
- Provides a uniform interface between the software component and the rest of the system components.
- The interfaces fall into the following two categories:
- Management Interface: This interface is used to control and configure the software components.
- Service Interface: This interface allows software components to expose component specific functionality.
How it Works
Both management and service interfaces are exposed using RMD APIs. EO provides threads for receiving RMD messages and worker threads to process them. The components encapsulated by EO communicate to other such components using Communication Core components such as Event Manager (EM), Remote Method Dispatch (RMD), Intelligent Object Communication (IOC), and Name Service.
Log Service
The OpenClovis Log Service collects, translates, and publishes log messages to record any significant event in the system. For instance, operational state change of a component, managed object attribute value change, and so on.
The log service supports syslog and logging facility.
Features
- Log analyzes the proper system behavior and registers the potential unintended operations.
- Log-levels can be changed during the operation.
Transaction Manager (TM)
The OpenClovis Transaction Manager (TM) provides an infrastructure library for resource managers to use transaction semantics to manage distributed data. Any component that wishes to be a part of transactions can link with this library and act as a resource manager.
Features
- Supports re-startable transactions. For instance, if any transaction participant fails, data recovery mechanism helps to recover the application state using the transaction logs for enhanced availability.
How it works
It automatically tracks participants and provides ACID semantics to ensure that all participants are updated properly, or will rollback to previous state assuring data integrity despite component failures.
Basic Infrastructure Core
The Basic Infrastructure Core (BIC) comprises libraries and functions required for the system. It provides essential basic services to all the OpenClovis software, utilities, and other customized applications.BIC includes the following software libraries (APIs):
- Buffer Manager Library
- Heap Memory
- Timer Library
- Containers
- Circular List
- Queue Library
- Rule-Based Engine (RBE)
Buffer Manager Library
The OpenClovis Buffer Manager Library is designed to provide an efficient method of user-space buffer and memory management to increase the performance of communication-intensive OpenClovis SAFplus Platform components and user applications. It contains elastic buffers that expand based on application memory requirement.
Heap Memory
Heap memory is used for dynamic memory allocation. Memory is allocated from a large pool of unused memory area called the heap. The size of the memory allocation can be determined at run-time.
Timer Library
The OpenClovis Timer Library enables you to create multiple timers to execute application specific functionality after certain intervals. It performs the following functions:
- Creates multiple timers.
- Specifies a time-out value for each timer.
- Creates one-shot or repetitive timers.
- Specifies an application specific function that should be executed every time the timer fires or expires.
The operating systems such as Linux or BSD supports limited number of timers that can be created in an application. However, an application such as OSPF and BGP requires a lot of timers at various points during its execution of the application.
Containers
The OpenClovis Container Library provides basic data management facilities by means of container abstraction. It provides a common interface for all Container types.
It supports three types of containers listed as follows:
- Doubly linked list
- Hashtable (supports open-hashing)
- Red-Black trees (balanced binary tree)
Circular List
The OpenClovis Circular List provides implementation of circular linked list and supports addition, deletion, and retrieval of node and walks through the list.
Queue Library
The OpenClovis Queue Library provides implementation for an ordered list. It supports enqueuing, dequeuing, and retrieval of a node and walk through the queue.
Rule-Based Engine (RBE)
The OpenClovis Rule-Based Engine (RBE) provides a mechanism to create rules to be applied to the system instance data, based on simple expressions.
An expression consists of a mask and a value. These expressions are evaluated on user data and generate a Boolean value for the decision process.
For instance, RBE is used by the Event Service to support filter-based subscriptions. The event is published with a pattern that is matched against the filter provided by the subscribers. Only those subscribers that match successfully are notified. The RBE library provides simple bit-based matching based on the flags specified.
Interface Definition Language (IDL)
The OpenClovis Interface Definition Language (IDL) is a library used by all EOs to communicate efficiently across nodes. Using IDL, OpenClovis SAFplus Platform services can communicates across endian machines and mixed mode (32-bit and 64-bit architecture). IDL is a wrapper over RMD and supports RMD functionality to provide communication across EOs.
Componentization
Componentization is a mechanism that enables the user application to communicate with OpenClovis SAFplus Platform. These applications can be componentized and managed for administering the lifecycle of a component, for using the manageability features of OpenClovis SAFplus Platform, or for attaining high availability.
Significance of Componentization
The two most common software entities within the execution context are the processes and the threads. In most systems, processes are isolated from each other, whereas failure in a thread causes other threads to be affected also.
In UNIX, the interaction between the OS and a process is passive. The OS starts up the process, sets up its environment and executes its main function. This enables the process to execute without any direct interference from the OS. It can either enter into an infinite loop or choose to intermittently request for services from the OS. Since the OS is unaware of the process' internals, it reacts only when it receives service requests from the process. The mechanism for proactively sending information to the process is using signals.
However, this is not the only model for OS to process communication and has certain limitations:
- If the OS wants full control over the complete lifecycle of a process - as found in a carrier grade system.
- Higher level of service availability cannot be achieved without considering the application-specific constraints. The OS and application need to co-operate with the lifecycle of the application.
From a functional perspective, the OS needs the ability to invoke multiple well-defined functions within a process. It must provide a generic and high bandwidth scheme for bi-directional OS to application communication.
The OS to application communication can be done with either a synchronous remote function call paradigm, which creates a tight binding between the OS and the process, or a de-linked, asynchronous message based model. The asynchronous, message based model can be more efficient and deadlock safe than the function call model. However, it can also create queues and code organization issues when used for high latency functions.
Significantly, in a system with either of these approaches, a process' internal organization changes from having a linear, synchronous flow to an asynchronous, event driven flow. It is the difference between writing a console application and a windowing application that must respond to asynchronous events.
Features of Componentization
Componentization provides the following features:
- Facilitates Communication
- Statistics gathering and profiling
- Resource Management
- Debugging
Execution Entities
An execution entity is a form of a process, a task or a thread that contains a set of data and an execution context. The execution entities running in the system without external communication is not very effective in the system, unless it is capable of interacting with other execution entities. As the execution entities are distributed in nature, they need a common mechanism to facilitate the communication between them.
Constituents of Execution Entity
The execution entity mainly consists of:
- Attributes and methods to manage the entity.
- A mechanism to communicate with external execution entities.
- Implementation of the entity specific to its responsibilities.
OpenClovis Inc. provides the first two functionalities encapsulated within the Execution Object (EO). The Execution Object comprises all the common attributes and methods used to represent the execution entity in the system. The communication with the external entities is provided by creating a background thread that receives the messages on behalf of the execution entity. These messages are decoded and the corresponding methods within the execution entity are invoked.
Execution Object (EO)
OpenClovis Execution Object (EO) encapsulates each distinct SAFplus-aware software component and provides an execution environment for the components. It provides a uniform interface between the software component and the rest of the system components.
The interfaces fall into the following two categories:
- Management Interface: This interface is used to control and configure the software components.
- Service Interface: This interface allows software components to expose component specific functionalities.
With the help of Componentization, both management and service interfaces are exposed using RMD APIs. EO provides receiver threads for picking up messages from RMD and worker threads for processing them. It also provides an execution environment for the software component and allows it to be managed by the Component Manager.
The components encapsulated by EO communicate with other components using OpenClovis Communication Core components such as Event Manager (EM), Remote Method Dispatch (RMD), Intelligent Object Communication (IOC), and Name Service.
EO Infrastructure
The desired process model for a carrier grade system is the asynchronous event driven model. This can enabled in two ways:
- Write a new OS that natively supports this model.
- Design a low-level middleware that enables process manageability and provides a hook for delivering services to a process in a standard manner but without modification to the base OS.
OpenClovis EO infrastructure is the user space client library and the server infrastructure that enables these capabilities. When this infrastructure is linked into an application, it takes over the application's main function and creates communication links back to the middleware, executing application specific state changes at the behest of the middleware. This model requires some help from the application, which must be written to support multiple threads as well as an asynchronous flow.
Characteristics of an EO
You can define a "Managed EO" that comprises the following characteristics.
- An EO is a process that is modified to interface with the middleware such as having multiple threads.
- An EO can be manageable by the middleware.
- An EO can use one or more services provided by the middleware.
The EO infrastructure thus consists of an EO client library linked into each process and an EO manager library to manage the client. The client library implements the dispatching loop and manages communication between the application and OpenClovis SAFplus Platform services. Other services link to their client library functions into the EO infrastructure.
Both high level SAFplus Platform and application processes can be Componentized. The resulting applications can be "managed" or "unmanaged" depending on whether the manageability functions in their respective EOs are implemented or kept empty. Although, components like CPM cannot be Componentized as the EO infrastructure is dependent on it. Though many applications can interface with SAFplus Platform using the EO infrastructure, there can be some exceptions where the application may use a light weight library that does not link to the EO infrastructure.
SAFplus Platform Distributed Infrastructure
The distributed infrastructure of SAFplus Platform allows different components to communicate with each other agnostic of the computing unit. This inter-component communication is achieved through IOC Linux kernel module. RMD uses services provided by IOC to invoke remote procedure of other components.
The fundamental constituents of OpenClovis SAFplus Platform distributed infrastructure to realize a component comprises:
- IOC Port - It is a communication port where all the incoming messages are queued. Each component must have an IOC port to communicate with other components and services.
- Message Receiver Thread - The Message Receiver Thread picks up the messages from the IOC Port and queues it into EO Job Queue to further process the message.
- Worker Threads - When a message is queued into EO Job Queue the worker threads get invoked. One of these threads picks up the message and calls the corresponding function for this message. After the message is processed and the function is executed, the reply is returned depending on the options specified by you indicating whether it is a synchronous RMD call or an asynchronous RMD call.
- Client Tables and Function Tables - Each OpenClovis SAFplus Platform client and application client that the EO links can install the client tables by invoking the
clEoClientInstall
API. A client Id is used to index into the client table. The client table consists of the services exposed by the client in the form of a Function Table. These clients provide various services that are installed and registered in the Function Tables indexed by the service Id. The service Id combined with the client Id together forms the RMD number that invokes the services through RMD.
Componentization Process
The process of Componentization involves the following steps:
- Step 1: Defining the Skeleton of a Component
Define the skeleton of a component and describe its lifecycle functions and interface functions. Assign a unique IOC communication port to the component that identifies the execution object. - Step 2: Defining
clEoBasicLibs
Define the set of OpenClovis SAFplus Platform libraries required by the component. - Step 3: Initializing the CPM Library
Initialize the CPM client library. - Step 4: Registering the CPM
Register the component with CPM infrastructure. - Step 5: Un-registering the CPM Library
Un-register the application from CPM Infrastructure, on exit of the application. - Step 6: Finalizing the CPM Library
Finalize the CPM client library when the component exits.
Step 1: Defining the Skeleton of a Component
Using OpenClovis IDE, define a structure with the name as clEoConfig
with the following fields initialized appropriately:
- Component lifecycle management parameters
- Number of receiver threads
- Unique communication port Id associated with the component for communication within the SAFplus Platform infrastructure.
# define COMP_EO_NUM_THREAD 3
# define COMP_IOC_PORT 0
ClEoConfigT clEoConfig = {
COMP_EO_NAME, /* EO Name */
COMP_EO_THREAD_PRIORITY, /* EO Thread Priority */
COMP_EO_NUM_THREAD, /* No of EO thread needed */
COMP_IOC_PORT, /* Required IOC Port */
COMP_EO_USER_CLIENT_ID, /* Size of the client table */
COMP_EO_USE_THREAD_MODEL, /* Whether to use main thread for EO
* receive or not */
clCompAppInitialize, /* Function callback to initialize the
* application */
clCompAppFinalize, /* Function callback to terminate the
* application */
clCompAppStateChange, /* Function callback to change the
* application state */
clCompAppHealthCheck, /* Function callback to check the
* application health */
};
Where,
COMP_EO_NAME
is the name of the component message passing infrastructure also called as the Execution Object (EO). The EO name on every node must be unique.COMP_EO_THREAD_PRIORITY
is the priority of the EO thread.COMP_EO_NUM_THREAD
is the count of worker threads for RMD message processing. This must be greater than or equal to 1.COMP_IOC_PORT
is the unique IOC communication port associated with every component.COMP_EO_USER_CLIENT_ID
is the number of clients that can exist for a given component. You can use this macroCOMP_EO_USER_CLIENT_ID
, as a default value.COMP_EO_USE_THREAD_MODEL
is the usage of the main thread. This is explained in details in Figure Illustration of the usage of application type.clCompAppInitialize
is invoked by the OpenClovis SAFplus Platform infrastructure when the componentized application is started. It performs the initialization actions (for example installing EO client tables) for any application that is being componentized. For SA-aware components, after the application is initialized, it informs AMF that it is ready to provide services usingclCpmComponentRegister
API.clCompAppFinalize
is being deprecated and must be NULL.clCompAppStateChange
is called whenever the application state needs to be changed toSUSPEND
orRESUME
.clCompAppHealthCheck
is called periodically by Component Manager to check the health of the componentized application. In response to this callback, you need to verify if the application is running properly and fill in the structure passed to it.
The application must consider the following while defining the clEoConfig
structure:
- If you select
COMP_EO_USE_THREAD_MODEL
asCL_EO_USE_THREAD_FOR_RECV
, the main thread must not be blocked in theclCompAppInitialize
. It must return after the library is initialized. Later, the main thread will be used for RMD message receive function. - If you select
COMP_EO_USE_THREAD_MODEL
asCL_EO_USE_THREAD_FOR_APP
, the main thread must be blocked in theClEoAppCreateCallbackT
or used by the application. It must return only when theClCpmTerminateCallbackT
is called.
Step 2: Defining clEoBasicLibs
This structure contains the basic OpenClovis SAFplus Platform libraries required by the component. The first six libraries must always be set to CL_TRUE
; the remaining can be made CL_TRUE
only if the corresponding services are required by the application. If set to CL_TRUE
, the corresponding libraries will automatically get initialized during component initialization.
ClUint8T clEoBasicLibs[] = {
CL_TRUE, /* OSAL */
CL_TRUE, /* Timer */
CL_TRUE, /* Buffer */
CL_TRUE, /* IOC */
CL_TRUE, /* RMD */
CL_TRUE, /* EO */
CL_TRUE, /* OM */
CL_FALSE, /* HAL */
CL_FALSE, /* DBAL */
};
Step 2.1: Defining clEoClientLibs
This structure contains a list of client libraries which the components can initialize if they need the corresponding service within the SAFplus Platform infrastructure. All the values in this structure are optional. If set to CL_TRUE
, the corresponding libraries will automatically get initialized during component initialization.
ClUint8T clEoClientLibs[] = {
CL_TRUE, /* Clovis Object Registry */
CL_TRUE, /* Chassis Manager */
CL_TRUE, /* Name Service*/
CL_TRUE, /* Log Service*/
CL_FALSE, /* Trace Service */
CL_FALSE, /* Diagnostic Manager */
CL_TRUE, /* Transaction Manager */
CL_FALSE, /* NA */
CL_TRUE, /* Provisioning Library*/
CL_TRUE, /* Alarm Manager */
CL_TRUE, /* Debug Service*/
CL_FALSE /* Group Membership Service */
};
When this is defined, the application is componentized. During startup, the stubs generated by OpenClovis IDE does all the necessary things as defined in the above structures.
OpenClovis SAFplus Platform libraries linked to the user application require three structures to be defined, otherwise, it will lead to compilation failure.
Step 3: Initializing the CPM Library
From clCompAppInitialize
callback, you need to initialize the CPM library by calling the clCpmClientInitialize
and provide a structure for callback, defined as ClCpmCallbacksT
in clCpmApi.h
file.
-
ClCpmHealthCheckCallbackT
callback: This callback is not implemented by OpenClovis SAFplus Platform for the current release. -
ClCpmTerminateCallbackT
callback: This is called when the application requires to be terminated. In this function callback, the application will clean up its acquired resources. -
ClCpmCSISetCallbackT
callback: This is called when AMF assigns the workload to the application. -
ClCpmCSIRmvCallbackT
callback: This is called when AMF removes the workload from the application. -
ClCpmProtectionGroupTrackCallbackT
callback: This is called when there is a change in the status of the protection group of a particular CSI. The application tracks the status of the CSI usingclCpmProtectionGroupTrack
API. -
ClCpmProxiedComponentInstantiateCallbackT
callback: This is called when the proxy component instantiates one or more of its proxied components. -
ClCpmProxiedComponentCleanupCallbackT
callback: This is called when the proxy component clean up one or more of its proxied components.
Step 4: Registering the CPM
From clCompAppInitialize
callback, you need to register the component by invoking clCpmComponentRegister
API. The component must be registered only when it is ready to provide the services. After the component is registered, it can either provide some specific service or use the services provided by other components. When AMF decides to terminate the component, it calls the ClCpmTerminateCallbackT
callback provided by the application.
Step 5: Un-registering the CPM Library
From ClCpmTerminateCallbackT
callback, you need to un-register the component by invoking clCpmComponentUnregister
API. The component is un-registered when the component is no longer providing the services. You can cleanup all the associated resources.
Step 6: Finalizing the CPM Library
From ClCpmTerminateCallbackT
callback, you need to finalize CPM library by calling the clCpmClientFinalize
API.
Component Characteristics
An application component in OpenClovis SAFplus Platform infrastructure has the following characteristics:
- Each component has an associated lifecycle and registers the initialization and shutdown functions with OpenClovis SAFplus Platform infrastructure.
- Each component provides implementation of high availability related OpenClovis SAFplus Platform mandated functionality. Each component has a communication port called the IOC port assigned by OpenClovis SAFplus Platform infrastructure to communicate with other components including Event, Checkpoint, Component Manager and so on. IOC manages the IOC port and implements the actual mechanism of sending and receiving messages.
- Each component installs the services into the function table indexed by the service id through the
clEoClientInstall
API. This function table is contained in the client table identified by the client id namely,CL_EO_NATIVE_COMPONENT_TABLE_ID
. To use any service of the component, you must use the RMD number that uniquely identifies the service. - Each component is associated with one or more worker threads. Every message intended to be delivered to a component, is set in a queue by IOC on the corresponding port. This message is then picked up by the receiver thread in EO and queued in the EO Job Queue. One of the worker threads picks up the message, decodes the RMD number, and executes the appropriate interface function.
Figure Component Realization in OpenClovis SAFplus Platform environment explains the elements of OpenClovis SAFplus Platform component.
OpenClovis SAFplus Platform component comprises the following:
- The function
cbfn3
is a callback function that represents the lifecycle function of the component. The lifecycle functions are registered with distributed infrastructure of OpenClovis SAFplus Platform and helps to initialize, finalize, and check the status of the component. These functions facilitate CPM to manage the lifecycle of the component. - The functions
fn1
andfn2
are the component interface functions with a unique identifier. These functions are called the service interface of the component and they represent the services that the components can provide.
The functionfn3
is a service interface of the CPM component. -
App2
can make calls tofn1
andfn2
, component interface function ofApp1
using RMD of OpenClovis SAFplus Platform distributed infrastructure. The functionsfn1
andfn2
are depicted asrfn1
andrfn2
respectively in Figure Illustration of the usage of application type.
The CPM server periodically performs a health check on the component and remotely invokes the CPM client service functionality using RMD. This functionality in turn invokes the health check up callback, illustrated as cbfn3
in Figure Illustration of the usage of application type, of the component and returns the status to the CPM server.
Component Initialization
OpenClovis SAFplus Platform provides a set of libraries that must be initialized for application manageability, high availability, and pre-defined main function for easy usability.
The following are the consequences when a component application is initialized:
- It initializes the basic OpenClovis SAFplus Platform libraries to provide operating system or architecture-independent execution context for the component. It also installs the signal handler to handle all the signals that causes the process termination [for example SIGSEGV, SIGFBP, SIGILL, and so on]. Whenever a signal is generated, signal handler receives the stack trace and signal-related information. The stack trace and the signal information are then moved into the shared memory.
- It creates the communication link.
- It initializes the client libraries so that the component can use the functionality provided by other components of OpenClovis SAFplus Platform. For example, Event, Provisioning and so on.
- It passes the control to the initialization callback function provided in the
clEoConfig
structure, where it can implement the specific functionalities exposed by the application. - If the application holds the main thread, the execution continues with the application. Otherwise, after the initialization callback returns, the main thread waits in a loop to accept messages.
- When OpenClovis SAFplus Platform requires to shutdown a component, based on the policy or during node shutdown,
ClCpmTerminateCallbackT
callback is invoked, which requires to follow the steps explained above.
If you create new threads or tasks, and send messages from the thread context, then clEoMyEoObjectSet()
and clEoMyEoIocPortSet()
must be in the new thread context to set the environment for communication purpose. If not, every RMD returns a failure.
Using Availability Management Framework (AMF)
Componentization process helps the applications to interact with the OpenClovis SAFplus Platform infrastructure.
Applications can be made highly available by providing certain callbacks specific to AMF. AMF requires applications to provide callbacks for lifecycle operations, work assignment operations, and protect group related operations (optional). AMF decides to instantiate and assign workload to a component. The components can be instantiated by calling the clCompAppInitialize
callback of the component. This callback is an indication to the component to start the process.
After the component completes the start process and is ready to receive the work assignments, it informs the AMF framework by invoking the AMF clCpmComponentRegister
API.
Registration process is an indication for AMF that the component have been successfully started and can be assigned work by AMF. On receiving the register callback AMF assigns workload to the component with appropriate high availability state using the CSI set callback API registered in the callback function list using clCpmClientInitialize
API.
The CSI set API informs the component that a given CSI (workload) have been assigned to the component. The CSI contains information about the name-value pairs and HA state assigned to the component. The name value pairs are the names and values that a component requires to start serving the workloads. For example, a name-value pair can be a configuration filename and its path or location of checkpointed data. The high availability state is assigned by the AMF to the component, for example, ACTIVE or STANDBY. The ACTIVE HA state is an indication that the component will be active for the given CSI. The STANDBY high availability state indicates that the component must prepare itself to takeover in case of failures of the ACTIVE component.
After receiving the CSI set request, the component informs the AMF framework after it is completes the processing required for serving the CSI. This response is an indication for the AMF that the component has successfully received the CSI request and is ready to serve the CSI.
HA state can have the following values:
- Active - This means the component is ready to serve the CSI.
- Standby - This means that component is on standby for the CSI.
- Quiescing - This means the component must finish the existing operations and must not take any new assignment. After it has completed serving the existing request, it informs the AMF framework by calling
Quiescing
complete API. This is an indication to AMF that the component will not take any new assignment for the CSI and the AMF framework can remove the CSI assignments from this component and send it to other components.
AMF framework can call the CSI remove callback to remove the CSI assignments from the component. Component's response to this API is an indication for AMF to assign the CSI to other component.
AMF also provides API for interested component to track the status of protection group. Protection group comprises all the components that are assigned HA state (Active or Standby) for the CSI. Protection group tracks API allows the interested component to register with the framework indicating the CSI for which it is interested. The AMF framework informs the component whenever there is a change in the protection group by calling the protection group callback.
Proxied components can interact with the AMF framework through a proxy component. Proxy component is an SA-aware component that can communicate with the AMF framework directly. The calls to proxied component pass through the proxy component. All the lifecycle operations, workload operations for proxied component are carried through the proxy component.
Characteristics of a Sample Application
The sample application depicts an application to generate Global Sequence Numbers. The following are the characteristics of this component:
-
clCompAppInitialize
,clCompAppFinalize
, andclCompAppStateChange
are the lifecycle related functions. -
clCompAppHealthCheck
is the functionality related to High Availability.
Sample Code for Componentization
---------------------------------------------------------------
clCompAppMain.h
---------------------------------------------------------------
#ifndef CL_COMP_APP_MAIN
# define CL_COMP_APP_MAIN
# include <clCompCfg.h>
# ifndef COMP_NAME
# error "COMP_NAME is not defined. Bad or missing ./clCompCfg.h"
# endif
ClRcT clCompAppTerminate
(ClInvocationT invocation,
const ClNameT *compName);
ClRcT clCompAppAMFCSISet
(ClInvocationT invocation,
const ClNameT *compName,
ClAmsHAStateT haState,
ClAmsCSIDescriptorT csiDescriptor);
ClRcT clCompAppAMFCSIRemove
(ClInvocationT invocation,
const ClNameT *compName,
const ClNameT *csiName,
ClAmsCSIFlagsT csiFlags);
ClRcT clCompAppInitialize
(ClUint32T argc,
ClCharT *argv[]);
ClRcT clCompAppFinalize();
ClRcT clCompAppStateChange
(ClEoStateT eoState);
ClRcT clCompAppHealthCheck
(ClEoSchedFeedBackT *schFeedback);
#endif
---------------------------------------------------------------
clCompAppMain.c
---------------------------------------------------------------
#include <clCommon.h>
#include <clOsalApi.h>
#include <clIocServices.h>
#include <clRmdApi.h>
#include <clDebugApi.h>
#include <clOmApi.h>
#include <clOampRtApi.h>
#include <clProvApi.h>
#include <clAlarmApi.h>
#include <clEoApi.h>
#include <clCpmApi.h>
#include <clIdlApi.h>
#include <string.h>
#include "./clCompAppMain.h"
#include "clCompA.h"
#if HAS_EO_SERVICES
extern ClRcT idlClientInstall(void);
#endif
ClCpmHandleT cpmHandle;
ClRcT clCompAppTerminate(ClInvocationT invocation, const ClNameT *compName)
{
ClRcT rc;
COMPA_DBG_PRINT(("Inside %s \n", __FUNCTION__));
/*
Do the App Finalization
*/
clAppFinalize();
COMPA_DBG_PRINT(("Unregister with CPM before Exit .................%s\n",
compName->value));
rc = clCpmComponentUnregister(cpmHandle, compName, NULL);
COMPA_DBG_PRINT(("Finalize before Exit ................. %s\n",
compName->value));
rc = clCpmClientFinalize(cpmHandle);
clCpmResponse(cpmHandle, invocation, CL_OK);
return CL_OK;
}
ClRcT clCompAppAMFCSISet(ClInvocationT invocation, const ClNameT *compName,
ClAmsHAStateT haState,
ClAmsCSIDescriptorT csiDescriptor)
{
ClRcT rc = CL_OK;
COMPA_DBG_PRINT(("Inside Function %s \n", __FUNCTION__));
if (haState == CL_AMS_HA_STATE_QUIESCING)
{
/*
TODO make the quiescing complete call after the work assigned is
done
*/
rc = clAppSetQuiescingState(invocation, compName, csiDescriptor);
COMPA_DBG_PRINT(("######## before clCpmCSIQuiescingComplete for "
"%s ########\n", COMP_EO_NAME));
clCpmCSIQuiescingComplete(cpmHandle, invocation, rc);
}
else if (haState == CL_AMS_HA_STATE_ACTIVE)
{
rc = clAppSetActiveState(invocation, compName, csiDescriptor);
COMPA_DBG_PRINT(("######## before clCpmResponse(ACTIVE) for "
"%s########\n", COMP_EO_NAME));
clCpmResponse(cpmHandle, invocation, rc);
}
else if (haState == CL_AMS_HA_STATE_STANDBY)
{
rc = clAppSetStandbyState(invocation, compName, csiDescriptor);
COMPA_DBG_PRINT(("######## before clCpmResponse(STANDBY) for "
"%s########\n", COMP_EO_NAME));
clCpmResponse(cpmHandle, invocation, rc);
}
else
{
COMPA_DBG_PRINT(("######## before clCpmResponse(%d) for %s########\n",
haState, COMP_EO_NAME));
clCpmResponse(cpmHandle, invocation, CL_OK);
}
if (CL_OK != rc)
{
COMPA_DBG_PRINT(("clCompAppAMFCSISet failed with %d for %s\n", rc,
COMP_EO_NAME));
}
return CL_OK;
}
ClRcT clCompAppAMFCSIRemove(ClInvocationT invocation, const ClNameT *compName,
const ClNameT *csiName, ClAmsCSIFlagsT csiFlags)
{
ClRcT rc = CL_OK;
COMPA_DBG_PRINT(("Inside Function %s \n", __FUNCTION__));
/*
TODO stop the work assigned before making the response done
*/
rc = clAppCSIRemove(invocation, compName, csiName, csiFlags);
clCpmResponse(cpmHandle, invocation, rc);
return CL_OK;
}
/*
Service Initialization:
1. Initialize your counter and create mutex.
2. Register Name and generate Logical Address
3 Initialize Checkpoint Service
*/
ClRcT clAppInitialize(ClCpmHandleT cpmHandle)
{
ClRcT rc = CL_OK;
ClNameSvcRegisterT nameRegInfo = { {0} };
ClEoExecutionObjT *pEoObj = NULL;
ClNameT compName = { 0 };
clLogWrite(CL_LOG_HANDLE_APP,CL_LOG_DEBUG,COMP_A_SERVICE_NAME,
CL_COMPA_LOG_0_INIT_ENTER);
gAppGlobal.version.releaseCode = 'B';
gAppGlobal.version.majorVersion = 0x01;
gAppGlobal.version.minorVersion = 0x01;
gAppGlobal.ckptName.length = sizeof(CKPT_NAME);
strncpy(gAppGlobal.ckptName.value, CKPT_NAME, strlen(CKPT_NAME));
gAppGlobal.cpmHandle = cpmHandle;
gAppGlobal.ckptHdl = -1;
/*
counter is initialized here
*/
gAppGlobal.count = 0;
rc = clOsalMutexCreate(&gAppGlobal.mutex);
if (rc != CL_OK)
{
COMPA_DBG_PRINT(("Failure in mutex create [0x %x]", rc));
clLogWrite(CL_LOG_HANDLE_APP,CL_LOG_ERROR,COMP_A_SERVICE_NAME,
CL_COMPA_LOG_1_MUTEX_CREATE_FAILED, rc);
return rc;
}
/* register your name with Name Service and get the logical address */
rc = clCpmComponentNameGet(gAppGlobal.cpmHandle, &compName);
if (CL_OK != rc)
{
COMPA_DBG_PRINT(("Failure in component name get [0x %x]", rc));
clLogWrite(CL_LOG_HANDLE_APP,CL_LOG_ERROR,COMP_A_SERVICE_NAME,
CL_COMPA_LOG_1_NAME_GET_FAILED, rc);
goto mutexCleanup;
}
rc = clCpmComponentIdGet(gAppGlobal.cpmHandle, &compName,
&gAppGlobal.compId);
if (CL_OK != rc)
{
COMPA_DBG_PRINT(("Failed to get the component ID [0x %x]", rc));
clLogWrite(CL_LOG_HANDLE_APP,CL_LOG_ERROR,COMP_A_SERVICE_NAME,
CL_COMPA_LOG_1_ID_GET_FAILED, rc);
goto mutexCleanup;
}
nameRegInfo.name.length = strlen(COMP_A_SERVICE_NAME);
strcpy(nameRegInfo.name.value, COMP_A_SERVICE_NAME);
nameRegInfo.compId = gAppGlobal.compId;
nameRegInfo.priority = CL_NS_PRIORITY_HIGH;
nameRegInfo.attrCount = 0;
gAppGlobal.nameObjRef = CL_NS_GET_OBJ_REF;
rc = clNameRegister(gAppGlobal.nameCntxId, &nameRegInfo,
&(gAppGlobal.nameObjRef));
if (rc != CL_OK)
{
COMPA_DBG_PRINT(("Failed to register with name [0x %x]", rc));
clLogWrite(CL_LOG_HANDLE_APP,CL_LOG_ERROR,COMP_A_SERVICE_NAME,
CL_COMPA_LOG_1_NAME_REGISTER_FAILED, rc);
goto mutexCleanup;
}
else
{
clLogWrite(CL_LOG_HANDLE_APP,CL_LOG_DEBUG,COMP_A_SERVICE_NAME,
CL_COMPA_LOG_0_NAME_REGISTERED);
}
/*
Initialize the Checkpoint Service
*/
rc = clCkptInitialize(&gAppGlobal.ckptSvcHdl, NULL, &gAppGlobal.version);
if (rc != CL_OK)
{
COMPA_DBG_PRINT(("Failed in checkpoint initialize rc [0x %x]", rc));
clLogWrite(CL_LOG_HANDLE_APP,CL_LOG_ERROR,COMP_A_SERVICE_NAME,
CL_COMPA_LOG_1_CKPT_INIT_FAILED, rc);
goto nameCleanup;
}
rc = clEoMyEoObjectGet(&pEoObj);
if (rc != CL_OK)
{
COMPA_DBG_PRINT(("Failed to get the EoObject rc [0x %x]", rc));
goto ckptCleanup;
}
gAppGlobal.pEoObj = pEoObj;
rc = compADebugRegister(gAppGlobal.pEoObj);
if (rc != CL_OK)
{
COMPA_DBG_PRINT(("Failed to register with debug rc[0x %x]", rc));
goto ckptCleanup;
}
COMPA_DBG_PRINT(("Sucessfully completed initialization\n"));
clLogWrite(CL_LOG_HANDLE_APP,CL_LOG_DEBUG,COMP_A_SERVICE_NAME,
CL_COMPA_LOG_0_INIT_DONE, rc);
return CL_OK;
ckptCleanup:
clCkptFinalize(gAppGlobal.ckptSvcHdl);
nameCleanup:
clNameComponentDeregister(gAppGlobal.compId);
mutexCleanup:
clOsalMutexDelete(gAppGlobal.mutex);
return rc;
}
ClRcT clCompAppInitialize(ClUint32T argc, ClCharT *argv[])
{
ClNameT appName;
ClCpmCallbacksT callbacks;
ClVersionT version;
ClIocPortT iocPort;
ClRcT rc = CL_OK;
/*
Do the App intialization
*/
/*
Do the CPM client init/Register
*/
version.releaseCode = 'B';
version.majorVersion = 01;
version.minorVersion = 01;
callbacks.appHealthCheck = NULL;
callbacks.appTerminate = clCompAppTerminate;
callbacks.appCSISet = clCompAppAMFCSISet;
callbacks.appCSIRmv = clCompAppAMFCSIRemove;
callbacks.appProtectionGroupTrack = NULL;
callbacks.appProxiedComponentInstantiate = NULL;
callbacks.appProxiedComponentCleanup = NULL;
clEoMyEoIocPortGet(&iocPort);
COMPA_DBG_PRINT(("Application Address 0x%x Port %x\n",
clIocLocalAddressGet(), iocPort));
rc = clCpmClientInitialize(&cpmHandle, &callbacks, &version);
COMPA_DBG_PRINT(("After clCpmClientInitialize %d\t %x\n", cpmHandle, rc));
#if HAS_EO_SERVICES
idlClientInstall();
#endif
/*
Block and use the main thread if required other wise return
*/
/*
TODO: Application code should come here
*/
clAppInitialize(cpmHandle);
/*
if main thread usage policy == CL_EO_USE_THREAD_FOR_APP return from this
function only after app finishes
*/
/*
if main thread usage policy == CL_EO_USE_THREAD_FOR_RECV return from
this function immediately after doing some application initialize stuff
*/
rc = clCpmComponentNameGet(cpmHandle, &appName);
COMPA_DBG_PRINT(("After clCpmComponentNameGet %d\t %s\n", cpmHandle,
appName.value));
rc = clCpmComponentRegister(cpmHandle, &appName, NULL);
COMPA_DBG_PRINT(("After clCpmClientRegister %x\n", rc));
return CL_OK;
}
ClRcT clCompAppFinalize()
{
return CL_OK;
}
ClRcT clCompAppStateChange(ClEoStateT eoState)
{
/*
Application state change
*/
return CL_OK;
}
ClRcT clCompAppHealthCheck(ClEoSchedFeedBackT *schFeedback)
{
/*
Modify following as per App requirement
*/
schFeedback->freq = CL_EO_BUSY_POLL;
schFeedback->status = CL_CPM_EO_ALIVE;
return CL_OK;
}
ClEoConfigT clEoConfig = {
COMP_EO_NAME, /* EO Name */
COMP_EO_THREAD_PRIORITY, /* EO Thread Priority */
COMP_EO_NUM_THREAD, /* No of EO thread needed */
COMP_IOC_PORT, /* Required Ioc Port */
COMP_EO_USER_CLIENT_ID,
COMP_EO_USE_THREAD_MODEL, /* Whether to use main thread for
eo Recv or not */
clCompAppInitialize, /* Function CallBack to initialize
the Application */
clCompAppFinalize, /* Function Callback to Terminate
the Application */
clCompAppStateChange, /* Function Callback to change the
Application state */
clCompAppHealthCheck, /* Function Callback to change the
Application state */
};
/*
You may force default libraries here by using CL_TRUE or CL_FALSE or use
appropriate configuration generated by ClovisWorks in ./clCompAppMain.h The
first 6 basic libraries are mandatory.
*/
ClUint8T clEoBasicLibs[] = {
COMP_EO_BASICLIB_OSAL, /* osal */
COMP_EO_BASICLIB_TIMER, /* timer */
COMP_EO_BASICLIB_BUFFER, /* buffer */
COMP_EO_BASICLIB_IOC, /* ioc */
COMP_EO_BASICLIB_RMD, /* rmd */
COMP_EO_BASICLIB_EO, /* eo */
COMP_EO_BASICLIB_OM, /* om */
COMP_EO_BASICLIB_HAL, /* hal */
COMP_EO_BASICLIB_DBAL, /* dbal */
};
ClUint8T clEoClientLibs[] = {
COMP_EO_CLIENTLIB_COR, /* cor */
COMP_EO_CLIENTLIB_CM, /* cm */
COMP_EO_CLIENTLIB_NAME, /* name */
COMP_EO_CLIENTLIB_LOG, /* log */
COMP_EO_CLIENTLIB_TRACE, /* trace */
COMP_EO_CLIENTLIB_DIAG, /* diag */
COMP_EO_CLIENTLIB_TXN, /* txn */
CL_FALSE, /* NA */
COMP_EO_CLIENTLIB_PROV, /* Prov */
COMP_EO_CLIENTLIB_ALARM, /* alarm */
COMP_EO_CLIENTLIB_DEBUG, /* debug */
COMP_EO_CLIENTLIB_GMS /* gms */
};
#########