
This decouples the build and packaging of guest-server, guest-agent from mtce, by splitting guest component into stx-nfv repo. This leaves existing C++ code, scripts, and resource files untouched, so there is no functional change. Code refactoring is beyond the scope of this update. Makefiles were modified to include devel headers directories /usr/include/mtce-common and /usr/include/mtce-daemon. This ensures there is no contamination with other system headers. The cgts-mtce-common package is renamed and split into: - repo stx-metal: mtce-common, mtce-common-dev - repo stx-metal: mtce - repo stx-nfv: mtce-guest - repo stx-ha: updates package dependencies to mtce-pmon for service-mgmt, sm, and sm-api mtce-common: - contains common and daemon shared source utility code mtce-common-dev: - based on mtce-common, contains devel package required to build mtce-guest and mtce - contains common library archives and headers mtce: - contains components: alarm, fsmon, fsync, heartbeat, hostw, hwmon, maintenance, mtclog, pmon, public, rmon mtce-guest: - contains guest component guest-server, guest-agent Story: 2002829 Task: 22748 Change-Id: I9c7a9b846fd69fd566b31aa3f12a043c08f19f1f Signed-off-by: Jim Gauld <james.gauld@windriver.com>
377 lines
13 KiB
C++
377 lines
13 KiB
C++
/*
|
|
* Copyright (c) 2013-2017 Wind River Systems, Inc.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* Wind River Titanium Cloud Platform remote logging Monitor Handler
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <dirent.h>
|
|
#include <list>
|
|
#include <map>
|
|
#include <syslog.h>
|
|
#include <sys/wait.h>
|
|
#include <time.h>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <ctime>
|
|
#include <vector> /* for storing dynamic resource names */
|
|
#include <dirent.h>
|
|
#include <signal.h>
|
|
#include "rmon.h" /* rmon header file */
|
|
#include "rmonHttp.h" /* for rmon HTTP libEvent utilties */
|
|
#include "rmonApi.h"
|
|
#include <algorithm>
|
|
#include <fcntl.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/syscall.h>
|
|
#include <cctype>
|
|
#include <pthread.h>
|
|
#include <linux/rtnetlink.h> /* for ... RTMGRP_LINK */
|
|
#include "nlEvent.h" /* for ... open_netlink_socket */
|
|
#include "nodeEvent.h" /* for inotify */
|
|
#include <json-c/json.h> /* for ... json-c json string parsing */
|
|
#include "jsonUtil.h"
|
|
#include <cstring>
|
|
#include <iomanip>
|
|
#include <cstdlib>
|
|
|
|
static libEvent_type remoteLoggingAudit; // for system remotelogging-show
|
|
|
|
static inline SFmAlarmDataT
|
|
create_remoteLogging_controller_connectivity_alarm (SFmAlarmDataT data,
|
|
AlarmFilter filter)
|
|
{
|
|
snprintf (data.alarm_id, FM_MAX_BUFFER_LENGTH, "%s",
|
|
filter.alarm_id);
|
|
data.alarm_state = FM_ALARM_STATE_SET;
|
|
snprintf(data.entity_type_id, FM_MAX_BUFFER_LENGTH, "system.host");
|
|
snprintf(data.entity_instance_id, FM_MAX_BUFFER_LENGTH, "%s",
|
|
filter.entity_instance_id);
|
|
data.severity = FM_ALARM_SEVERITY_MINOR;
|
|
snprintf(data.reason_text, sizeof(data.reason_text),
|
|
"Controller cannot establish connection with remote logging server.");
|
|
data.alarm_type = FM_ALARM_COMM;
|
|
data.probable_cause = FM_ALARM_COMM_SUBSYS_FAILURE;
|
|
data.service_affecting = FM_FALSE;
|
|
snprintf(data.proposed_repair_action, sizeof(data.proposed_repair_action),
|
|
"Ensure Remote Log Server IP is reachable from Controller through "
|
|
"OAM interface; otherwise contact next level of support.");
|
|
return data;
|
|
}
|
|
|
|
// alarm data for all remote loggin alarms
|
|
static SFmAlarmDataT alarmData;
|
|
|
|
int rmonHdlr_remotelogging_query (resource_config_type * ptr);
|
|
|
|
// this is used to create a buffer to store output from a command
|
|
// that gets the connection status of a port.
|
|
// the command filters the /proc/net/tcp(udp) files leaving only the status
|
|
// you generally expect a 1 character integer value for the status
|
|
#define CONNECTION_STATUS_COMMAND_OUTPUT_BUFFER_SIZE 8
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Name : rmonHdlr_remotelogging_handler
|
|
*
|
|
* Purpose : Handles the remote logging response message
|
|
*
|
|
*****************************************************************************/
|
|
void rmonHdlr_remotelogging_handler ( struct evhttp_request *req, void *arg )
|
|
{
|
|
|
|
if ( !req )
|
|
{
|
|
elog (" Request Timeout\n");
|
|
remoteLoggingAudit.status = FAIL_TIMEOUT ;
|
|
goto _remote_logging_handler_done ;
|
|
}
|
|
|
|
remoteLoggingAudit.status = rmonHttpUtil_status ( remoteLoggingAudit ) ;
|
|
if ( remoteLoggingAudit.status == HTTP_NOTFOUND )
|
|
{
|
|
|
|
goto _remote_logging_handler_done ;
|
|
}
|
|
else if ( remoteLoggingAudit.status != PASS )
|
|
{
|
|
dlog (" remote logging HTTP Request Failed (%d)\n",
|
|
remoteLoggingAudit.status);
|
|
|
|
goto _remote_logging_handler_done ;
|
|
}
|
|
|
|
if ( rmonHttpUtil_get_response ( remoteLoggingAudit ) != PASS )
|
|
goto _remote_logging_handler_done ;
|
|
|
|
_remote_logging_handler_done:
|
|
/* This is needed to get out of the loop */
|
|
event_base_loopbreak((struct event_base *)arg);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Name : rmonHdlr_remotelogging_query
|
|
*
|
|
* Purpose : Send a HTTP remotelogging show request
|
|
*
|
|
*****************************************************************************/
|
|
|
|
int rmonHdlr_remotelogging_query (resource_config_type * ptr)
|
|
{
|
|
|
|
// we want this handler to run once every 5 minutes
|
|
// rmon currently runs once every 30 seconds
|
|
static bool first_execution = true;
|
|
static int exec_counter = 9;
|
|
exec_counter = (exec_counter + 1) % 10;
|
|
if(exec_counter != 0)
|
|
{
|
|
return 0;
|
|
}
|
|
// extract the ip and port for the remote logging server
|
|
FILE* pFile;
|
|
string remote_ip_address = "";
|
|
string remote_port = "";
|
|
string transport_type = "";
|
|
string line;
|
|
bool feature_enabled = false;
|
|
|
|
std::ifstream syslog_config("/etc/syslog-ng/syslog-ng.conf");
|
|
// look for this line in the config file:
|
|
// destination remote_log_server {tcp("128.224.140.219" port(514));};
|
|
while(std::getline(syslog_config, line))
|
|
{
|
|
// include remotelogging.conf is present if the feature is enabled
|
|
if(line.find("@include \"remotelogging.conf\"") == 0)
|
|
{
|
|
feature_enabled = true;
|
|
}
|
|
if(line.find("destination remote_log_server") != 0)
|
|
{
|
|
continue;
|
|
}
|
|
int start = line.find("{") + 1;
|
|
int end = line.find("(", start + 1);
|
|
transport_type= line.substr(start, end - start);
|
|
start = line.find("\"") + 1;
|
|
end = line.find("\"", start + 1);
|
|
remote_ip_address = line.substr(start, end - start);
|
|
start = line.find("port(") + 5;
|
|
end = line.find(")", start + 1);
|
|
remote_port = line.substr(start, end - start);
|
|
}
|
|
|
|
syslog_config.close();
|
|
|
|
// cleanup of any alarms if the remotelogging feature is not enabled
|
|
// this is important for when users turn off the remote logging feature when an alarm is active
|
|
// if the line containing this information is not in config, remote logging is not used
|
|
if(remote_ip_address.empty() || remote_port.empty() || transport_type.empty() || !feature_enabled)
|
|
{
|
|
// currently, only controllers raise alarms
|
|
if(is_controller())
|
|
{
|
|
// clear any raised alarms
|
|
if(ptr->alarm_raised)
|
|
{
|
|
rmon_ctrl_type* _rmon_ctrl_ptr;
|
|
_rmon_ctrl_ptr = get_rmon_ctrl_ptr();
|
|
AlarmFilter alarmFilter;
|
|
snprintf(alarmFilter.alarm_id, FM_MAX_BUFFER_LENGTH, REMOTE_LOGGING_CONTROLLER_CONNECTIVITY_ALARM_ID);
|
|
snprintf(alarmFilter.entity_instance_id, FM_MAX_BUFFER_LENGTH,
|
|
"%s", _rmon_ctrl_ptr->my_hostname);
|
|
int rc;
|
|
if ((rc = rmon_fm_clear(&alarmFilter)) != FM_ERR_OK)
|
|
{
|
|
wlog ("Failed to clear stale remotelogging connectivity alarm for"
|
|
"entity instance id: %s error: %d",
|
|
alarmFilter.entity_instance_id, rc);
|
|
}
|
|
else
|
|
{
|
|
ptr->alarm_raised = false;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// construct the remote logging server IP string
|
|
// the files being looked at(/proc/net/tcp(udp)) uses hex values, so convert the
|
|
// string we got from the config file to that format
|
|
// - convert all numbers to hex and hex to capitals
|
|
// reverse ordering of the "ipv4" values
|
|
std::stringstream config_ip(remote_ip_address); // the ip string from the config file
|
|
std::stringstream proc_file_ip; // the ip string formatted to compare to /proc/net/tcp(udp)
|
|
int ipv = 4;
|
|
|
|
// IPv4
|
|
if(remote_ip_address.find(".") != string::npos)
|
|
{
|
|
// ipv4 example: config file 10.10.10.45, /proc/net/tcp 2D0A0A0A
|
|
int a, b, c, d;
|
|
char trash;
|
|
config_ip >> a >> trash >> b >> trash >> c >> trash >> d;
|
|
proc_file_ip << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << d;
|
|
proc_file_ip << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << c;
|
|
proc_file_ip << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << b;
|
|
proc_file_ip << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << a;
|
|
proc_file_ip << ":";
|
|
proc_file_ip << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << atoi(remote_port.c_str());
|
|
}
|
|
// IPv6
|
|
else if (remote_ip_address.find(":") != string::npos)
|
|
{
|
|
ipv = 6;
|
|
// ipv6 example: config file 0:0:0:0:ffff:0:80e0:8d6c , /proc/net/tcp6 0000000000000000FFFF00006C8D0E080
|
|
int a, b, c, d;
|
|
char trash;
|
|
// first, the hex that are in the same order from config to /proc/net/...
|
|
for(int i = 0; i < 6; i++)
|
|
{
|
|
config_ip >> std::hex >> a >> trash;
|
|
proc_file_ip << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << a;
|
|
}
|
|
|
|
// now the hex that needs to be re ordered
|
|
config_ip >> std::hex >> a >> trash >> c;
|
|
b = (a & 0xFF);
|
|
a = (a >> 8);
|
|
d = (c & 0xFF);
|
|
c = (c >> 8);
|
|
|
|
proc_file_ip << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << d;
|
|
proc_file_ip << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << c;
|
|
proc_file_ip << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << b;
|
|
proc_file_ip << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << a;
|
|
proc_file_ip << ":";
|
|
proc_file_ip << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << atoi(remote_port.c_str());
|
|
}
|
|
// garbage
|
|
else
|
|
{
|
|
wlog("Unrecognized ip format in syslog config file\n");
|
|
}
|
|
|
|
string connection_check_filename;
|
|
if(transport_type == "tcp")
|
|
{
|
|
connection_check_filename = "tcp";
|
|
}
|
|
else if (transport_type == "udp")
|
|
{
|
|
connection_check_filename = "udp";
|
|
}
|
|
// todo: eventually we will have TLS as a transport type and potentially others
|
|
else
|
|
{
|
|
wlog("Unrecognized remote logging transport type: %s \n", transport_type.c_str());
|
|
}
|
|
|
|
if(ipv == 6)
|
|
{
|
|
connection_check_filename = connection_check_filename + "6";
|
|
}
|
|
|
|
std::string command = "cat /proc/net/" + connection_check_filename +" | awk '{print $3 \" \" $4}' | grep " \
|
|
+ proc_file_ip.str() + " | awk '{print $2}'";
|
|
if(!(pFile = popen(command.c_str(), "r")))
|
|
{
|
|
elog ("Failed to execute command for getting remotelogging tcp port status");
|
|
}
|
|
else
|
|
{
|
|
char cmd_output[CONNECTION_STATUS_COMMAND_OUTPUT_BUFFER_SIZE];
|
|
int connection_status = 0;
|
|
rmon_ctrl_type* _rmon_ctrl_ptr;
|
|
_rmon_ctrl_ptr = get_rmon_ctrl_ptr();
|
|
AlarmFilter alarmFilter;
|
|
SFmAlarmDataT active_alarm;
|
|
|
|
memset(cmd_output, 0, CONNECTION_STATUS_COMMAND_OUTPUT_BUFFER_SIZE);
|
|
fgets((char*) &cmd_output, CONNECTION_STATUS_COMMAND_OUTPUT_BUFFER_SIZE, pFile);
|
|
pclose(pFile);
|
|
std::stringstream s(cmd_output);
|
|
s >> std::hex >> connection_status;
|
|
|
|
snprintf(alarmFilter.alarm_id, FM_MAX_BUFFER_LENGTH, REMOTE_LOGGING_CONTROLLER_CONNECTIVITY_ALARM_ID);
|
|
snprintf(alarmFilter.entity_instance_id, FM_MAX_BUFFER_LENGTH,
|
|
"%s", _rmon_ctrl_ptr->my_hostname);
|
|
|
|
if(first_execution)
|
|
{
|
|
if (fm_get_fault (&alarmFilter, &active_alarm) == FM_ERR_OK)
|
|
{
|
|
ptr->alarm_raised = true;
|
|
}
|
|
else
|
|
{
|
|
ptr->alarm_raised = false;
|
|
}
|
|
}
|
|
if(connection_status == 1)
|
|
{
|
|
if(is_controller())
|
|
{
|
|
// connection is established, clear the alarm
|
|
if(ptr->alarm_raised)
|
|
{
|
|
int rc;
|
|
if ((rc = rmon_fm_clear(&alarmFilter)) != FM_ERR_OK)
|
|
{
|
|
wlog ("Failed to clear stale remotelogging connectivity alarm for"
|
|
"entity instance id: %s error: %d",
|
|
alarmFilter.entity_instance_id, rc);
|
|
}
|
|
else
|
|
{
|
|
ptr->alarm_raised = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(is_controller())
|
|
{
|
|
// connection is not established, raise an alarm
|
|
if (!ptr->alarm_raised)
|
|
{
|
|
int rc;
|
|
alarmData = \
|
|
create_remoteLogging_controller_connectivity_alarm(alarmData,
|
|
alarmFilter);
|
|
|
|
if ((rc = rmon_fm_set(&alarmData, NULL)) != FM_ERR_OK)
|
|
{
|
|
wlog("Failed to create alarm %s for entity instance id: %s"
|
|
"error: %d \n", REMOTE_LOGGING_CONTROLLER_CONNECTIVITY_ALARM_ID,
|
|
alarmData.entity_instance_id, (int) rc);
|
|
}
|
|
else
|
|
{
|
|
ptr->alarm_raised = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
elog ("%s cannot connect to remote log server", _rmon_ctrl_ptr->my_hostname);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|