1#!/usr/bin/perl -w
2#
3# MIT Public License
4# http://www.opensource.org/licenses/MIT
5#
6# Copyright (C) 2012-2014 Tieto Corporation.
7#
8# Permission is hereby granted, free of charge, to any person obtaining a copy
9# of this software and associated documentation files (the "Software"), to deal
10# in the Software without restriction, including without limitation the rights
11# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12# copies of the Software, and to permit persons to whom the Software is
13# furnished to do so, subject to the following conditions:
14#
15# The above copyright notice and this permission notice shall be included in all
16# copies or substantial portions of the Software.
17#
18# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24# THE SOFTWARE.
25
26use strict;
27#use lib "/srv/xmlrpc/perl-lib";
28use LWP::UserAgent;
29use HTTP::Request::Common;
30eval {
31  require IO::Socket::SSL;
32  import IO::Socket::SSL;
33};
34die "Info: Please install Net::SSLeay perl module\n      yum install perl-Net-SSLeay\n      apt-get install libnet-ssleay-perl\n" if $@;
35use Net::SSLeay;
36use MIME::Base64;
37use Getopt::Long;
38eval {
39  require JSON;
40  import JSON;
41};
42die "Info: Please install JSON perl module\n      yum install perl-JSON\n      apt-get install libjson-perl\n" if $@;
43eval {
44  require Date::Parse;
45  import Date::Parse;
46};
47die "Info: Please install Date::Parse perl module\n      yum install perl-TimeDate\n      apt-get install libtimedate-perl\n" if $@;
48
49my $verbose = 0;
50my %keys;
51my %remove;
52# Generate password string with "echo -n password | base64"
53my $servers = {
54	"test1" => {
55		url      => "http://localhost",
56		username => "admin",
57		password => "YWRtaW4=",
58	},
59	# "server2" => {
60	#	url      => "https://server2.acme.com:444",
61	#	username => "admin",
62	#	password => "YWRtaW4=",
63	#	cert     => "/var/cfengine/httpd/ssl/certs/server2.cert",
64	#	},
65	};
66
67GetOptions('v|verbose' => \$verbose) || die "Usage: $0 [--verbose]\n";
68
69###########################################################################
70# Setup server certificate
71#
72# This is required to make this script work with libnet-http-perl version > 6
73# Hostname is checked by devault in versions > 6 and we only use IP address
74$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0;
75
76for my $server (sort keys %$servers) {
77	print "Server: $server  ";
78
79	# Set up server cert
80	if ( exists $servers->{$server}->{cert} ) {
81		eval {
82			IO::Socket::SSL::set_ctx_defaults(
83				verify_mode => Net::SSLeay->VERIFY_PEER(),
84				ca_file => "$servers->{$server}->{cert}",
85			);
86		};
87	}
88
89	my $query = '{ "query": "SELECT Hosts.HostKey AS \"Host key\", Hosts.HostName AS \"Host name\", Hosts.LastReportTimeStamp AS \"Last report time\", Hosts.FirstReportTimeStamp AS \"First report-time\", Hosts.IPAddress AS \"IP address\" FROM Hosts" }';
90
91	my $ua = new LWP::UserAgent(agent => "CLEANUP/1.0");
92	my $req = HTTP::Request->new(POST => "$servers->{$server}->{url}/api/query");
93
94	$req->authorization_basic($servers->{$server}->{username}, decode_base64($servers->{$server}->{password}));
95	$req->content($query);
96	my $res = $ua->request($req);
97	unless ( $res->is_success ) {
98		print "Query failed\n" if $verbose;
99		next;
100	}
101	print "Query successful\n" if $verbose;
102	my $decoded_json = decode_json( $res->content );
103
104	for my $row ( @{$decoded_json->{data}[0]->{rows}} ) {
105		my $ip_address = "Unknown";
106		my $host_key = $row->[0];
107		my $hostname = $row->[1];
108		my $report_timestamp = str2time($row->[2]);
109		my $first_report = str2time($row->[3]);
110		$ip_address = $row->[4] if $row->[4];
111		$first_report =~ s/\..*//;
112		$report_timestamp =~ s/\..*//;
113		# print "$host_key $hostname $first_report $report_timestamp $ip_address\n" if $verbose;
114		$keys{$host_key}->{$server}->{first_report} = $first_report;
115		$keys{$host_key}->{$server}->{hostname} = $hostname;
116		$keys{$host_key}->{$server}->{report_timestamp} = $report_timestamp;
117		$keys{$host_key}->{$server}->{ip_address} = $ip_address;
118	}
119}
120
121# Remove all hosts with report_timestamp older then 90 days
122for my $key ( sort keys %keys ) {
123	for my $server ( sort keys %{$keys{$key}} ) {
124		if ( $keys{$key}->{$server}->{report_timestamp} < time() - 60 * 60 * 24 * 90 ) {
125			print "Host key $key, hostname $keys{$key}->{$server}->{hostname} on server $server is older then 90 days\n" if $verbose;
126			$remove{$key}->{$server} = 1;
127		}
128	}
129}
130
131# Remove all duplicate keys if report_timestamp is 24 hours older then the newest one
132for my $key ( sort keys %keys ) {
133	my $newest = 0;
134	for my $server ( sort keys %{$keys{$key}} ) {
135		$newest = $keys{$key}->{$server}->{report_timestamp} if $keys{$key}->{$server}->{report_timestamp} > $newest;
136	}
137	for my $server ( sort keys %{$keys{$key}} ) {
138		if ( $keys{$key}->{$server}->{report_timestamp} < $newest - 60 * 60 * 24 ) {
139			print "Host key $key, hostname $keys{$key}->{$server}->{hostname} on server $server is a duplicate key and it is 24 hours older then the newest one\n";
140			$remove{$key}->{$server} = 1;
141		}
142	}
143}
144
145# Remove all duplicate hostnames if report_timestamp is 24 hours older then the newest one
146my %hosts;
147for my $key ( sort keys %keys ) {
148	for my $server ( sort keys %{$keys{$key}} ) {
149		$hosts{$keys{$key}->{$server}->{hostname}}->{newest} = 0 unless exists $hosts{$keys{$key}->{$server}->{hostname}};
150		$hosts{$keys{$key}->{$server}->{hostname}}->{server}->{$server}->{$key} = $keys{$key}->{$server}->{report_timestamp};
151		$hosts{$keys{$key}->{$server}->{hostname}}->{newest} = $keys{$key}->{$server}->{report_timestamp} if $keys{$key}->{$server}->{report_timestamp} > $hosts{$keys{$key}->{$server}->{hostname}}->{newest};
152	}
153}
154for my $hostname ( sort keys %hosts ) {
155	for my $server ( sort keys %{$hosts{$hostname}->{server}} ) {
156		for my $key ( sort keys %{$hosts{$hostname}->{server}->{$server}} ) {
157			if ( $hosts{$hostname}->{server}->{$server}->{$key} < $hosts{$hostname}->{newest} - 60 * 60 * 24 ) {
158				print "Host key $key, hostname $hostname on server $server is a duplicate hostname and it is 24 hours older then the newest one\n";
159				$remove{$key}->{$server} = 1;
160			}
161		}
162	}
163}
164
165# Remove hosts
166for my $key ( sort keys %remove ) {
167	for my $server ( sort keys %{$remove{$key}} ) {
168		print "Delete key $key from server $server: " if $verbose;
169
170		# Set up server cert
171		if ( exists $servers->{$server}->{cert} ) {
172			eval {
173				IO::Socket::SSL::set_ctx_defaults(
174					verify_mode => Net::SSLeay->VERIFY_PEER(),
175					ca_file => "$servers->{$server}->{cert}",
176				);
177			};
178		}
179
180		my $ua = new LWP::UserAgent(agent => "CLEANUP/1.0");
181		my $req = HTTP::Request->new(DELETE => "$servers->{$server}->{url}/api/host/$key");
182		$req->authorization_basic($servers->{$server}->{username}, decode_base64($servers->{$server}->{password}));
183		my $res = $ua->request($req);
184		unless ( $res->is_success ) {
185			print "Failed\n" if $verbose;
186			exit 1;
187		}
188		print "Successful\n" if $verbose;
189	}
190}
191