From 7abbc938afc8c61680de7877eed536d35806a1ad Mon Sep 17 00:00:00 2001 From: Digimer Date: Tue, 11 May 2021 14:14:00 -0400 Subject: [PATCH 1/2] * Renamed tools/striker-purge-host to tools/striker-purge-target and moved the code from test.pl over to it. No longer provides interactive selection, but now does work with Anvil! systems as well as hosts. * Fixed a bug in Database->get_tables_from_schema where history.X and X tables were being stored in the table list. * Updated ocf:alteeve:server to no do resyncs on DB connect. Signed-off-by: Digimer --- Anvil/Tools/Database.pm | 2 +- Anvil/Tools/ScanCore.pm | 12 +- notes | 175 +++++++++++++---- ocf/alteeve/server | 7 +- share/words.xml | 1 + tools/striker-purge-host | 329 -------------------------------- tools/striker-purge-target | 381 +++++++++++++++++++++++++++++++++++++ 7 files changed, 538 insertions(+), 369 deletions(-) delete mode 100755 tools/striker-purge-host create mode 100755 tools/striker-purge-target diff --git a/Anvil/Tools/Database.pm b/Anvil/Tools/Database.pm index 084bab68..f4a6a2f7 100644 --- a/Anvil/Tools/Database.pm +++ b/Anvil/Tools/Database.pm @@ -4633,7 +4633,7 @@ sub get_tables_from_schema "sys::database::history_table::${table}" => $anvil->data->{sys}{database}{history_table}{$table}, }}); } - if ($line =~ /CREATE TABLE (.*?) \(/i) + elsif ($line =~ /CREATE TABLE (.*?) \(/i) { my $table = $1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { table => $table }}); diff --git a/Anvil/Tools/ScanCore.pm b/Anvil/Tools/ScanCore.pm index 91a84b80..68642117 100644 --- a/Anvil/Tools/ScanCore.pm +++ b/Anvil/Tools/ScanCore.pm @@ -290,10 +290,16 @@ sub call_scan_agents } # If an agent takes a while to run, log it with higher verbosity - my $runtime = (time - $start_time); - my $log_level = $runtime > 10 ? 1 : $debug; + my $runtime = (time - $start_time); + my $log_level = $debug; + my $string_key = "log_0557"; + if ($runtime > 10) + { + $log_level = 1; + $string_key = "log_0621"; + } $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { output => $output, runtime => $runtime }}); - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => $log_level, key => "log_0557", variables => { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => $log_level, key => $string_key, variables => { agent_name => $agent_name, runtime => $runtime, return_code => $return_code, diff --git a/notes b/notes index 82041059..b7cfb9ac 100644 --- a/notes +++ b/notes @@ -974,7 +974,7 @@ OS10# configure terminal OS10(config)# vlt-domain 1 OS10(conf-vlt-1)# vlt-mac 00:00:00:00:00:02 -# show vlt 1 mismatch +OS10# show vlt 1 mismatch (If no issues, VLT is OK) # See how I am and my role (* == switch you're on) @@ -986,36 +986,47 @@ VLT Unit ID Role =====] VLAN Config [======== - -zo-switch02# configure terminal -zo-switch02(config)# interface vlan 100 -zo-switch02(conf-if-vl-100)# description BCN1 -zo-switch02(conf-if-vl-100)# interface range ethernet 1/1/1-1/1/10 -zo-switch02(conf-range-eth1/1/1-1/1/10)# switchport access vlan 100 -zo-switch02(conf-range-eth1/1/1-1/1/10)# exot -% Error: Unrecognized command. -zo-switch02(conf-range-eth1/1/1-1/1/10)# exit -zo-switch02(config)# interface range ethernet 1/1/11-1/1/14 -zo-switch02(conf-range-eth1/1/11-1/1/14)# switchport access vlan 200 -zo-switch02(conf-range-eth1/1/11-1/1/14)# exit -zo-switch02(config)# interface range ethernet 1/1/15-1/1/24 -zo-switch02(conf-range-eth1/1/15-1/1/24)# switchport access vlan 300 -ezo-switch02(conf-range-eth1/1/15-1/1/24)# exit -zo-switch02(config)# show vlan +OS10# configure terminal +OS10(config)# interface mgmt 1/1/1 +OS10(conf-if-ma-1/1/1)# no ip address dhcp +OS10(conf-if-ma-1/1/1)# ip address 10.201.1.2/16 +OS10(conf-if-ma-1/1/1)# no shutdown +OS10(conf-if-ma-1/1/1)# exit +OS10(config)# write memory +OS10(config)# hostname zo-switch01 +zo-switch01(config)# interface vlan 100 +zo-switch01(conf-if-vl-100)# description BCN1 +zo-switch01(conf-if-vl-100)# interface range ethernet 1/1/1-1/1/10 +zo-switch01(conf-range-eth1/1/1-1/1/10)# switchport access vlan 100 +zo-switch01(conf-range-eth1/1/1-1/1/10)# no shutdown +zo-switch01(conf-range-eth1/1/1-1/1/10)# exit +zo-switch01(config)# interface vlan 200 +zo-switch01(conf-if-vl-200)# description SN1 +zo-switch01(conf-if-vl-200)# interface range ethernet 1/1/11-1/1/14 +zo-switch01(conf-range-eth1/1/11-1/1/14)# switchport access vlan 200 +zo-switch01(conf-range-eth1/1/11-1/1/14)# no shutdown +zo-switch01(conf-range-eth1/1/11-1/1/14)# exit +zo-switch01(config)# interface vlan 300 +zo-switch01(conf-if-vl-300)# description IFN1 +zo-switch01(conf-if-vl-300)# interface range ethernet 1/1/15-1/1/24 +zo-switch01(conf-range-eth1/1/15-1/1/24)# switchport access vlan 300 +zo-switch01(conf-range-eth1/1/15-1/1/24)# no shutdown +zo-switch01(conf-range-eth1/1/15-1/1/24)# exit +zo-switch01(config)# show vlan Codes: * - Default VLAN, M - Management VLAN, R - Remote Port Mirroring VLANs, - @ – Attached to Virtual Network, P - Primary, C - Community, I - Isolated + @ - Attached to Virtual Network, P - Primary, C - Community, I - Isolated Q: A - Access (Untagged), T - Tagged NUM Status Description Q Ports * 1 Active A Eth1/1/27-1/1/30 A Po1000 100 Active BCN1 T Po1000 A Eth1/1/1-1/1/10 - 200 Active T Po1000 + 200 Active SN1 T Po1000 A Eth1/1/11-1/1/14 - 300 Active T Po1000 + 300 Active IFN1 T Po1000 A Eth1/1/15-1/1/24 4094 Active T Po1000 - +zo-switch01(config)# write memory ### Delete a VLAN: @@ -1023,18 +1034,109 @@ zo-switch02(config)# no interface vlan 3400 zo-switch02(config)# show vlan -# Configure VLANs. -OS10(config)# interface range vlan 100,200,300 -OS10(conf-range-vl-100,200,300)# exit -OS10(config)# interface range ethernet 1/1/1-1/1/10 -OS10(conf-range-eth1/1/1-1/1/10)# switchport access vlan 100 -OS10(conf-range-eth1/1/1-1/1/10)# exit -OS10(config)# interface range ethernet 1/1/11-1/1/14 -OS10(conf-range-eth1/1/11-1/1/14)# switchport access vlan 200 -OS10(conf-range-eth1/1/11-1/1/14)# exit -OS10(config)# interface range ethernet 1/1/15-1/1/24 -OS10(conf-range-eth1/1/15-1/1/24)# switchport access vlan 300 -OS10(conf-range-eth1/1/15-1/1/24)# exit +=== Firmware Update === + +Download the firmware from the Dell digital locker. Once downloaded, extracted and the sum verified, copy the 'PKGS_OS10-Enterprise-xxx-installer-x86_64.bin' file to one of the striker's /var/lib/tftpboot/' directory and start the dhcpd service. + +### NOTE: Use the striker NOT connected to the switch being upgraded! If necessary, move the uplink interfaces off of the switch to be upgraded! +Once ready, connect to the switch over serial port. + +### WARNING: The firmware update completely resets the switch! Backup you config before hand, if necessary. Also, watch for the loss of VLANs causing switch loops when 2+ uplinks are connected! + +# screen /dev/ttyUSB0 115200 + +# Reboot the switches; +reload + +When the grub boot menu appears, choose ++--------------------------------------------------------+ +|*ONIE: Install OS | +| ONIE: Rescue | +| ONIE: Uninstall OS | +| ONIE: Update ONIE | +| ONIE: Embed ONIE | +| ONIE: Diag ONIE | ++--------------------------------------------------------+ + +# This will start a constant scan of IP addresses and local storage looking for the firmware. To stop this, type: +ONIE:/ # onie-discovery-stop + +# Set the IP address so that we can talk to the striker with the firmware. + +ONIE:/ # ifconfig eth0 10.201.1.1 netmask 255.255.0.0 up +ONIE:/ # ping 10.201.4.1 +PING 10.201.4.1 (10.201.4.1): 56 data bytes +64 bytes from 10.201.4.1: seq=0 ttl=64 time=0.247 ms +64 bytes from 10.201.4.1: seq=1 ttl=64 time=0.506 ms + + +ONIE:/ # onie-nos-install tftp://10.201.4.1/PKGS_OS10-Enterprise-10.5.2.3.316stretch-installer-x86_64.bin + +discover: installer mode detected. +Stopping: discover... done. +Info: Fetching tftp://10.201.4.1/PKGS_OS10-Enterprise-10.5.2.3.316stretch-installer-x86_64.bin ... <- This step takes a while +PKGS_OS10-Enterprise 100% |*******************************| 744M 0:00:00 ETA +ONIE: Executing installer: tftp://10.201.4.1/PKGS_OS10-Enterprise-10.5.2.3.316stretch-installer-x86_64.bin +Initializing installer ... OK +Verifying image checksum ... OK +OS10 Installer: machine: dellemc_s4100_c2338/s4128t +Fixing up partitions ... OK +Deleting logical volume CONFIG ... OK +Deleting logical volume SYSROOT1 ... OK +Deleting logical volume SYSROOT2 ... OK +Creating logical volume SYSROOT ... OK +Creating ext4 filesystem on SYSROOT ... OK +Extracting OS10 ... OK +Installing OS10 on primary volume ... OK <- This takes a long time, be patient +Setting up shared data ... OK +Synchronizing standby image ... +OS10 installation is complete. +Creating ext4 filesystem on sda4 ... OK +Installing GRUB-UEFI ... OK +Saving system information ... OK +Saving ONIE support information ... OK +ONIE: NOS install successful: tftp://10.201.4.1/PKGS_OS10-Enterprise-10.5.2.3.316stretch-installer-x86_64.bin +ONIE: Rebooting... +ONIE:/ # discover: installer mode detected. +Stopping: discover...start-stop-daemon: warning: killing process 2881: No such process + done. +Stopping: dropbear ssh daemon... done. +Stopping: telnetd... done. +Stopping: syslogd... done. +Info: Unmounting kernel filesystems +umount: can't umount /: Invalid argument +The system is going down NOW! +Sent SIGTERM to all processes +Sent SIGKILL tosd 4:0:0:0: [sda] Synchronizing SCSI cache +reboot: Restarting system +reboot: machine restart + +### NOTE: The login prompt will appear before the system is ready to log in. The default username and password revert to 'admin' / 'admin', but this won't work for the first couple of minutes. +## OLD +zo-switch02# show version +Dell EMC Networking OS10 Enterprise +Copyright (c) 1999-2020 by Dell Inc. All Rights Reserved. +OS Version: 10.5.0.4 +Build Version: 10.5.0.4.638 +Build Time: 2020-01-30T21:08:56+0000 +System Type: S4128T-ON +Architecture: x86_64 +Up Time: 22:49:57 +zo-switch02# + +## New +zo-a01n01# show version +Dell EMC Networking OS10 Enterprise +Copyright (c) 1999-2021 by Dell Inc. All Rights Reserved. +OS Version: 10.5.2.3 +Build Version: 10.5.2.3.316 +Build Time: 2021-02-26T20:03:25+0000 +System Type: S4128T-ON +Architecture: x86_64 + + + + ==================================== -=] Rename a resource (ex: srv09-few-tcpremote1 -> srv09-fea-tcpremote1) @@ -1074,3 +1176,10 @@ May 02 13:35:21 zo-a01n02.zennioptical.com setroubleshoot[5333]: SELinux is prev # ausearch -c 'drbdsetup' --raw | audit2allow -M my-drbdsetup # semodule -X 300 -i my-drbdsetup.pp + If you believe that virsh should be allowed read access on the srv16-zo-psql-qa.xml file by default. + # ausearch -c 'virsh' --raw | audit2allow -M my-virsh + # semodule -X 300 -i my-virsh.pp + + + + \ No newline at end of file diff --git a/ocf/alteeve/server b/ocf/alteeve/server index 58564508..b671a6c3 100755 --- a/ocf/alteeve/server +++ b/ocf/alteeve/server @@ -97,8 +97,9 @@ $| = 1; # in the loop as well to override defaults in code. my $anvil = Anvil::Tools->new(); -# If we can connect to a database, we'll set/clear the 'migrating' flag during migrations -$anvil->Database->connect(); +# If we can connect to a database, we'll set/clear the 'migrating' flag during migrations. For timing reasons +# we don't let the RA do resyncs. +$anvil->Database->connect({no_resync => 1}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, secure => 0, key => "log_0132"}); if (not $anvil->data->{sys}{database}{connections}) { @@ -155,7 +156,7 @@ $anvil->data->{environment}{OCF_RESKEY_CRM_meta_stop_drbd_resources} = 0; $anvil->data->{switches}{migrate_to} = ""; # Sets 'meta_migrate_target' $anvil->data->{switches}{migrate_from} = ""; # Sets 'meta_migrate_source' When set without 'migrate_to', does a status check after migration $anvil->data->{switches}{server} = ""; # Sets 'name'. -$anvil->Get->switches({debug => 2}); +$anvil->Get->switches(); if ($anvil->data->{switches}{stop_drbd_resources}) { diff --git a/share/words.xml b/share/words.xml index cc1bc381..3fc76d85 100644 --- a/share/words.xml +++ b/share/words.xml @@ -1578,6 +1578,7 @@ The file: [#!variable!file!#] needs to be updated. The difference is: Successfully deleted the file: [#!variable!file!#] on the target: [#!variable!target!#]. The host: [#!variable!host_name!#] has shut down for thermal reasons: [#!variable!count!#] times. To prevent a frequent boot / thermal excursion / shutdown loop, we will wait: [#!variable!wait_for!#] before marking it's temperature as being OK again. This host has been running for: [#!variable!uptime!#]. The cluster will not be started (uptime must be less than 10 minutes for 'anvil-safe-start' to be called automatically). + - The Scan agent: [#!variable!agent_name!#] ran a bit long, exiting after: [#!variable!runtime!#] seconds with the return code: [#!variable!return_code!#]. The host name: [#!variable!target!#] does not resolve to an IP address. diff --git a/tools/striker-purge-host b/tools/striker-purge-host deleted file mode 100755 index 5c2648c0..00000000 --- a/tools/striker-purge-host +++ /dev/null @@ -1,329 +0,0 @@ -#!/usr/bin/perl -# -# This tool is meant to be run from the command line and lets a user purge all information about a given host -# from the databases. -# -# Exit codes; -# 0 = Normal exit. -# 1 = User's answer to which machine to purge was invalid. -# 2 = The passed-in (or selected) host UUID (no longer) exists. -# 3 = User did not confirm to proceed. -# 4 = Runaway loop detected. -# - -use strict; -use warnings; -use Anvil::Tools; -use Data::Dumper; -use Curses::UI; - -my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0]; -my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0]; -if (($running_directory =~ /^\./) && ($ENV{PWD})) -{ - $running_directory =~ s/^\./$ENV{PWD}/; -} - -# Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete. -$| = 1; - -my $anvil = Anvil::Tools->new(); - -$anvil->Get->switches; -$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0115", variables => { program => $THIS_FILE }}); - -$anvil->Database->connect({debug => 3}); -$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0132"}); - -# First, ask the user to select a host. -$anvil->data->{switches}{'host-uuid'} = "" if not defined $anvil->data->{switches}{'host-uuid'}; -$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 'switches::host-uuid' => $anvil->data->{switches}{'host-uuid'} }}); - -$anvil->Database->get_hosts(); - -# Ask the user to pick a host. -pick_host($anvil); - -# Ask the user to confirm. -confirm($anvil); - -# If we're alive, purge. -purge($anvil); - -$anvil->nice_exit({exit_code => 0}); - -############################################################################################################# -# Functions # -############################################################################################################# - -sub purge -{ - my ($anvil) = @_; - - # Read in all constraints and create a hash of what depends on what, then delete anything referencing - # 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 }}); - - my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); - my $count = @{$results}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { - results => $results, - count => $count, - }}); - foreach my $row (@{$results}) - { - my $table_name = $row->[0]; - $anvil->data->{sql}{history_tables}{$table_name} = 1; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "sql::history_tables::${table_name}" => $anvil->data->{sql}{history_tables}{$table_name} }}); - } - undef $query; - undef $results; - undef $count; - - ### NOTE: Credit for this query goes to: - ### - https://stackoverflow.com/questions/1152260/postgres-sql-to-list-table-foreign-keys - $query = " -SELECT - tc.table_schema||'.'||tc.table_name AS table, - kcu.column_name, - ccu.table_schema||'.'||ccu.table_name AS foreign_table, - ccu.column_name AS foreign_column_name -FROM - information_schema.table_constraints AS tc - JOIN information_schema.key_column_usage AS kcu - ON tc.constraint_name = kcu.constraint_name - AND tc.table_schema = kcu.table_schema - JOIN information_schema.constraint_column_usage AS ccu - ON ccu.constraint_name = tc.constraint_name - AND ccu.table_schema = tc.table_schema -WHERE - tc.constraint_type = 'FOREIGN KEY' -AND - ccu.column_name = 'host_uuid' -;"; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); - - $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); - $count = @{$results}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { - results => $results, - count => $count, - }}); - foreach my $row (@{$results}) - { - my $table_name = $row->[0]; - my $column_name = $row->[1]; - my $foreign_table_name = $row->[2]; - my $foreign_column_name = $row->[3]; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { - 's1:table_name' => $table_name, - 's2:column_name' => $column_name, - 's3:foreign_table_name' => $foreign_table_name, - 's4:foreign_column_name' => $foreign_column_name, - }}); - - if (not exists $anvil->data->{sql}{table}{$table_name}) - { - $anvil->data->{sql}{table}{$table_name}{column} = $column_name; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "sql::table::${table_name}::column" => $anvil->data->{sql}{table}{$table_name}{column} }}); - } - if (not exists $anvil->data->{sql}{table}{$foreign_table_name}) - { - $anvil->data->{sql}{table}{$foreign_table_name}{column} = $foreign_column_name; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { "sql::table::${foreign_table_name}::column" => $anvil->data->{sql}{table}{$foreign_table_name}{column} }}); - } - $anvil->data->{sql}{table}{$foreign_table_name}{referenced_by}{$table_name} = 1; - } - - my $loops = 20; - my $loop = 0; - my $done = 0; - my $queries = []; - until($done) - { - $loop++; - foreach my $table_name (sort {$a cmp $b} keys %{$anvil->data->{sql}{table}}) - { - my $count = exists $anvil->data->{sql}{table}{$table_name}{referenced_by} ? keys %{$anvil->data->{sql}{table}{$table_name}{referenced_by}} : 0; - #print "-- Table: [".$table_name."] is referenced by: [".$count."] foreign table(s);\n"; - if (not $count) - { - # If there is a corresponding history table, delete it as well. - my $column = $anvil->data->{sql}{table}{$table_name}{column}; - my $history_table = $table_name; - $history_table =~ s/^public\./history\./; - if (exists $anvil->data->{sql}{history_tables}{$history_table}) - { - push @{$queries}, "DELETE FROM ".$history_table." WHERE ".$column." = ".$anvil->Database->quote($anvil->data->{switches}{'host-uuid'}).";"; - } - - # Now delete the public table entries. - push @{$queries}, "DELETE FROM ".$table_name." WHERE ".$column." = ".$anvil->Database->quote($anvil->data->{switches}{'host-uuid'}).";"; - - foreach my $foreign_table_name (sort {$a cmp $b} keys %{$anvil->data->{sql}{table}}) - { - if (exists $anvil->data->{sql}{table}{$foreign_table_name}{referenced_by}{$table_name}) - { - #print "-- Deleting: [".$foreign_table_name."] reference to: [".$table_name."]\n"; - delete $anvil->data->{sql}{table}{$foreign_table_name}{referenced_by}{$table_name}; - } - } - #print "-- Deleting hash entry for: [".$table_name."]\n"; - delete $anvil->data->{sql}{table}{$table_name}; - } - } - - my $remaining = keys %{$anvil->data->{sql}{table}}; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { remaining => $remaining }}); - if (not $remaining) - { - $done = 1; - } - elsif ($loop > $loops) - { - print "Run-away loop detected, aborting!\n"; - $anvil->nice_exit({exit_code => 4}); - } - } - - # Do the deed. - $anvil->Database->write({debug => 2, query => $queries, source => $THIS_FILE, line => __LINE__}); - my $host_uuid = $anvil->data->{switches}{'host-uuid'}; - print $anvil->Words->string({key => "message_0176", variables => { host_name => $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_name} }})."\n"; - - return(0); -} - -sub confirm -{ - my ($anvil) = @_; - - # Normalize -y - $anvil->data->{switches}{'y'} = "" if not defined $anvil->data->{switches}{'y'}; - $anvil->data->{switches}{'yes'} = "" if not defined $anvil->data->{switches}{'yes'}; - $anvil->data->{switches}{'Y'} = "" if not defined $anvil->data->{switches}{'Y'}; - $anvil->data->{switches}{'Yes'} = "" if not defined $anvil->data->{switches}{'Yes'}; - $anvil->data->{switches}{'YES'} = "" if not defined $anvil->data->{switches}{'YES'}; - if (not $anvil->data->{switches}{'y'}) - { - if ($anvil->data->{switches}{'yes'}) { $anvil->data->{switches}{'y'} = $anvil->data->{switches}{'yes'}; } - elsif ($anvil->data->{switches}{'Y'}) { $anvil->data->{switches}{'y'} = $anvil->data->{switches}{'Y'}; } - elsif ($anvil->data->{switches}{'Yes'}) { $anvil->data->{switches}{'y'} = $anvil->data->{switches}{'Yes'}; } - elsif ($anvil->data->{switches}{'YES'}) { $anvil->data->{switches}{'y'} = $anvil->data->{switches}{'YES'}; } - } - - # Ask to confirm, is sane and not -y - my $host_uuid = $anvil->data->{switches}{'host-uuid'}; - if (not exists $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_name}) - { - print $anvil->Words->string({key => "error_0131", variables => { host_uuid => $host_uuid }})."\n\n"; - $anvil->nice_exit({exit_code => 2}); - } - else - { - print $anvil->Words->string({key => "message_0172"})."\n\n"; - print $anvil->Words->string({key => "message_0173", variables => { - host_name => $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_name}, - host_uuid => $host_uuid, - }})."\n"; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 'switches::y' => $anvil->data->{switches}{'y'} }}); - if ($anvil->data->{switches}{'y'} eq "#!SET!#") - { - # Already confirmed. - print $anvil->Words->string({key => "message_0023"})."\n\n"; - return(0); - } - print $anvil->Words->string({key => "message_0021"})." "; - chomp(my $answer = ); - - if ($answer =~ /^y/i) - { - # Proceed. - print $anvil->Words->string({key => "message_0175"})."\n"; - } - else - { - # Abort. - print $anvil->Words->string({key => "message_0022"})."\n"; - $anvil->nice_exit({exit_code => 3}); - } - } - - return(0); -} - -sub pick_host -{ - my ($anvil) = @_; - - return(0) if $anvil->data->{switches}{'host-uuid'}; - - # Get a list of hosts - my $i = 1; - my $select = {}; - my $host_length = 0; - my $type_length = 0; - foreach my $host_name (sort {$a cmp $b} keys %{$anvil->data->{sys}{hosts}{by_name}}) - { - my $host_uuid = $anvil->data->{sys}{hosts}{by_name}{$host_name}; - my $host_type = $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_type}; - my $anvil_name = defined $anvil->data->{hosts}{host_uuid}{$host_uuid}{anvil_name} ? $anvil->data->{hosts}{host_uuid}{$host_uuid}{anvil_name} : ""; - my $say_type = $anvil->Words->string({key => "brand_0009"}); - if ($host_type eq "striker") { $say_type = $anvil->Words->string({key => "brand_0003"}); } - elsif ($host_type eq "node") { $say_type = $anvil->Words->string({key => "brand_0007"}); } - elsif ($host_type eq "dr") { $say_type = $anvil->Words->string({key => "brand_0008"}); } - - if (length($host_name) > $host_length) { $host_length = length($host_name); } - if (length($say_type) > $type_length) { $type_length = length($say_type); } - - # This is used to build the menu. - $select->{$i}{host_uuid} = $host_uuid; - $select->{$i}{host_name} = $host_name; - $select->{$i}{host_type} = $host_type; - $select->{$i}{say_type} = $say_type; - $select->{$i}{anvil_name} = $anvil_name; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { - "${i}::host_uuid" => $select->{$i}{host_uuid}, - "${i}::host_name" => $select->{$i}{host_name}, - "${i}::host_type" => $select->{$i}{host_type}, - "${i}::anvil_name" => $select->{$i}{anvil_name}, - }}); - $i++; - } - - my $pad = $i > 9 ? 2 : 1; - print $anvil->Words->string({key => "message_0169"})."\n\n"; - foreach my $i (sort {$a cmp $b} keys %{$select}) - { - print " ".$anvil->Words->string({key => "message_0170", variables => { - key => sprintf("%".$pad."s", $i), - type => sprintf("%-".$type_length."s", $select->{$i}{say_type}), - host_name => sprintf("%-".$host_length."s", $select->{$i}{host_name}), - host_uuid => $select->{$i}{host_uuid}, - }})."\n"; - } - print "\n".$anvil->Words->string({key => "message_0171"})." "; - - chomp(my $answer = ); - - if (not exists $select->{$answer}{host_name}) - { - print "\n".$anvil->Words->string({key => "error_0130", variables => { answer => $answer }})."\n\n"; - $anvil->nice_exit({exit_code => 1}); - } - else - { - print "\n"; - $anvil->data->{switches}{'host-uuid'} = $select->{$answer}{host_uuid}; - } - - return(0); -} diff --git a/tools/striker-purge-target b/tools/striker-purge-target new file mode 100755 index 00000000..709099fd --- /dev/null +++ b/tools/striker-purge-target @@ -0,0 +1,381 @@ +#!/usr/bin/perl +# +# This is a tool that purges hosts or Anvil! systems from the ScanCore databases. +# + +use strict; +use warnings; +use Anvil::Tools; +use Data::Dumper; + +$| = 1; + +my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0]; +my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0]; +if (($running_directory =~ /^\./) && ($ENV{PWD})) +{ + $running_directory =~ s/^\./$ENV{PWD}/; +} + +my $anvil = Anvil::Tools->new(); + + +$anvil->Database->connect({debug => 3}); +$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0132"}); +if (not $anvil->data->{sys}{database}{connections}) +{ + # No databases, exit. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, priority => "err", key => "error_0003"}); + $anvil->nice_exit({exit_code => 1}); +} + +$anvil->data->{switches}{'anvil'} = ""; +$anvil->data->{switches}{'host'} = ""; +$anvil->data->{switches}{'y'} = ""; +$anvil->data->{switches}{'yes'} = ""; +$anvil->Get->switches(); +if ((not $anvil->data->{switches}{'anvil'}) && (not $anvil->data->{switches}{'host'})) +{ + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0240"}); + $anvil->nice_exit({exit_code => 1}); +} +$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "switches::anvil" => $anvil->data->{switches}{'anvil'}, + "switches::host" => $anvil->data->{switches}{'host'}, + "switches::y" => $anvil->data->{switches}{'y'}, + "switches::yes" => $anvil->data->{switches}{'yes'}, +}}); + +$anvil->data->{purge}{anvil_uuid} = ""; +$anvil->data->{purge}{anvil_name} = ""; +$anvil->data->{purge}{host_uuid} = ""; +$anvil->data->{purge}{host_name} = ""; +$anvil->data->{purge}{hosts} = []; +$anvil->Database->get_anvils(); +$anvil->Database->get_hosts({include_deleted => 1}); +my $vacuum = 0; +if ($anvil->data->{switches}{'anvil'}) +{ + my $anvil_name = ""; + my $anvil_uuid = ""; + if ($anvil->Validate->uuid({uuid => $anvil->data->{switches}{'anvil'}})) + { + $anvil_uuid = $anvil->data->{switches}{'anvil'}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { anvil_uuid => $anvil_uuid }}); + + # Convert it to an Anvil! name. + $anvil->data->{purge}{anvil_uuid} = $anvil_uuid; + $anvil->data->{purge}{anvil_name} = exists $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid} ? $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_name} : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "purge::anvil_uuid" => $anvil->data->{purge}{anvil_uuid}, + "purge::anvil_name" => $anvil->data->{purge}{anvil_name}, + }}); + + if (not $anvil->data->{purge}{anvil_name}) + { + # Bad anvil_uuid + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "error_0302", priority => "err", variables => { anvil_uuid => $anvil->data->{switches}{'anvil'} }}); + $anvil->nice_exit({exit_code => 1}); + } + } + else + { + # Look for the name. + $anvil_name = $anvil->data->{switches}{'anvil'}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { anvil_name => $anvil_name }}); + + $anvil->data->{purge}{anvil_name} = $anvil_name; + $anvil->data->{purge}{anvil_uuid} = exists $anvil->data->{anvils}{anvil_name}{$anvil_name} ? $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_uuid} : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "purge::anvil_uuid" => $anvil->data->{purge}{anvil_uuid}, + "purge::anvil_name" => $anvil->data->{purge}{anvil_name}, + }}); + + if (not $anvil->data->{purge}{anvil_uuid}) + { + # Bad anvil name. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "error_0302", priority => "err", variables => { anvil_uuid => $anvil->data->{switches}{'anvil'} }}); + $anvil->nice_exit({exit_code => 1}); + } + } + + # Load the list of hosts. + $anvil_uuid = $anvil->data->{purge}{anvil_uuid}; + push @{$anvil->data->{purge}{hosts}}, $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node1_host_uuid}; + push @{$anvil->data->{purge}{hosts}}, $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node2_host_uuid}; + if ($anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_dr1_host_uuid}) + { + push @{$anvil->data->{purge}{hosts}}, $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_dr1_host_uuid}; + } +} +elsif($anvil->data->{switches}{'host'}) +{ + my $host_uuid = ""; + my $host_name = ""; + if ($anvil->Validate->uuid({uuid => $anvil->data->{switches}{'host'}})) + { + $host_uuid = $anvil->data->{switches}{'host'}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_uuid => $host_uuid }}); + + # Convert it to an Anvil! name. + $anvil->data->{purge}{host_uuid} = $host_uuid; + $anvil->data->{purge}{host_name} = exists $anvil->data->{hosts}{host_uuid}{$host_uuid} ? $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_name} : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "purge::host_uuid" => $anvil->data->{purge}{host_uuid}, + "purge::host_name" => $anvil->data->{purge}{host_name}, + }}); + + if (not $anvil->data->{purge}{host_name}) + { + # Bad host_uuid + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "error_0303", priority => "err", variables => { host_uuid => $anvil->data->{switches}{'host'} }}); + $anvil->nice_exit({exit_code => 1}); + } + } + else + { + # Look for the name. + $host_name = $anvil->data->{switches}{'host'}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_name => $host_name }}); + + $anvil->data->{purge}{host_name} = $host_name; + $anvil->data->{purge}{host_uuid} = $anvil->Get->host_uuid_from_name({host_name => $host_name});; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "purge::host_uuid" => $anvil->data->{purge}{host_uuid}, + "purge::host_name" => $anvil->data->{purge}{host_name}, + }}); + + if (not $anvil->data->{purge}{host_uuid}) + { + # Bad anvil name. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "error_0303", priority => "err", variables => { host_uuid => $anvil->data->{switches}{'host'} }}); + $anvil->nice_exit({exit_code => 1}); + } + } + push @{$anvil->data->{purge}{hosts}}, $anvil->data->{purge}{host_uuid}; +} +else +{ + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => ""}); + $anvil->nice_exit({exit_code => 1}); +} + +# Ask to confirm. +if (($anvil->data->{switches}{'y'}) or ($anvil->data->{switches}{'yes'})) +{ + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0241"}); +} +else +{ + if ($anvil->data->{purge}{anvil_name}) + { + # Show the Anvil! and member hosts. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0243", variables => { + anvil_name => $anvil->data->{purge}{anvil_name}, + anvil_uuid => $anvil->data->{purge}{anvil_uuid}, + }}); + + my $anvil_uuid = $anvil->data->{purge}{anvil_uuid}; + my $anvil_name = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_name}; + my $node1_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node1_host_uuid}; + my $node2_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node2_host_uuid}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + anvil_uuid => $anvil_uuid, + anvil_name => $anvil_name, + node1_host_uuid => $node1_host_uuid, + }}); + + # Node 1; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0244", variables => { + host_name => $anvil->data->{hosts}{host_uuid}{$node1_host_uuid}{host_name}, + host_uuid => $node1_host_uuid, + }}); + + # Node 2; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0244", variables => { + host_name => $anvil->data->{hosts}{host_uuid}{$node2_host_uuid}{host_name}, + host_uuid => $node2_host_uuid, + }}); + + # DR, if set. + if ($anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_dr1_host_uuid}) + { + my $dr1_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_dr1_host_uuid}; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0244", variables => { + host_name => $anvil->data->{hosts}{host_uuid}{$dr1_host_uuid}{host_name}, + host_uuid => $dr1_host_uuid, + }}); + } + } + else + { + # Ask the user to confirm the host deletion. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0242", variables => { + host_name => $anvil->data->{purge}{host_name}, + host_uuid => $anvil->data->{purge}{host_uuid}, + }}); + } + print $anvil->Words->string({key => "message_0021"})." "; + + chomp(my $answer = ); + if ((lc($answer) ne "y") && (lc($answer) ne "yes")) + { + print "\n".$anvil->Words->string({key => "message_0022"})."\n\n"; + $anvil->nice_exit({exit_code => 1}); + } +} + +# List all database tables in reverse order with X_host_uuid tables +$anvil->Database->find_host_uuid_columns({search_column => "host_uuid", main_table => "hosts"}); + +# For each host +foreach my $host_uuid (@{$anvil->data->{purge}{hosts}}) +{ + my $host_name = $anvil->data->{hosts}{host_uuid}{$host_uuid}{host_name}; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0245", variables => { + host_name => $host_name, + host_uuid => $host_uuid, + }}); + + my $queries = []; + foreach my $hash_ref (@{$anvil->data->{sys}{database}{uuid_tables}}) + { + my $table = $hash_ref->{table}; + my $host_uuid_column = $hash_ref->{host_uuid_column}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + 's1:table' => $table, + 's2:host_uuid_column' => $host_uuid_column, + }}); + + if (($table eq "hosts") && ($anvil->data->{purge}{anvil_uuid})) + { + # Remove this host from the Anvil! + next if not $anvil->data->{purge}{anvil_uuid}; + my $anvil_uuid = $anvil->data->{purge}{anvil_uuid}; + my $host_key = ""; + if ($host_uuid eq $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node1_host_uuid}) + { + $host_key = "anvil_node1_host_uuid"; + } + elsif ($host_uuid eq $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node2_host_uuid}) + { + $host_key = "anvil_node2_host_uuid"; + } + elsif ($host_uuid eq $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_dr1_host_uuid}) + { + $host_key = "anvil_dr1_host_uuid"; + } + if ($host_key) + { + my $query = " +UPDATE + anvils +SET + ".$host_key." = NULL, + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + anvil_uuid = ".$anvil->Database->quote($anvil_uuid)." +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$queries}, $query; + } + } + + # Just delete the record normally. + if ($anvil->data->{sys}{database}{history_table}{$table}) + { + my $query = "DELETE FROM history.".$table." WHERE ".$host_uuid_column." = ".$anvil->Database->quote($host_uuid).";"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$queries}, $query; + } + + my $query = "DELETE FROM ".$table." WHERE ".$host_uuid_column." = ".$anvil->Database->quote($host_uuid).";"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$queries}, $query; + } + + # Commit. + $anvil->Database->write({query => $queries, source => $THIS_FILE, line => __LINE__}); + + $vacuum = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { vacuum => $vacuum }}); +} + +# If we're deleting an Anvil!, clear it out now. +if ($anvil->data->{purge}{anvil_uuid}) +{ + # List all database tables in reverse order with X_host_uuid tables + $anvil->Database->find_host_uuid_columns({search_column => "anvil_uuid", main_table => "anvils"}); + + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0246", variables => { + anvil_name => $anvil->data->{purge}{anvil_name}, + anvil_uuid => $anvil->data->{purge}{anvil_uuid}, + }}); + + my $queries = []; + foreach my $hash_ref (@{$anvil->data->{sys}{database}{uuid_tables}}) + { + my $table = $hash_ref->{table}; + my $host_uuid_column = $hash_ref->{host_uuid_column}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + 's1:table' => $table, + 's2:host_uuid_column' => $host_uuid_column, + }}); + + # If the table is 'servers', we need to pull up the server_uuids and delete their definition + # file entries. + if ($table eq "servers") + { + my $query = "SELECT server_uuid FROM servers WHERE server_anvil_uuid = ".$anvil->Database->quote($anvil->data->{purge}{anvil_uuid}).";"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, 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 => { + results => $results, + count => $count, + }}); + foreach my $row (@{$results}) + { + my $server_uuid = $row->[0]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { server_uuid => $server_uuid }}); + + my $query = "DELETE FROM history.server_definitions WHERE server_definition_server_uuid = ".$anvil->Database->quote($server_uuid).";"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$queries}, $query; + + $query = "DELETE FROM server_definitions WHERE server_definition_server_uuid = ".$anvil->Database->quote($server_uuid).";"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$queries}, $query; + } + + } + + if ($anvil->data->{sys}{database}{history_table}{$table}) + { + my $query = "DELETE FROM history.".$table." WHERE ".$host_uuid_column." = ".$anvil->Database->quote($anvil->data->{purge}{anvil_uuid}).";"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$queries}, $query; + } + + my $query = "DELETE FROM ".$table." WHERE ".$host_uuid_column." = ".$anvil->Database->quote($anvil->data->{purge}{anvil_uuid}).";"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$queries}, $query; + } + + # Commit. + $anvil->Database->write({query => $queries, source => $THIS_FILE, line => __LINE__}); + + $vacuum = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { vacuum => $vacuum }}); +} + +# Vacuum the database +if ($vacuum) +{ + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0458"}); + my $query = "VACUUM FULL;"; + $anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__}); +} + +$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0025"}); +$anvil->nice_exit({exit_code => 0}); From 309aa1368403a1659adf0233b39fb98626e9d667 Mon Sep 17 00:00:00 2001 From: Digimer Date: Tue, 11 May 2021 14:25:27 -0400 Subject: [PATCH 2/2] * Updated the name of striker-purge-target in the makefile. Signed-off-by: Digimer --- tools/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/Makefile.am b/tools/Makefile.am index e95ea9f9..d1993a21 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -40,7 +40,7 @@ dist_sbin_SCRIPTS = \ striker-parse-os-list \ striker-parse-oui \ striker-prep-database \ - striker-purge-host \ + striker-purge-target \ striker-scan-network \ striker-auto-initialize-all