* To address issues with scancore debugging, we needed a tool to purge old anvils and hosts from the database. The 'test.pl' in this commit contains the new logic that will be merged into tools/striker-purge-host shortly.

* Created Database->find_host_uuid_columns() and ->_find_column() to create a list of tables and column names in the proper order to allow deletion of foreign keys to that deeply nested primary keys can be deleted. Specifically, this was meant for hosts -> host_uuid and anvils -> anvil_uuid, though it should work for other tables.
* Updated html/jquery-ui-1.12.1/package.json to address CVE-2020-7729
* Fixed a bug in the temperature table's history procedure where temperature_weight wasn't being copied.
* Updated anvil-provision-server to support '--anvil' that can take either the anvil-uuid or anvil-name.
* Updated anvil-safe-stop to default the stop-reason to 'user'.

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 4 years ago
parent 3fb81c1a0a
commit f833c311ba
  1. 170
      Anvil/Tools/Database.pm
  2. 15
      Anvil/Tools/Words.pm
  3. 4
      html/jquery-ui-1.12.1/package.json
  4. 59
      notes
  5. 9
      scancore-agents/scan-ipmitool/scan-ipmitool
  6. 2
      share/anvil.sql
  7. 9
      share/words.xml
  8. 1
      tools/anvil-delete-server
  9. 21
      tools/anvil-provision-server
  10. 6
      tools/anvil-safe-stop
  11. 4
      tools/striker-purge-host

@ -23,6 +23,7 @@ my $THIS_FILE = "Database.pm";
# configure_pgsql
# connect
# disconnect
# find_host_uuid_columns
# get_alerts
# get_anvils
# get_bridges
@ -91,6 +92,7 @@ my $THIS_FILE = "Database.pm";
# update_host_status
# write
# _archive_table
# _find_column
# _find_behind_database
# _mark_database_as_behind
# _test_access
@ -1723,7 +1725,7 @@ sub connect
# For now, we just find which DBs are behind and let each agent deal with bringing their tables up to
# date.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"sys::database::connections" => $anvil->data->{sys}{database}{connections},
no_resync => $no_resync,
}});
@ -1828,6 +1830,102 @@ sub disconnect
}
=head2 find_host_uuid_columns
This looks through all ScanCore agent schemas, then all core tables and looks for tables with columns that end in C<< X_host_uuid >>. These are stored in an array, ordered such that you can delete records for a given host without deleting primary keys before all foreign keys are gone.
The array is stored in C<< sys::database::uuid_tables >>. Each array entry will be hash references with C<< table >> and C<< host_uuid_column >> keys containing the table name, and the C<< X_host_uuid >> column.
### NOTE: Don't sort the array! It's ordered for safe deletions.
$anvil->Database->find_host_uuid_columns();
foreach my $hash_ref (@{$anvil->data->{sys}{database}{uuid_tables}})
{
my $table = $hash_ref->{table};
my $host_uuid_column = $hash_ref->{host_uuid_column};
print "Table: [".$table."], host UUID column: [".$host_uuid_column."]\n";
}
The array reference is returned.
Parameters;
=head3 main_table (optional, default 'hosts')
This is the "parent" table, generally the top table with no further foreign keys above it.
=head3 search_column (optional, default 'host_uuid')
This is the UUID column used as a suffix in the parent table to search for.
=cut
sub find_host_uuid_columns
{
my $self = shift;
my $parameter = shift;
my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Database->find_host_uuid_columns()" }});
my $main_table = defined $parameter->{main_table} ? $parameter->{main_table} : "hosts";
my $search_column = defined $parameter->{search_column} ? $parameter->{search_column} : "host_uuid";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
main_table => $main_table,
search_column => $search_column,
}});
$anvil->data->{sys}{database}{uuid_tables} = [];
$anvil->ScanCore->_scan_directory({
debug => $debug,
directory => $anvil->data->{path}{directories}{scan_agents},
});
foreach my $agent_name (sort {$a cmp $b} keys %{$anvil->data->{scancore}{agent}})
{
my $sql_path = $anvil->data->{scancore}{agent}{$agent_name}.".sql";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
agent_name => $agent_name,
sql_path => $sql_path,
}});
if (not -e $sql_path)
{
next;
}
my $tables = $anvil->Database->get_tables_from_schema({
debug => $debug,
schema_file => $sql_path,
});
foreach my $table (reverse @{$tables})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { table => $table }});
$anvil->Database->_find_column({
debug => $debug,
table => $table,
search_column => $search_column,
});
}
}
my $tables = $anvil->Database->get_tables_from_schema({debug => $debug, schema_file => $anvil->data->{path}{sql}{'anvil.sql'}});
foreach my $table (reverse @{$tables})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { table => $table }});
$anvil->Database->_find_column({
debug => $debug,
table => $table,
search_column => $search_column,
});
}
# Manually push 'hosts'
push @{$anvil->data->{sys}{database}{uuid_tables}}, {
table => $main_table,
host_uuid_column => $search_column,
};
return($anvil->data->{sys}{database}{uuid_tables});
}
=head2 get_alerts
This reads in alerts from the C<< alerts >> table.
@ -4525,12 +4623,29 @@ sub get_tables_from_schema
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { line => $line }});
$line =~ s/--.*?//;
next if $line =~ /CREATE TABLE history\./;
if ($line =~ /CREATE TABLE history\.(.*?) \(/)
{
my $table = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { table => $table }});
$anvil->data->{sys}{database}{history_table}{$table} = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"sys::database::history_table::${table}" => $anvil->data->{sys}{database}{history_table}{$table},
}});
}
if ($line =~ /CREATE TABLE (.*?) \(/i)
{
my $table = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { table => $table }});
push @{$tables}, $table;
if (not exists $anvil->data->{sys}{database}{history_table}{$table})
{
$anvil->data->{sys}{database}{history_table}{$table} = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"sys::database::history_table::${table}" => $anvil->data->{sys}{database}{history_table}{$table},
}});
}
}
}
@ -15621,6 +15736,57 @@ COPY history.".$table." (";
}
=head2 _find_column
This takes a table name and looks for a column that ends in C<< _host_uuid >> and, if found, stores it in the C<< sys::database::uuid_tables >> array.
Parameters;
=head3 table (required)
This is the table being queried.
=cut
sub _find_column
{
my $self = shift;
my $parameter = shift;
my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Database->_find_column()" }});
my $table = defined $parameter->{table} ? $parameter->{table} : "";
my $search_column = defined $parameter->{search_column} ? $parameter->{search_column} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
table => $table,
search_column => $search_column,
}});
return('!!error!!') if not $table;
my $query = "SELECT column_name FROM information_schema.columns WHERE table_catalog = ".$anvil->Database->quote($anvil->data->{sys}{database}{name})." AND table_schema = 'public' AND table_name = ".$anvil->Database->quote($table)." AND data_type = 'uuid' AND is_nullable = 'NO' AND column_name LIKE '\%_".$search_column."';";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { query => $query }});
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
my $count = @{$results};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
table => $table,
count => $count,
}});
if ($count)
{
my $host_uuid_column = $results->[0]->[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { host_uuid_column => $host_uuid_column }});
push @{$anvil->data->{sys}{database}{uuid_tables}}, {
table => $table,
host_uuid_column => $host_uuid_column,
};
}
return(0);
}
=head2 _find_behind_databases
This returns the most up to date database ID, the time it was last updated and an array or DB IDs that are behind.

@ -501,6 +501,21 @@ sub parse_banged_string
"s3:value" => $value,
}});
# We've built things to support unit translation, though it's not implemented
# (yet). This clears those up.
if ($value =~ /^name=(.*?):units=(.*)$/)
{
my $name = $1;
my $unit = $2;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"s1:name" => $name,
"s2:unit" => $unit,
}});
$value = $name;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { value => $value }});
}
# Remove this pair
$variable_string =~ s/^\Q$pair//;
$variable_string =~ s/^,//;

@ -50,7 +50,9 @@
"scripts": {
"test": "grunt"
},
"dependencies": {},
"dependencies": {
"grunt": ">=1.3.0"
},
"devDependencies": {
"commitplease": "2.3.0",
"grunt": "0.4.5",

59
notes

@ -7,6 +7,15 @@ TODO:
============
# Dump
su - postgres -c "pg_dump anvil > /tmp/anvil.out" && mv /tmp/anvil.out /root/
su - postgres -c "pg_dump --schema-only anvil > /tmp/anvil.out" && mv /tmp/anvil.out /root/
cp /root/anvil.out /; su - postgres -c "dropdb anvil" && su - postgres -c "createdb --owner admin anvil" && su - postgres -c "psql anvil < /anvil.out"
su postgres -c "psql anvil"
============
# ScanCore post-scan logic;
Sole node:
@ -761,17 +770,63 @@ resource srv01-sql {
}
==================
1. Battery, short = -, add + / - to cell icon
mediawiki on EL8 install notes (starting from a minimal install);
dnf module reset php
dnf module enable php:7.4
dnf install httpd php php-mysqlnd php-gd php-xml mariadb-server mariadb php-mbstring php-json \
vim postgresql-server postgresql-plperl bash-completion wget tar rsync mlocate php-pecl-apcu \
dnf install httpd php php-gd php-xml php-mbstring php-json \
vim bash-completion wget tar rsync mlocate php-pecl-apcu \
memcached php-pear icu php-intl php-pgsql bzip2
### PostgreSQL
dnf install postgresql-server postgresql-plperl
postgresql-setup --initdb
systemctl start postgresql.service
systemctl enable postgresql.service
### MariaDB
dnf install php-mysqlnd php-gd php-xml mariadb-server mariadb
systemctl start mariadb
mysql_secure_installation
|Set root password? [Y/n] y
|New password:
|Re-enter new password:
|Password updated successfully!
|Remove anonymous users? [Y/n] y
|Disallow root login remotely? [Y/n] y
|Remove test database and access to it? [Y/n] y
|Reload privilege tables now? [Y/n] y
mysql -u root -p
### In mariadb
MariaDB [(none)]> CREATE DATABASE digimer_wiki;
MariaDB [(none)]> CREATE USER 'digimer'@'localhost' IDENTIFIED BY 'Initial1';
MariaDB [(none)]> GRANT ALL PRIVILEGES ON digimer_wiki.* TO 'digimer'@'localhost';
MariaDB [(none)]> FLUSH PRIVILEGES;
MariaDB [(none)]> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| digimer_wiki |
| information_schema |
| mysql |
| performance_schema |
+--------------------+
MariaDB [(none)]> SHOW GRANTS FOR 'digimer'@'localhost';
+----------------------------------------------------------------------------------------------------------------+
| Grants for digimer@localhost |
+----------------------------------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `digimer`@`localhost` IDENTIFIED BY PASSWORD '*xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' |
| GRANT ALL PRIVILEGES ON `digimer_wiki`.* TO `digimer`@`localhost` |
+----------------------------------------------------------------------------------------------------------------+
MariaDB [(none)]> exit
# Back to terminal
systemctl enable mariadb
# Common
systemctl start httpd.service
systemctl enable httpd.service
systemctl start memcached.service

@ -2061,8 +2061,14 @@ sub find_ipmi_targets
}
}
### NOTE: This is changed. Now that striker's directly call the targets to check temperature when
### evaluating thermal boot conditions, we don't need to store this data.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ipmi_targets => $ipmi_targets }});
return($ipmi_targets);
# Which hosts we scan depends on if we're a Striker dashboard or not. If we are, we'll try to scan
# all machines in all Anvil! systems. Otherwise, we only scan ourselves.
=cut
if ($host_type ne "striker")
{
# We're not a dashboard, so we don't scan others.
@ -2180,7 +2186,6 @@ AND
}});
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ipmi_targets => $ipmi_targets }});
=cut
return($ipmi_targets);
}

@ -1897,6 +1897,7 @@ BEGIN
temperature_sensor_host,
temperature_sensor_name,
temperature_value_c,
temperature_weight,
temperature_state,
temperature_is,
modified_date)
@ -1906,6 +1907,7 @@ BEGIN
history_temperature.temperature_agent_name,
history_temperature.temperature_sensor_host,
history_temperature.temperature_sensor_name,
history_temperature.temperature_weight,
history_temperature.temperature_value_c,
history_temperature.temperature_state,
history_temperature.temperature_is,

@ -413,6 +413,8 @@ The attempt to start the servers appears to have failed. The return code '0' was
<key name="error_0299">This host is not in a cluster, or it's in the cluster but not ready yet. Either way, unable to check the config.</key>
<key name="error_0300">Failed to find the install manifest for the: [#!variable!anvil_name!#] Anvil! system. Unable to check or update the fence configuration.</key>
<key name="error_0301">Failed to parse the install manifest with UUID: [#!variable!manifest_uuid!#]. Unable to check or update the fence configuration.</key>
<key name="error_0302">The passed in Anvil! UUID: [#!variable!anvil_uuid!#] was not found in the database.</key>
<key name="error_0303">The passed in host UUID: [#!variable!host_uuid!#] was not found in the database.</key>
<!-- Files templates -->
<!-- NOTE: Translating these files requires an understanding of which likes are translatable -->
@ -1911,6 +1913,13 @@ Are you sure that you want to delete the server: [#!variable!server_name!#]? [Ty
<key name="message_0237">One or more servers are migrating. While this is the case, ScanCore post-scan checks are not performed.</key>
<key name="message_0238">Preventative live migration has completed.</key>
<key name="message_0239">Preventative live migration has been disabled. We're healthier than our peer, but we will take no action.</key>
<key name="message_0240"><![CDATA[Please specify the Anvil! or host you want to purge. Use '--anvil <name_or_uuid>' or '--host <name_or_uuid>'.]]></key>
<key name="message_0241"><![CDATA[Used '-y' or '--yes', proceeding automatically.]]></key>
<key name="message_0242">Are you sure that you want to completely purge: [#!variable!host_name!#] (UUID: [#!variable!host_uuid!#] from the Anvil! database(s)?</key>
<key name="message_0243">Are you sure that you want to completely purge the Anvil!: [#!variable!anvil_name!#] (UUID: [#!variable!anvil_uuid!#] along with the machines:</key>
<key name="message_0244">- Host name: [#!variable!host_name!#] (host UUID: [#!variable!host_uuid!#]:</key>
<key name="message_0245">Now purging: [#!variable!host_name!#] (host UUID: [#!variable!host_uuid!#]:</key>
<key name="message_0246">Purging the Anvil!: [#!variable!anvil_name!#] (UUID: [#!variable!anvil_uuid!#]:</key>
<!-- Success messages shown to the user -->
<key name="ok_0001">Saved the mail server information successfully!</key>

@ -179,6 +179,7 @@ sub run_jobs
# Make sure the server is flagged as DELETEd.
$anvil->Database->get_servers();
my $server_state = $anvil->data->{servers}{server_uuid}{$server_uuid}{server_state};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { server_state => $server_state }});
if ($server_state ne "DELETED")
{
my $query = "

@ -32,6 +32,7 @@ my $anvil = Anvil::Tools->new();
# Read switches (target ([user@]host[:port]) and the file with the target's password. If the password is
# passed directly, it will be used. Otherwise, the password will be read from the database.
$anvil->data->{switches}{anvil} = "";
$anvil->data->{switches}{'anvil-name'} = "";
$anvil->data->{switches}{'anvil-uuid'} = "";
$anvil->data->{switches}{os} = "";
@ -45,6 +46,7 @@ $anvil->data->{switches}{'storage-size'} = "";
$anvil->Get->switches;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, secure => 0, key => "log_0115", variables => { program => $THIS_FILE }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
'switches::anvil' => $anvil->data->{switches}{anvil},
'switches::anvil-name' => $anvil->data->{switches}{'anvil-name'},
'switches::anvil-uuid' => $anvil->data->{switches}{'anvil-uuid'},
'switches::os' => $anvil->data->{switches}{os},
@ -1445,6 +1447,25 @@ sub interactive_question
{
my ($anvil) = @_;
# If 'switches::anvil' is set, see if it's a UUID and then set either 'anvil-uuid' or 'anvil-name'.
if (($anvil->data->{switches}{anvil}) && (not $anvil->data->{switches}{'anvil-uuid'}) && (not $anvil->data->{switches}{'anvil-name'}))
{
if ($anvil->Validate->uuid({uuid => $anvil->data->{switches}{anvil}}))
{
$anvil->data->{switches}{'anvil-uuid'} = $anvil->data->{switches}{anvil};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"new_server::anvil_uuid" => $anvil->data->{new_server}{anvil_uuid},
}});
}
else
{
$anvil->data->{switches}{'anvil-name'} = $anvil->data->{switches}{anvil};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"new_server::anvil_name" => $anvil->data->{new_server}{anvil_name},
}});
}
}
# Do we know or can we find the Anvil! UUID?
$anvil->data->{new_server}{anvil_uuid} = $anvil->data->{switches}{'anvil-uuid'} ? $anvil->data->{switches}{'anvil-uuid'} : "";
$anvil->data->{new_server}{anvil_name} = $anvil->data->{switches}{'anvil-name'} ? $anvil->data->{switches}{'anvil-name'} : "";

@ -117,6 +117,12 @@ if (not $anvil->data->{sys}{anvil_uuid})
$anvil->nice_exit({exit_code => 1});
}
# If no stop-reason was set, set it to 'user'
if (not $anvil->data->{switches}{'stop-reason'})
{
$anvil->data->{switches}{'stop-reason'} = "user";
}
# Migrate or stop the servers, if any servers are running here.
process_servers($anvil);

@ -64,6 +64,10 @@ sub purge
# hosts without any further references. As each delete is done, delete it. Loop until all entries are
# gone.
die;
# Load all the known tables in the DB.
my $query = "SELECT schemaname||'.'||tablename AS table_name FROM pg_catalog.pg_tables WHERE schemaname = 'history' ORDER BY tablename ASC, schemaname ASC;";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});

Loading…
Cancel
Save