enh(iplabel): Add new filters to scenarios selection and fix identifiers management

Refs:CTOR-1107
This commit is contained in:
Evan-Adam 2025-01-13 10:41:58 +01:00 committed by GitHub
parent 942c0b7940
commit 47cf7c9e8b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 1219 additions and 120 deletions

View File

@ -43,14 +43,19 @@ sub new {
if (!defined($options{noptions})) { if (!defined($options{noptions})) {
$options{options}->add_options(arguments => { $options{options}->add_options(arguments => {
'api-username:s' => { name => 'api_username' }, 'api-username:s' => { name => 'api_username' },
'api-password:s' => { name => 'api_password' }, 'api-password:s' => { name => 'api_password' },
'hostname:s' => { name => 'hostname' }, 'hostname:s' => { name => 'hostname' },
'port:s' => { name => 'port' }, 'port:s' => { name => 'port' },
'proto:s' => { name => 'proto' }, 'proto:s' => { name => 'proto' },
'timeout:s' => { name => 'timeout' }, 'timeout:s' => { name => 'timeout' },
'url-path:s' => { name => 'url_path' }, 'url-path:s' => { name => 'url_path' },
'authent-endpoint' => { name => 'authent_endpoint' } 'filter-id:s' => { name => 'filter_id' },
'filter-name:s' => { name => 'filter_name' },
'filter-workspaceid:s' => { name => 'filter_workspaceid' },
'filter-siteid:s' => { name => 'filter_siteid' },
'filter-status:s@' => { name => 'filter_status' },
'authent-endpoint' => { name => 'authent_endpoint' }
}); });
} }
$options{options}->add_help(package => __PACKAGE__, sections => 'REST API OPTIONS', once => 1); $options{options}->add_help(package => __PACKAGE__, sections => 'REST API OPTIONS', once => 1);
@ -170,6 +175,57 @@ sub get_access_token {
return $access_token; return $access_token;
} }
sub request_scenarios_status{
my ($self, %options) = @_;
my $status_filter = {};
my @get_param;
if (defined($self->{option_results}->{filter_status}) && $self->{option_results}->{filter_status}[0] ne '') {
$status_filter->{statusFilter} = $self->{option_results}->{filter_status};
}
if (defined($self->{option_results}->{filter_workspaceid}) && $self->{option_results}->{filter_workspaceid} ne '') {
push(@get_param, "workspaceId=$self->{option_results}->{filter_workspaceid}");
}
if (defined($self->{option_results}->{filter_siteid}) && $self->{option_results}->{filter_siteid} ne '') {
push(@get_param, "siteId=$self->{option_results}->{filter_siteid}");
}
my $results = $self->request_api(
endpoint => '/results-api/scenarios/status',
method => 'POST',
post_body => $status_filter,
get_param => \@get_param,
);
if (ref($results) eq "HASH" ) {
if (defined($results->{message})) {
$self->{output}->add_option_msg(short_msg => "Cannot get scenarios : " . $results->{message});
$self->{output}->option_exit();
}
if (defined($results->{error})) {
$self->{output}->add_option_msg(short_msg => "Cannot get scenarios : " . $results->{error});
$self->{output}->option_exit();
}
$self->{output}->add_option_msg(short_msg => "Cannot get scenarios due to an unknown error, please use the --debug option to find more information");
$self->{output}->option_exit();
}
my @scenarios;
for my $scenario (@$results) {
if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' &&
$scenario->{scenarioName} !~ /$self->{option_results}->{filter_name}/) {
$self->{output}->output_add(long_msg => "skipping scenario '" . $scenario->{scenarioName} . "': no matching filter.", debug => 1);
next;
}
if (defined($self->{option_results}->{filter_id}) && $self->{option_results}->{filter_id} ne '' &&
$scenario->{scenarioId} !~ /$self->{option_results}->{filter_id}/) {
$self->{output}->output_add(long_msg => "skipping scenario '" . $scenario->{scenarioName} . "': no matching filter.", debug => 1);
next;
}
push(@scenarios, $scenario);
}
return \@scenarios;
}
sub request_api { sub request_api {
my ($self, %options) = @_; my ($self, %options) = @_;
@ -195,7 +251,7 @@ sub request_api {
$json = JSON::XS->new->utf8->decode($response); $json = JSON::XS->new->utf8->decode($response);
}; };
if ($@) { if ($@) {
$self->{output}->add_option_msg(short_msg => "Cannot decode Vault JSON response: $@"); $self->{output}->add_option_msg(short_msg => "Cannot decode ekara JSON response: $@");
$self->{output}->option_exit(); $self->{output}->option_exit();
}; };
@ -209,11 +265,11 @@ __END__
=head1 NAME =head1 NAME
Ip-Label Ekara Rest API ip-label Ekara Rest API
=head1 REST API OPTIONS =head1 REST API OPTIONS
Ip-Label Ekara Rest API ip-label Ekara Rest API
=over 8 =over 8
@ -237,6 +293,37 @@ Set username.
Set password. Set password.
=item B<--filter-id>
Filter by monitor ID (can be a regexp).
=item B<--filter-name>
Filter by monitor name (can be a regexp).
=item B<--filter-status>
Filter by numeric status (can be multiple).
0 => 'Unknown',
1 => 'Success',
2 => 'Failure',
3 => 'Aborted',
4 => 'No execution',
5 => 'No execution',
6 => 'Stopped',
7 => 'Excluded',
8 => 'Degraded'
Example: --filter-status='1,2'
=item B<--filter-workspaceid>
Filter scenario to check by workspace id.
=item B<--filter-siteid>
Filter scenario to check by site id.
=item B<--timeout> =item B<--timeout>
Set timeout in seconds (default: 10). Set timeout in seconds (default: 10).

View File

@ -36,6 +36,7 @@ sub custom_status_output {
sub custom_duration_output { sub custom_duration_output {
my ($self, %options) = @_; my ($self, %options) = @_;
if ($self->{result_values}->{status} =~ 'Open') { if ($self->{result_values}->{status} =~ 'Open') {
return sprintf( return sprintf(
'start time: %s, duration: %s', 'start time: %s, duration: %s',
@ -145,8 +146,6 @@ sub new {
bless $self, $class; bless $self, $class;
$options{options}->add_options(arguments => { $options{options}->add_options(arguments => {
'filter-id:s' => { name => 'filter_id' },
'filter-name:s' => { name => 'filter_name' },
'ignore-closed' => { name => 'ignore_closed' }, 'ignore-closed' => { name => 'ignore_closed' },
'timeframe:s' => { name => 'timeframe'} 'timeframe:s' => { name => 'timeframe'}
}); });
@ -176,23 +175,10 @@ my $status_mapping = {
sub manage_selection { sub manage_selection {
my ($self, %options) = @_; my ($self, %options) = @_;
my $results = $options{custom}->request_api( my $results = $options{custom}->request_scenarios_status();
endpoint => '/results-api/scenarios/status',
method => 'POST',
);
my $scenarios_list = {}; my $scenarios_list = {};
foreach (@$results) { foreach (@$results) {
if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' &&
$_->{scenarioName} !~ /$self->{option_results}->{filter_name}/) {
$self->{output}->output_add(long_msg => "skipping scenario '" . $_->{scenarioName} . "': no matching filter.", debug => 1);
next;
}
if (defined($self->{option_results}->{filter_id}) && $self->{option_results}->{filter_id} ne '' &&
$_->{scenarioId} !~ /$self->{option_results}->{filter_id}/) {
$self->{output}->output_add(long_msg => "skipping scenario '" . $_->{scenarioName} . "': no matching filter.", debug => 1);
next;
}
push @{$scenarios_list->{scenarioIds}}, $_->{scenarioId}; push @{$scenarios_list->{scenarioIds}}, $_->{scenarioId};
} }
@ -212,6 +198,10 @@ sub manage_selection {
); );
} }
else{
$self->{output}->add_option_msg(short_msg => "No scenarios found, can't search for incidents. Please check filters.");
$self->{output}->option_exit();
}
$self->{global}->{total} = 0; $self->{global}->{total} = 0;
@ -257,7 +247,7 @@ __END__
=head1 MODE =head1 MODE
Check IP Label Ekara incidents. Check Ekara incidents.
=over 8 =over 8
@ -266,14 +256,6 @@ Check IP Label Ekara incidents.
Set timeframe period in seconds. (default: 900) Set timeframe period in seconds. (default: 900)
Example: --timeframe='3600' will check the last hour Example: --timeframe='3600' will check the last hour
=item B<--filter-id>
Filter by monitor ID (can be a regexp).
=item B<--filter-name>
Filter by monitor name (can be a regexp).
=item B<--ignore-closed> =item B<--ignore-closed>
Ignore solved incidents within the timeframe. Ignore solved incidents within the timeframe.

View File

@ -34,16 +34,6 @@ sub custom_status_output {
return sprintf('status: %s (%s)', $self->{result_values}->{status}, $self->{result_values}->{num_status}); return sprintf('status: %s (%s)', $self->{result_values}->{status}, $self->{result_values}->{num_status});
} }
sub custom_date_output {
my ($self, %options) = @_;
return sprintf(
'last execution: %s (%s ago)',
$self->{result_values}->{lastexec},
centreon::plugins::misc::change_seconds(value => $self->{result_values}->{freshness})
);
}
sub prefix_scenario_output { sub prefix_scenario_output {
my ($self, %options) = @_; my ($self, %options) = @_;
@ -131,9 +121,7 @@ sub new {
bless $self, $class; bless $self, $class;
$options{options}->add_options(arguments => { $options{options}->add_options(arguments => {
'filter-id:s' => { name => 'filter_id' },
'filter-name:s' => { name => 'filter_name' },
'filter-status:s@' => { name => 'filter_status' },
'filter-type:s' => { name => 'filter_type' }, 'filter-type:s' => { name => 'filter_type' },
'timeframe:s' => { name => 'timeframe'} 'timeframe:s' => { name => 'timeframe'}
}); });
@ -162,35 +150,14 @@ my $status_mapping = {
sub manage_selection { sub manage_selection {
my ($self, %options) = @_; my ($self, %options) = @_;
my $results = $options{custom}->request_scenarios_status();
my $status_filter = {};
if (defined($self->{option_results}->{filter_status}) && $self->{option_results}->{filter_status}[0] ne '') {
$status_filter->{statusFilter} = $self->{option_results}->{filter_status};
}
my $results = $options{custom}->request_api(
endpoint => '/results-api/scenarios/status',
method => 'POST',
post_body => $status_filter
);
my $time = time(); my $time = time();
my $start_date = POSIX::strftime('%Y-%m-%dT%H:%M:%SZ', gmtime($time - $self->{timeframe})); my $start_date = POSIX::strftime('%Y-%m-%dT%H:%M:%SZ', gmtime($time - $self->{timeframe}));
my $end_date = POSIX::strftime('%Y-%m-%dT%H:%M:%SZ', gmtime($time)); my $end_date = POSIX::strftime('%Y-%m-%dT%H:%M:%SZ', gmtime($time));
foreach (@$results) { foreach my $scenario (@$results) {
if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' &&
$_->{scenarioName} !~ /$self->{option_results}->{filter_name}/) {
$self->{output}->output_add(long_msg => "skipping scenario '" . $_->{scenarioName} . "': no matching filter.", debug => 1);
next;
}
if (defined($self->{option_results}->{filter_id}) && $self->{option_results}->{filter_id} ne '' &&
$_->{scenarioId} !~ /$self->{option_results}->{filter_id}/) {
$self->{output}->output_add(long_msg => "skipping scenario '" . $_->{scenarioName} . "': no matching filter.", debug => 1);
next;
}
my $scenario_detail = $options{custom}->request_api( my $scenario_detail = $options{custom}->request_api(
endpoint => '/results-api/results/' . $_->{scenarioId}, endpoint => '/results-api/results/' . $scenario->{scenarioId},
method => 'POST', method => 'POST',
get_param => [ get_param => [
'from=' . $start_date, 'from=' . $start_date,
@ -200,35 +167,38 @@ sub manage_selection {
if (defined($self->{option_results}->{filter_type}) && $self->{option_results}->{filter_type} ne '' && if (defined($self->{option_results}->{filter_type}) && $self->{option_results}->{filter_type} ne '' &&
$scenario_detail->{infos}->{plugin_id} !~ /$self->{option_results}->{filter_type}/i) { $scenario_detail->{infos}->{plugin_id} !~ /$self->{option_results}->{filter_type}/i) {
$self->{output}->output_add(long_msg => "skipping scenario '" . $_->{scenarioName} . "': no matching filter.", debug => 1); $self->{output}->output_add(long_msg => "skipping scenario '" . $scenario->{scenarioName} . "': no matching filter.", debug => 1);
next; next;
} }
$self->{scenarios}->{ $_->{scenarioName} } = { $self->{scenarios}->{ $scenario->{scenarioName} } = {
display => $_->{scenarioName}, display => $scenario->{scenarioName},
global => { global => {
display => $_->{scenarioName}, display => $scenario->{scenarioName},
id => $_->{scenarioId}, id => $scenario->{scenarioId},
num_status => $_->{currentStatus}, num_status => $scenario->{currentStatus},
status => $status_mapping->{$_->{currentStatus}}, status => $status_mapping->{$scenario->{currentStatus}} // 'Unknown',
} }
}; };
if (!defined $scenario_detail->{results} or scalar(@{$scenario_detail->{results}}) <= 0) {
foreach my $kpi (@{$scenario_detail->{kpis}}) { $self->{output}->add_option_msg(short_msg => "Scenario '" . $scenario->{scenarioName} . "' Don't have any performance data, please try to add a bigger timeframe");
$self->{scenarios}->{ $_->{scenarioName} }->{global}->{$kpi->{label}} = $kpi->{value}; next;
} }
$self->{scenarios}->{ $_->{scenarioName} }->{steps_index}->{0} = 'Default'; foreach my $kpi (@{$scenario_detail->{kpis}}) {
$self->{scenarios}->{ $scenario->{scenarioName} }->{global}->{$kpi->{label}} = $kpi->{value};
}
$self->{scenarios}->{ $scenario->{scenarioName} }->{steps_index}->{0} = 'Default';
if ($scenario_detail->{infos}->{info}->{hasStep}) { if ($scenario_detail->{infos}->{info}->{hasStep}) {
foreach my $steps (@{$scenario_detail->{steps}}) { foreach my $steps (@{$scenario_detail->{steps}}) {
$self->{scenarios}->{ $_->{scenarioName} }->{steps_index}->{$steps->{index}} = $steps->{name}; $self->{scenarios}->{ $scenario->{scenarioName} }->{steps_index}->{$steps->{index} - 1} = $steps->{name};
} }
} }
foreach my $step_metrics (@{$scenario_detail->{results}}) { foreach my $step_metrics (@{$scenario_detail->{results}}) {
my $exec_time = str2time($step_metrics->{planningTime}, 'GMT'); my $exec_time = str2time($step_metrics->{planningTime}, 'GMT');
$self->{scenarios}->{ $_->{scenarioName} }->{steps}->{ $self->{scenarios}->{ $_->{scenarioName} }->{steps_index}->{ $step_metrics->{stepId} } }->{ $step_metrics->{metric} } = $step_metrics->{value}; $self->{scenarios}->{ $scenario->{scenarioName} }->{steps}->{ $self->{scenarios}->{ $scenario->{scenarioName} }->{steps_index}->{ $step_metrics->{stepId} } }->{ $step_metrics->{metric} } = $step_metrics->{value};
$self->{scenarios}->{ $_->{scenarioName} }->{steps}->{ $self->{scenarios}->{ $_->{scenarioName} }->{steps_index}->{ $step_metrics->{stepId} } }->{last_exec} = POSIX::strftime('%d-%m-%Y %H:%M:%S %Z', localtime($exec_time)); $self->{scenarios}->{ $scenario->{scenarioName} }->{steps}->{ $self->{scenarios}->{ $scenario->{scenarioName} }->{steps_index}->{ $step_metrics->{stepId} } }->{last_exec} = POSIX::strftime('%d-%m-%Y %H:%M:%S %Z', localtime($exec_time));
$self->{scenarios}->{ $_->{scenarioName} }->{steps}->{ $self->{scenarios}->{ $_->{scenarioName} }->{steps_index}->{ $step_metrics->{stepId} } }->{display} = $self->{scenarios}->{ $_->{scenarioName} }->{steps_index}->{ $step_metrics->{stepId} }; $self->{scenarios}->{ $scenario->{scenarioName} }->{steps}->{ $self->{scenarios}->{ $scenario->{scenarioName} }->{steps_index}->{ $step_metrics->{stepId} } }->{display} = $self->{scenarios}->{ $scenario->{scenarioName} }->{steps_index}->{ $step_metrics->{stepId} };
} }
} }
@ -244,7 +214,7 @@ __END__
=head1 MODE =head1 MODE
Check IP Label Ekara scenarios. Check ip-label Ekara scenarios.
=over 8 =over 8
@ -253,28 +223,7 @@ Check IP Label Ekara scenarios.
Set timeframe period in seconds. (default: 900) Set timeframe period in seconds. (default: 900)
Example: --timeframe='3600' will check the last hour Example: --timeframe='3600' will check the last hour
=item B<--filter-id>
Filter by monitor ID (can be a regexp).
=item B<--filter-name>
Filter by monitor name (can be a regexp).
=item B<--filter-status>
Filter by numeric status (can be multiple).
0 => 'Unknown',
1 => 'Success',
2 => 'Failure',
3 => 'Aborted',
4 => 'No execution',
5 => 'No execution',
6 => 'Stopped',
7 => 'Excluded',
8 => 'Degraded'
Example: --filter-status='1,2'
=item B<--filter-type> =item B<--filter-type>

View File

@ -0,0 +1,36 @@
*** Settings ***
Documentation Check Iplabel incidents
Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource
Suite Setup Start Mockoon ${MOCKOON_JSON}
Suite Teardown Stop Mockoon
Test Timeout 120s
*** Variables ***
${MOCKOON_JSON} ${CURDIR}${/}monitoring-iplabel-ekara.json
${cmd} ${CENTREON_PLUGINS}
... --plugin=apps::monitoring::iplabel::ekara::restapi::plugin
... --hostname=localhost
... --api-username='username'
... --api-password='password'
... --port='3000'
... --proto='http'
*** Test Cases ***
incidents ${tc}
[Documentation] Check Iplabel scenarios
[Tags] monitoring iplabel restapi
${command} Catenate
... ${cmd}
... --mode=incidents
... ${extra_options}
Ctn Run Command And Check Result As Strings ${command} ${expected_result}
Examples: tc extra_options expected_result --
... 1 --filter-name='Centreon Demo Navigation|AKILA - .Web.' CRITICAL: Incident #25421291, Scenario 'Centreon Demo Navigation' severity: Critical - Incident #25421962, Scenario 'AKILA - (Web)' severity: Critical - Incident #25422458, Scenario 'Centreon Demo Navigation' severity: Critical - Incident #25423513, Scenario 'Centreon Demo Navigation' status: Open, severity: Critical | 'ekara.incidents.current.total.count'=4;;;0;
... 2 --filter-name='not a name' UNKNOWN: No scenarios found, can't search for incidents. Please check filters.

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,42 @@
*** Settings ***
Documentation Check Iplabel scenarios
Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource
Suite Setup Start Mockoon ${MOCKOON_JSON}
Suite Teardown Stop Mockoon
Test Timeout 120s
*** Variables ***
${MOCKOON_JSON} ${CURDIR}${/}monitoring-iplabel-ekara.json
${cmd} ${CENTREON_PLUGINS}
... --plugin=apps::monitoring::iplabel::ekara::restapi::plugin
... --hostname=localhost
... --api-username='username'
... --api-password='password'
... --port='3000'
... --proto='http'
*** Test Cases ***
scenario ${tc}
[Documentation] Check Iplabel scenarios
[Tags] monitoring iplabel restapi
${command} Catenate
... ${cmd}
... --mode=scenarios
... ${extra_options}
Ctn Run Command And Check Result As Strings ${command} ${expected_result}
Examples: tc extra_options expected_result --
... 1 --filter-name='Centreon Demo Navigation|Centreon Demo ping NA' --output-ignore-perfdata CRITICAL: Scenario 'Centreon Demo Navigation': status: Failure (2) WARNING: Scenario 'Centreon Demo ping NA': status: Degraded (8)
... 2 --filter-name='AKILA - Business App' OK: Scenario 'AKILA - Business App': status: Success (1), availability: 100%, time total all steps: 4280ms - All steps are ok | 'AKILA - Business App#scenario.availability.percentage'=100%;;;0;100 'AKILA - Business App#scenario.time.allsteps.total.milliseconds'=4280ms;;;0; 'AKILA - Business App~Dashboard 2#scenario.step.time.milliseconds'=898ms;;;0; 'AKILA - Business App~Dashboard 3#scenario.step.time.milliseconds'=848ms;;;0; 'AKILA - Business App~Run Chrome#scenario.step.time.milliseconds'=2534ms;;;0;
... 3 --filter-name='wrong currentstatus.*' WARNING: Scenario 'wrong currentstatus, no perfdata': status: Unknown (14) - Scenario 'wrong currentstatus, no perfdata' Don't have any performance data, please try to add a bigger timeframe
... 4 --filter-name='not a scenario name' UNKNOWN: No scenario found
... 5 --filter-id='09fe2561.*' --warning-time-total-allsteps='30' --output-ignore-perfdata WARNING: Scenario 'AKILA - (Web) ': time total all steps: 5822ms
... 6 --filter-status='2' --output-ignore-perfdata CRITICAL: Scenario 'Centreon Demo Navigation': status: Failure (2)
... 7 --filter-status='2' --filter-siteid='site' --filter-workspaceid='workspace' --output-ignore-perfdata CRITICAL: Scenario 'Centreon Demo Navigation': status: Failure (2)
... 8 --filter-type='not a scenario type' UNKNOWN: No scenario found
... 9 --api-password='Wrongpassword' --api-username='wrongUsername' UNKNOWN: Authentication endpoint returns error code 'Wrong email or password' (add --debug option for detailed message)

View File

@ -13,8 +13,10 @@ ACS
ADSL ADSL
Alcatel Alcatel
allCapacity allCapacity
allsteps
Ansible Ansible
--api-filter-orgs --api-filter-orgs
api.ip-label.net
api.meraki.com api.meraki.com
--api-password --api-password
--api-path --api-path
@ -31,6 +33,7 @@ AWSCLI
Backbox Backbox
base64 base64
blocked-by-uf blocked-by-uf
BPL
--cacert-file --cacert-file
cardtemperature cardtemperature
centreon centreon
@ -41,14 +44,8 @@ centreonvault
CloudWatch CloudWatch
connections-dhcp connections-dhcp
connections-dns connections-dns
cpu-utilization
cpu-utilization-1m
cpu-utilization-5m
cpu-utilization-5s
CPUs
connections-dhcp
connections-dns
cpu cpu
CPUs
cpu-utilization cpu-utilization
cpu-utilization-1m cpu-utilization-1m
cpu-utilization-5m cpu-utilization-5m
@ -64,11 +61,12 @@ dev
df df
--dfsr --dfsr
dfsrevent dfsrevent
--diskpath
--display-transform-dst --display-transform-dst
--display-transform-src --display-transform-src
--diskpath
dns-resolve-time dns-resolve-time
--dyn-mode --dyn-mode
Ekara
-EncodedCommand -EncodedCommand
env env
ESX ESX
@ -93,6 +91,7 @@ frsevent
HashiCorp HashiCorp
hashicorpvault hashicorpvault
HPE HPE
HTTPR
Huawei Huawei
ifAlias ifAlias
ifDesc ifDesc
@ -110,6 +109,7 @@ InterrupibleSleep
in-ucast in-ucast
iops iops
IpAddr IpAddr
ip-label
ipv4 ipv4
ipv6 ipv6
ISAM ISAM
@ -178,10 +178,10 @@ NTP
NVOS NVOS
--oid --oid
OID OID
OIDs
--oid-display --oid-display
--oid-extra-display --oid-extra-display
--oid-filter --oid-filter
OIDs
okta okta
oneaccess-sys-mib oneaccess-sys-mib
OpenMetrics OpenMetrics
@ -221,9 +221,9 @@ SkyHigh
SnapMirror SnapMirror
SnapMirrors SnapMirrors
SNMP SNMP
snmpd.conf
space-usage-prct space-usage-prct
--sql-errors-exit --sql-errors-exit
snmpd.conf
SSDCapacity SSDCapacity
SSH SSH
statefile statefile