nfv/mtce-guest/src/guestInstClass.cpp

765 lines
24 KiB
C++

/*
* Copyright (c) 2013-2016 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
/**
* @file
* Wind River CGTS Platform Guest Services "Instances Base Class"
*/
#include <sys/types.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
using namespace std;
#include "nodeBase.h" /* for ... common definitions */
#include "nodeEvent.h" /* for ... set_inotify_watch_file */
#include "nodeTimers.h" /* for ... mtcTimer */
#include "guestBase.h" /* for ... instInfo */
#include "guestUtil.h" /* for ... guestUtil_inst_init */
#include "guestInstClass.h" /* for ... get_inst */
#include "guestSvrUtil.h" /* for ... hb_get_state_name */
/**< constructor */
guestInstClass::guestInstClass()
{
inst_head = NULL ;
inst_tail = NULL ;
memory_allocs = 0 ;
memory_used = 0 ;
instances = 0 ;
for ( int i = 0 ; i < MAX_INSTANCES ; i++ )
{
inst_ptrs[i] = NULL ;
}
fsm_exit = false ;
reporting = true ;
return ;
}
/**< destructor */
guestInstClass::~guestInstClass()
{
inst * inst_ptr = inst_head ;
inst * temp_ptr = inst_ptr ;
while ( inst_ptr != NULL )
{
temp_ptr = inst_ptr ;
inst_ptr = inst_ptr->next ;
delInst (temp_ptr);
}
if ( memory_used != 0 )
{
elog ( "Apparent Memory Leak - Allocs:%d and Bytes:%d\n",
memory_allocs, memory_used );
}
else
{
dlog ( "No Memory Leaks\n\n");
}
return ;
}
void guestInstClass::guest_fsm_run ( void )
{
fsm_run ();
}
/*
* Allocate new instance and tack it on the end of the instance list
*/
struct guestInstClass::inst* guestInstClass::addInst ( string uuid )
{
if ( uuid.length() != UUID_LEN )
{
elog ("invalid instance uuid ; cannot add %s\n", uuid.c_str());
return static_cast<struct inst *>(NULL);
}
/* verify instance is not already provisioned */
struct inst * inst_ptr = guestInstClass::getInst ( uuid );
if ( inst_ptr )
{
if ( guestInstClass::remInst ( uuid ) )
{
/* Should never get here but if we do then */
/* something is seriously wrong */
elog ("%s unable to remove instance during reprovision\n",
log_prefix(&inst_ptr->instance).c_str());
return static_cast<struct inst *>(NULL);
}
}
/* allocate memory for new instance */
inst_ptr = guestInstClass::newInst ();
if( inst_ptr == NULL )
{
elog ( "failed to allocate memory for new instance\n" );
return static_cast<struct inst *>(NULL);
}
guestUtil_inst_init ( &inst_ptr->instance );
/* Init the new instance */
inst_ptr->instance.uuid = uuid ;
inst_ptr->query_flag = false ;
inst_ptr->instance.connect_wait_in_secs = DEFAULT_CONNECT_WAIT ;
/* Init instance's connect and monitor timers */
/* Assign the timer the instance's name */
mtcTimer_init ( inst_ptr->reconnect_timer, uuid );
mtcTimer_init ( inst_ptr->connect_timer, uuid );
mtcTimer_init ( inst_ptr->monitor_timer, uuid );
mtcTimer_init ( inst_ptr->init_timer, uuid );
mtcTimer_init ( inst_ptr->vote_timer, uuid );
inst_ptr->action = FSM_ACTION__NONE ;
inst_ptr->connectStage = INST_CONNECT__START ;
inst_ptr->monitorStage = INST_MONITOR__STEADY ;
inst_ptr->messageStage = INST_MESSAGE__RECEIVE ;
/* If the instance list is empty add it to the head */
if( inst_head == NULL )
{
inst_head = inst_ptr ;
inst_tail = inst_ptr ;
inst_ptr->prev = NULL ;
inst_ptr->next = NULL ;
}
else
{
/* link the new_instance to the tail of the inst_list
* then mark the next field as the end of the inst_list
* adjust tail to point to the last instance
*/
inst_tail->next = inst_ptr ;
inst_ptr->prev = inst_tail ;
inst_ptr->next = NULL ;
inst_tail = inst_ptr ;
}
instances++ ;
ilog ("%s added as instance %d\n", log_prefix(&inst_ptr->instance).c_str(), instances);
return inst_ptr ;
}
/* Remove an instance from the linked list of instances - may require splice action */
int guestInstClass::remInst( string uuid )
{
if ( uuid.empty() )
return -ENODEV ;
if ( inst_head == NULL )
return -ENXIO ;
struct inst * inst_ptr = getInst ( uuid );
if ( inst_ptr == NULL )
return -EFAULT ;
stop_instance_timers ( inst_ptr );
/* Close the channel if it is open */
guestUtil_close_channel ( &inst_ptr->instance );
/* If the instance is the head instance */
if ( inst_ptr == inst_head )
{
/* only one instance in the list case */
if ( inst_head == inst_tail )
{
dlog2 ("Single Inst -> Head Case\n");
inst_head = NULL ;
inst_tail = NULL ;
}
else
{
dlog2 ("Multiple Insts -> Head Case\n");
inst_head = inst_head->next ;
inst_head->prev = NULL ;
}
}
/* if not head but tail then there must be more than one
* instance in the list so go ahead and chop the tail.
*/
else if ( inst_ptr == inst_tail )
{
dlog2 ("Multiple Inst -> Tail Case\n");
inst_tail = inst_tail->prev ;
inst_tail->next = NULL ;
}
else
{
dlog2 ("Multiple Inst -> Full Splice Out\n");
inst_ptr->prev->next = inst_ptr->next ;
inst_ptr->next->prev = inst_ptr->prev ;
}
delInst ( inst_ptr );
instances-- ;
if ( instances == 0 )
ilog ("no instances to monitor\n");
return (PASS) ;
}
/* Perform a linked list search for the instance matching the instance name */
struct guestInstClass::inst* guestInstClass::getInst ( string chan_or_uuid )
{
struct inst * inst_ptr = static_cast<struct inst *>(NULL) ;
/* check for empty list condition */
if ( inst_head )
{
for ( inst_ptr = inst_head ; inst_ptr != NULL ; inst_ptr = inst_ptr->next )
{
if ( !inst_ptr->instance.uuid.compare (chan_or_uuid) )
{
return inst_ptr ;
}
if ( !inst_ptr->instance.chan.compare (chan_or_uuid) )
{
return inst_ptr ;
}
if (( inst_ptr->next == NULL ) || ( inst_ptr == inst_tail ))
break ;
}
}
return static_cast<struct inst *>(NULL);
}
/*
* Allocates memory for a new instance and stores its address in inst_ptrs
*
* @param void
* @return pointer to the newly allocted instance memory
*/
struct guestInstClass::inst * guestInstClass::newInst ( void )
{
struct guestInstClass::inst * temp_inst_ptr = NULL ;
if ( memory_allocs == 0 )
{
memset ( inst_ptrs, 0 , sizeof(struct inst *)*MAX_INSTANCES);
}
// find an empty spot
for ( int i = 0 ; i < MAX_INSTANCES ; i++ )
{
if ( inst_ptrs[i] == NULL )
{
inst_ptrs[i] = temp_inst_ptr = new inst ;
memory_allocs++ ;
memory_used += sizeof (struct guestInstClass::inst);
return temp_inst_ptr ;
}
}
elog ( "failed to store new instance pointer address\n" );
return temp_inst_ptr ;
}
/* Frees the memory of a pre-allocated instance and removes
* it from the inst_ptrs list.
*
* @param instance * pointer to the instance memory address to be freed
* @return int return code { PASS or -EINVAL }
*/
int guestInstClass::delInst ( struct guestInstClass::inst * inst_ptr )
{
if ( memory_allocs > 0 )
{
for ( int i = 0 ; i < MAX_INSTANCES ; i++ )
{
if ( inst_ptrs[i] == inst_ptr )
{
delete inst_ptr ;
inst_ptrs[i] = NULL ;
memory_allocs-- ;
memory_used -= sizeof (struct guestInstClass::inst);
return PASS ;
}
}
elog ( "unable to validate memory address being freed\n" );
}
else
elog ( "free memory called when there is no memory to free\n" );
return -EINVAL ;
}
/***************************************************************************************
* P U B L I C I N T E R F A C E S
**************************************************************************************/
/* Add an instance based on its uuid.
* If the instance already exists then update its info */
int guestInstClass::add_inst ( string uuid , instInfo & instance )
{
int rc = FAIL ;
struct guestInstClass::inst * inst_ptr = getInst(uuid);
if ( inst_ptr )
{
ilog ("********************************************************\n");
ilog ("%s Already provisioned - TODO: Create copy constructor \n", uuid.c_str());
ilog ("********************************************************\n");
/* Send back a retry in case the add needs to be converted to a modify */
rc = PASS ;
}
/* Otherwise add it as a new instance */
else
{
if ( uuid.length() != UUID_LEN )
{
elog ("invalid uuid %s\n", uuid.c_str());
return (FAIL_INVALID_UUID);
}
inst_ptr = guestInstClass::addInst(uuid);
if ( inst_ptr )
{
rc = PASS ;
}
else
{
elog ("failed to add instance '%s'\n", uuid.c_str());
rc = FAIL_NULL_POINTER ;
}
}
if ( rc == PASS )
{
inst_ptr->heartbeat_count = 0 ;
inst_ptr->mismatch_count = 0 ;
/* TODO: This needs to be a complete copy - Need copy constructor */
inst_ptr->instance.heartbeat.failures = 0 ;
inst_ptr->instance.heartbeat.failed = false ;
inst_ptr->instance.heartbeat.reporting = instance.heartbeat.reporting ;
inst_ptr->instance.heartbeat.provisioned = instance.heartbeat.provisioned ;
inst_ptr->instance.heartbeat.state = instance.heartbeat.state ;
inst_ptr->instance.hbState = hbs_server_waiting_init ;
inst_ptr->instance.vnState = hbs_server_waiting_init ;
inst_ptr->instance.name_log_prefix = "" ;
inst_ptr->instance.uuid_log_prefix = "" ;
inst_ptr->instance.name = instance.name ;
inst_ptr->instance.inst = instance.inst ;
inst_ptr->instance.connected = instance.connected ;
inst_ptr->instance.heartbeating = instance.heartbeating ;
inst_ptr->instance.chan_fd = instance.chan_fd ;
inst_ptr->instance.chan_ok = instance.chan_ok ;
inst_ptr->instance.corrective_action = instance.corrective_action ;
inst_ptr->instance.heartbeat_interval_ms = instance.heartbeat_interval_ms ;
inst_ptr->instance.vote_secs = instance.vote_secs ;
inst_ptr->instance.shutdown_notice_secs = instance.shutdown_notice_secs ;
inst_ptr->instance.suspend_notice_secs = instance.suspend_notice_secs ;
inst_ptr->instance.resume_notice_secs = instance.resume_notice_secs ;
inst_ptr->instance.restart_secs = instance.restart_secs ;
/* Update the channel */
if ( instance.chan.length() > UUID_LEN )
inst_ptr->instance.chan = instance.chan ;
}
return (rc);
}
/*****************************************************************************
*
* Name : del_inst
*
* Purpose : Delete an instance from the linked list
*
*****************************************************************************/
int guestInstClass::del_inst ( string uuid )
{
int rc = FAIL ;
if ( ! uuid.empty() )
{
/* free memory */
rc = remInst ( uuid );
}
return ( rc );
}
/*****************************************************************************
*
* Name : qry_inst
*
* Purpose : Send instance info to the guestAgent
*
*****************************************************************************/
int guestInstClass::qry_inst ( )
{
return ( guestAgent_qry_handler ());
}
void guestInstClass::stop_instance_timers ( struct guestInstClass::inst * inst_ptr )
{
/* Free the mtc timer if in use */
if ( inst_ptr->reconnect_timer.tid )
{
mtcTimer_stop ( inst_ptr->reconnect_timer );
inst_ptr->reconnect_timer.ring = false ;
inst_ptr->reconnect_timer.tid = NULL ;
}
/* Free the connect timer if in use */
if ( inst_ptr->connect_timer.tid )
{
mtcTimer_stop ( inst_ptr->connect_timer );
inst_ptr->connect_timer.ring = false ;
inst_ptr->connect_timer.tid = NULL ;
}
/* Free the monitor timer if in use */
if ( inst_ptr->monitor_timer.tid )
{
mtcTimer_stop ( inst_ptr->monitor_timer );
inst_ptr->monitor_timer.ring = false ;
inst_ptr->monitor_timer.tid = NULL ;
}
/* Free the init timer if in use */
if ( inst_ptr->init_timer.tid )
{
mtcTimer_stop ( inst_ptr->init_timer );
inst_ptr->init_timer.ring = false ;
inst_ptr->init_timer.tid = NULL ;
}
/* Free the vote timer if in use */
if ( inst_ptr->vote_timer.tid )
{
mtcTimer_stop ( inst_ptr->vote_timer );
inst_ptr->vote_timer.ring = false ;
inst_ptr->vote_timer.tid = NULL ;
}
}
void guestInstClass::free_instance_resources ( void )
{
/* check for empty list condition */
if ( inst_head )
{
for ( struct inst * inst_ptr = inst_head ; ; inst_ptr = inst_ptr->next )
{
if ( inst_ptr->instance.chan_fd )
{
ilog ("%s closing fd %d for uuid %s\n",
log_prefix(&inst_ptr->instance).c_str(),
inst_ptr->instance.chan_fd,
inst_ptr->instance.uuid.c_str());
close ( inst_ptr->instance.chan_fd );
}
stop_instance_timers ( inst_ptr );
if (( inst_ptr->next == NULL ) || ( inst_ptr == inst_tail ))
break ;
}
}
}
/****************************************************************************/
/** FSM Control Utilities */
/****************************************************************************/
void guestInstClass::reconnect_start ( const char * uuid_ptr )
{
string uuid = uuid_ptr ;
if ( uuid.length() != UUID_LEN )
{
elog ("invalid uuid %s (uuid:%ld)\n", uuid.c_str(), uuid.length());
return ;
}
struct guestInstClass::inst * inst_ptr = guestInstClass::getInst(uuid);
if ( inst_ptr )
{
guestUtil_close_channel ( &inst_ptr->instance );
}
else
{
inst_ptr = guestInstClass::addInst(uuid);
}
if ( inst_ptr )
{
instInfo * instInfo_ptr = &inst_ptr->instance ;
if ( instInfo_ptr->fd_namespace.size() )
{
/* Setup inotify to watch for new instance serial IO channel creations */
if ( set_inotify_watch_file ( instInfo_ptr->fd_namespace.data(),
instInfo_ptr->inotify_file_fd,
instInfo_ptr->inotify_file_wd))
{
elog ("%s failed to setup 'inotify' on %s\n",
log_prefix(instInfo_ptr).c_str(),
instInfo_ptr->fd_namespace.c_str());
}
}
ilog ("%s reconnecting ... %s\n", log_prefix(instInfo_ptr).c_str(),
instInfo_ptr->connected ? " CONNECTED" : "" );
if ( inst_ptr->connect_timer.tid )
mtcTimer_stop ( inst_ptr->connect_timer );
inst_ptr->action = FSM_ACTION__CONNECT ;
inst_ptr->connectStage = INST_CONNECT__START ;
// mtcTimer_start ( inst_ptr->connect_timer, guestTimer_handler, inst_ptr->instance.connect_wait_in_secs );
//ilog ("%s connect attempt in %d seconds\n",
// log_prefix(&inst_ptr->instance).c_str(), inst_ptr->instance.connect_wait_in_secs);
instInfo_ptr->connecting = true ;
}
else
{
elog ("%s failed to find or add instance\n", uuid.c_str() );
}
}
/****************************************************************************/
/** Inst Class Setter / Getters */
/****************************************************************************/
/*****************************************************************************
*
* Name : get_inst
*
* Purpose : Return a pointer to the instance for a specified uuid
*
*****************************************************************************/
instInfo * guestInstClass::get_inst ( string uuid )
{
struct guestInstClass::inst * inst_ptr = guestInstClass::getInst(uuid);
if ( inst_ptr )
{
return (&inst_ptr->instance );
}
return static_cast<instInfo *>(NULL);
}
/*****************************************************************************
*
* Name : getInst_timer
*
* Purpose : Return a pointer to the instance that contains the timer for
* the specified timer ID.
*
*****************************************************************************/
struct guestInstClass::inst * guestInstClass::getInst_timer ( timer_t tid, int timer_id )
{
if ( tid != NULL )
{
if ( inst_head )
{
struct inst * inst_ptr ;
for ( inst_ptr = inst_head ; inst_ptr != NULL ; inst_ptr = inst_ptr->next )
{
if (( timer_id == INST_TIMER_MONITOR ) && (inst_ptr->monitor_timer.tid == tid ))
{
return inst_ptr ;
}
else if (( timer_id == INST_TIMER_CONNECT ) && (inst_ptr->connect_timer.tid == tid ))
{
return inst_ptr ;
}
else if (( timer_id == INST_TIMER_VOTE ) && ( inst_ptr->vote_timer.tid == tid ))
{
return inst_ptr ;
}
else if (( timer_id == INST_TIMER_INIT ) && ( inst_ptr->init_timer.tid == tid ))
{
return inst_ptr ;
}
else if (( timer_id == INST_TIMER_RECONNECT ) && ( inst_ptr->reconnect_timer.tid == tid ))
{
return inst_ptr ;
}
if (( inst_ptr->next == NULL ) || ( inst_ptr == inst_tail ))
break ;
}
}
}
return static_cast<struct inst *>(NULL);
}
/* Get an instance's heartbeat fault reporting state */
bool guestInstClass::get_reporting_state ( string uuid )
{
guestInstClass::inst * inst_ptr = guestInstClass::getInst ( uuid );
if ( inst_ptr )
{
return ( inst_ptr->instance.heartbeat.reporting );
}
else
{
wlog ("uuid not found '%s'\n", uuid.c_str());
}
return ( false );
}
/* Set an instances heartbeat fault reporting state */
int guestInstClass::set_reporting_state( string uuid, bool reporting )
{
guestInstClass::inst * inst_ptr = guestInstClass::getInst ( uuid );
if ( inst_ptr )
{
inst_ptr->instance.heartbeat.reporting = reporting ;
}
else
{
wlog ("uuid not found '%s'\n", uuid.c_str());
return (FAIL_NOT_FOUND) ;
}
return (PASS);
}
/*****************************************************************************
*
* Name : print_all_instances
*
* Purpose: Print a summary of the instances that are currently provisioned
*
*****************************************************************************/
void guestInstClass::print_all_instances ( void )
{
bool found = false;
int i = 0 ;
if ( inst_head )
{
struct inst * inst_ptr ;
for ( inst_ptr = inst_head ; inst_ptr != NULL ; inst_ptr = inst_ptr->next )
{
ilog ("%2d %s Heartbeat: Notify:%c Failures:%d\n", i,
log_prefix(&inst_ptr->instance).c_str(),
inst_ptr->instance.heartbeat.reporting ? 'Y':'n',
inst_ptr->instance.heartbeat.failures);
found = true ;
i++ ;
if (( inst_ptr->next == NULL ) || ( inst_ptr == inst_tail ))
break ;
}
}
if ( found == false )
{
ilog ("no instances provisioned\n");
}
}
/*****************************************************************************
*
* Name : print_instances (private)
*
*****************************************************************************/
void guestInstClass::print_instances ( void )
{
print_all_instances();
}
/*****************************************************************************
* Memory Dump Stuff *
*****************************************************************************/
void guestInstClass::print_node_info ( void )
{
fflush (stdout);
fflush (stderr);
}
void guestInstClass::mem_log_info ( void )
{
char str[MAX_MEM_LOG_DATA] ;
snprintf (&str[0], MAX_MEM_LOG_DATA, "Instances:%d Allocs:%d Memory:%d\n", instances, memory_allocs, memory_used );
mem_log (str);
}
void mem_log_delimit_host ( void )
{
char str[MAX_MEM_LOG_DATA] ;
snprintf (&str[0], MAX_MEM_LOG_DATA, "-------------------------------------------------------------\n");
mem_log (str);
}
void guestInstClass::mem_log_inst_info ( void )
{
char str[MAX_MEM_LOG_DATA] ;
struct inst * inst_ptr = static_cast<struct inst *>(NULL) ;
for ( inst_ptr = inst_head ; inst_ptr != NULL ; inst_ptr = inst_ptr->next )
{
snprintf (&str[0], MAX_MEM_LOG_DATA, "Name : %s %s (%s)\n",
inst_ptr->instance.name.data(),
inst_ptr->instance.uuid.data(),
inst_ptr->instance.inst.data());
mem_log (str);
snprintf (&str[0], MAX_MEM_LOG_DATA, "Action: %8d Connect:%2d Message:%2d Delay:%d secs\n",
inst_ptr->action,
inst_ptr->connectStage,
inst_ptr->messageStage,
inst_ptr->instance.connect_wait_in_secs);
mem_log (str);
snprintf (&str[0], MAX_MEM_LOG_DATA, "State : Reporting: %c Failures: %d Failed: %c\n",
inst_ptr->instance.heartbeat.reporting ? 'Y' : 'n',
inst_ptr->instance.heartbeat.failures,
inst_ptr->instance.heartbeat.failed ? 'Y' : 'n' );
mem_log (str);
snprintf (&str[0], MAX_MEM_LOG_DATA, "Setup : Select :%2d Channel OK: %c hbState:%s vnState:%s\n",
inst_ptr->instance.chan_fd,
inst_ptr->instance.chan_ok ? 'Y' : 'n' ,
hb_get_state_name(inst_ptr->instance.hbState),
hb_get_state_name(inst_ptr->instance.vnState));
mem_log (str);
snprintf (&str[0], MAX_MEM_LOG_DATA, "Oper : Connected: %c Heartbeating: %c\n",
inst_ptr->instance.connected ? 'Y' : 'n',
inst_ptr->instance.heartbeating ? 'Y' : 'n');
mem_log (str);
mem_log_delimit_host();
/* exit if this happens to be the last one in the list */
if (( inst_ptr->next == NULL ) || ( inst_ptr == inst_tail ))
break ;
}
if ( inst_head == NULL )
{
snprintf (&str[0], MAX_MEM_LOG_DATA, "no instances\n");
mem_log (str);
}
}
void guestInstClass::memDumpAllState ( void )
{
mem_log_info ( );
mem_log_delimit_host ();
mem_log_inst_info ();
}