/usr/local/CPAN/Annelidous-snapshot/Annelidous/Search/Ubersmith.pm


#!/usr/bin/perl
#
# Annelidous - the flexibile cloud management framework
# Copyright (C) 2009  Eric Windisch <eric@grokthis.net>
# 
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
# 
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

package Annelidous::Search::Ubersmith;

# Inheritance (shorthand for using @INC/require)
use base ("Annelidous::Search");

use Annelidous::Cluster::GrokThis;
use Data::Dumper;

sub new {
	my $class=shift;
	my $self={
	    -dbh=>undef,
	    @_
	};
	bless $self, $class;

	if (defined $self->{-dbh}) {
		$self->{dbh}=$self->{-dbh};
	}

	return $self;
}

#
# CPU lookup table.
# TODO: lookup_cpu is static, must be made dynamic...
#
sub lookup_cpu {
    my $self=shift;
    my $plan=shift;
    #$self->memhash{$plan};
    my $cpuhash={
        vi05=>1,
        vi06=>1,
        vi10=>1,
        vi11=>1,
        vi20=>1,
        vi40=>1,
        gvs=>2,
        gvm=>2,
        gvl=>2,
        gvx=>2
    };
    return $cpuhash->{$plan};
}


#
# Memory lookup table.
# TODO: lookup_mem is static, must be made dynamic...
#
sub lookup_mem {
    my $self=shift;
    my $plan=shift;
    #$self->memhash{$plan};
    my $memhash={
        vi05=>64,
        vi06=>64,
        vi10=>128,
        vi11=>128,
        vi20=>256,
        vi40=>512,
        gvs=>96,
        gvm=>128,
        gvl=>256,
        gvx=>512
    };
    return $memhash->{$plan};
}

#
# The base find method on which the others are built.
#
sub _find {
    my $self=shift;
    my $where=shift;
    my @args=@_;
    my @cl=$self->db_fetch("select distinct first as first_name,last as last_name,username,code as plan,email, desserv as description,PACKAGES.packid as id,PACKAGES.servername as host,CLIENT.clientid as client_id from PACKAGES join CLIENT on PACKAGES.clientid=CLIENT.clientid join plans on plans.plan_id=PACKAGES.plan_id ".$where, @args);

    # strcmp to detect an ipv4 address which is specified in ipv6 notation with "0000::" prefix
    my $sth=$self->{dbh}->prepare("select INET_NTOA(conv(addr,16,10)) as ip from ip_assignments where ip_assignments.service_id=? and strcmp('0000',substr(addr,1,4))=0");

    # IPv6 addresses
    # We have a simple, but long, conversion from an IPv6 address without separators, to one with separators.
    my $sth6=$self->{dbh}->prepare("select concat(substr(addr,1,4),':',substr(addr,5,4),':',substr(addr,9,4),':',substr(addr,13,4),':',substr(addr,17,4),':',substr(addr,21,4),':',substr(addr,25,4),':',substr(addr,29,4),substr(addr,33,4)) as ip6 from ip_assignments where ip_assignments.service_id=? and strcmp('0000',substr(addr,1,4))");

    foreach my $client (@cl) {
		$client->{'cpu_count'}=$self->lookup_cpu($client->{'plan'});
		$client->{'memory'}=$self->lookup_mem($client->{'plan'});
        
        $sth->execute($client->{'id'});
        my @ip4;
        while (my $addrinfo=$sth->fetchrow_hashref) {
            push @ip4, $addrinfo->{'ip'};
        }

        $sth6->execute($client->{'id'});
        my @ip6;
        while (my $addrinfo=$sth6->fetchrow_hashref) {
            push @ip6, $addrinfo->{'ip6'};
        }

        $client->{'ip6'}=join ' ', @ip6;
        $client->{'ip4'}=join ' ', @ip4;
        $client->{'ip'}=join ' ', ($client->{'ip4'}, $client->{'ip6'});

        my $ip6router;
        if ($client->{'ip6'}) {
			$client->{'ip6'} =~ /(.*).(\/\w+)/;
			$client->{'ip6router'}=$1."1".$2;

			my $ip6in4=$ip4[0];
			$ip6in4 =~ s/\./:/g;
			$client->{'ip6'} =~ /^(\w+:\w+:\w+:\w+:)/;
			$client->{'ip6in4'} = $1.$ip6in4;
		}

        # replace any C/v/Vs with c'
        $client->{username} =~ s/^(c|v)?/c/i;

		# TODO: FIXME: Bitness check from uber
		$client->{bitness} = 64;
		#$client->{bitness} = 32;
    }

    $sth->finish();
    $sth6->finish();
    return @cl;
}

#
# Fetches all clients
#
sub find {
    my $self=shift;
	my $where=shift;
	if (defined ($where)) {
		#print $where."\n";
		$where =~ s/^and//g;
		#print $where."\n";
		$where = "where category='vps' and ".$where;
	} else {
		$where = "where category='vps'";
	}
    return $self->_find($where,@_);
}

sub find_inactive {
	my $self=shift;
	my $where=shift;
	$where="PACKAGES.active!=1 ".$where;
	return $self->find($where,@_);
}
	
sub find_active {
	my $self=shift;
	my $where=shift;
	$where="PACKAGES.active=1 ".$where;
	return $self->find($where,@_);
}

#
# Fetches clients based on username
#
sub find_byusername {
    my $self=shift;
    my $username=shift;
    return $self->find("and username regexp ?", $username);
}
sub find_active_byusername {
    my $self=shift;
    my $username=shift;
    return $self->find_active("and username regexp ?", $username);
}

sub by_id {
    my $self=shift;
    my $id=shift;
	#print "Hunting for id: ";
	#print Dumper $id."\n";
	# Do not discriminate on active or not.
    return $self->find("and PACKAGES.packid=?", $id);
}

sub by_clientid {
    my $self=shift;
    my $id=shift;
	my $active=shift;
	my $where="and CLIENT.clientid=?";
	if ($active) {
		$where.=" and PACKAGES.active=1";
	}
	# Do not discriminate on active or not.
    return $self->find($where, $id);
}

#
# Fetches clients based on email address
#
sub find_bymail {
    my $self=shift;
    my $email=shift;
    return $self->find ("and email=?", $email);
}

#
# Fetches clients based on their host
#
sub find_byhost {
    my $self=shift;
    my $hostname=shift;
    return $self->find ("and servername=?", $hostname);
}

sub get_default_cluster {
	my $self=shift;
	return Annelidous::Cluster::GrokThis->new($self);
}

# Authentication per-service
# I.E. sub-accounts
sub auth_service {
    my $self=shift;
    my $username=shift;
    my $password=shift;
    my $c=$self->find("and username=? and password=?", $username,$password);

	# Return the single authorized package/service.
	return [$c->{id}];
}

# Authentication per-account
# I.E. master accounts
sub auth_account {
    my $self=shift;
    my $username=shift;
    my $password=shift;
    my @cl=$self->db_fetch("select clientid from CLIENT where clientid=? and password=? limit 1",$username,$password);
	if ($#cl > -1) {
		return $self->authorized_services($cl[0]->{clientid});
	}
	return 0;
}

sub authorized_services {
	my $self=shift;
	my $clientid=shift;
	my @services=();
    # Iterate over the options.
    foreach my $pack ($self->by_clientid($clientid)) {
        # Ignore broken guests
        next if ($pack->{username} =~ /^c?$/ || ! $pack->{ip});

        # Just get the first IP:
        @_=split (/ /, $pack->{ip});
        my $ip=$_[0];

        # Skip any entries that don't have IPs.
        next unless ($ip);
		push @services, $pack->{id};
	}
	# Authorized services by packid.
	return @services;
}


1;