#!/usr/bin/env perl IO::Socket::SSL::set_ctx_defaults( SSL_verify_mode => SSL_VERIFY_NONE ); package INF::SuperMicro; use HTTP::Status; use HTTP::Cookies; use IO::Socket::SSL qw(); use HTML::TreeBuilder; use HTTP::Request::Common; use LWP::UserAgent; use URI::Escape; use File::Temp qw/ tempfile tempdir /; use XML::Simple; use Data::Dumper; use JSON::PP; sub login($) { my $self = shift; my $post = POST( $self->{bmc_url} . '/cgi/login.cgi' ); my $form_content = 'name=' . $self->{user} . '&' . 'pwd=' . $self->{password}; $post->header( 'Content-Type' => 'application/x-www-form-urlencoded' ); $post->header( 'Content-Length' => length($form_content) ); $post->content($form_content); my $res = $self->{ua}->request($post); unless ( $res->is_success ) { print STDERR "Login failed - did not get 200\n"; print Dumper($res); $self->{logged_in} = undef; return -1; } $self->{logged_in} = 1; #my $json = decode_json( $res->content ); #$self->{skey} = $json->{session_key}; #print "Session key ".$self->{skey}."\n"; return 0; } sub view($) { my $self = shift; $self->login() unless defined $self->{logged_in}; my $get = GET( $self->{bmc_url} . '/cgi/url_redirect.cgi?url_name=ikvm&url_type=jwsk' ); my $res = $self->{ua}->request($get); unless ( $res->is_success ) { print STDERR "JWSK frequest failed - did not get 200\n"; return -1; } my $xml = new XML::Simple; my $c = $res->content; $c =~ s/^[\s\n\r]+//s; my $data = $xml->XMLin($c); return undef unless defined $data->{'application-desc'}; my $args = $data->{'application-desc'}->{'argument'}; return undef unless defined $args; mkdir $ENV{HOME} . "/.supermicro_kvm"; chdir $ENV{HOME} . "/.supermicro_kvm"; my $prefix = "/usr/local/share/inf/supermicro/"; return undef unless $c =~ /iKVM__V([A-Za-z0-9\.]+).jar/; my $v = $1; if ( -d $prefix . $v ) { printf "Known version\n"; $prefix .= $v . '/'; } elsif ( scalar(@$args) == 8 ) { printf "Unknow version $v, 8 args using 1.69.21\n"; $prefix .= '1.69.21.0x0/'; } else { printf "Unknow version $v, 10 args using 1.69.25\n"; $prefix .= '1.69.25.0x0/'; } if ( defined $self->{proxy_host} ) { my $tmp = File::Temp->new( UNLINK => 0, SUFFIX => '.cnf' ); select( ( select($tmp), $| = 1 )[0] ); print $tmp "server = 127.0.0.1\n"; print $tmp "server_port = " . $self->{proxy_port} . "\n"; print $tmp "local = 127.0.0.0/255.0.0.0\n"; $ENV{'LD_PRELOAD'} = 'libtsocks.so'; $ENV{'TSOCKS_CONF_FILE'} = $tmp->filename; print "Filename is $tmp->filename\n"; } my $jars = [ $prefix . "iKVM__V" . $v . ".jar" ]; my $cp = join( ':', @$jars ); push @$java_args, "-cp"; push @$java_args, $cp; push @$java_args, "-Djava.library.path=" . $prefix; push @$java_args, "tw.com.aten.ikvm.KVMMain"; system( "echo", "java", @$java_args, @$args ); system( "java", @$java_args, @$args ); } sub get_host_power($) { my ($self) = @_; $self->login() unless defined $self->{logged_in}; my $post = POST( $self->{bmc_url} . '/cgi/ipmi.cgi' ); my $form_content = 'op=POWER_INFO.XML&r=(0%2C0)'; $post->header( 'Content-Type' => 'application/x-www-form-urlencoded' ); $post->header( 'Content-Length' => length($form_content) ); $post->content($form_content); my $res = $self->{ua}->request($post); unless ( $res->is_success ) { print STDERR " get host power - did not get 200\n"; return undef; } my $xml = new XML::Simple; my $c = $res->content; $c =~ s/^[\s\n\r]+//s; my $data = $xml->XMLin($c); return undef unless defined $data->{'POWER_INFO'}; return undef unless defined $data->{'POWER_INFO'}->{'POWER'}; return undef unless defined $data->{'POWER_INFO'}->{'POWER'}->{'STATUS'}; return $data->{'POWER_INFO'}->{'POWER'}->{'STATUS'}; } sub set_host_power($$) { my ( $self, $what ) = @_; $self->login() unless defined $self->{logged_in}; my $post = POST( $self->{bmc_url} . '/cgi/ipmi.cgi' ); my $form_content = 'op=POWER_INFO.XML&r=(1%2C' . $what . ')'; $post->header( 'Content-Type' => 'application/x-www-form-urlencoded' ); $post->header( 'Content-Length' => length($form_content) ); $post->content($form_content); my $res = $self->{ua}->request($post); unless ( $res->is_success ) { print STDERR " get host power - did not get 200\n"; return 0; } my $xml = new XML::Simple; my $c = $res->content; $c =~ s/^[\s\n\r]+//s; my $data = $xml->XMLin($c); return 0 unless defined $data->{'POWER_INFO'}; return 0 unless defined $data->{'POWER_INFO'}->{'POWER'}; return 0 unless defined $data->{'POWER_INFO'}->{'POWER'}->{'STATUS'}; return 1; } sub reset($) { my $self = shift; return $self->set_host_power('0'); } sub port_off($) { my $self = shift; return $self->set_host_power('1'); } sub orderly_shutdown($) { my $self = shift; return $self->set_host_power('2'); } sub port_on($) { my $self = shift; return $self->set_host_power('3'); } sub port_cycle($) { my $self = shift; return $self->set_host_power('5'); } sub port_state_get($$) { my $self = shift; return $self->get_host_power(); } sub port_state_get_no_cache($$) { my $self = shift; return $self->get_host_power(); } sub port_name_get($$) { my $self = shift; return $self->{name}; } sub name_get($) { my $self = shift; return $self->{name}; } sub pdu_load_get($) { return "N/A"; } sub psu_status($) { return "N/A"; } sub port_count($) { return 1; } sub port_id_get_by_number($$) { return "K0"; } sub logout($) { my $self = shift; } sub new ($;$) { my ( $class, $parm ) = @_; my $self; $self->{ua} = my $ua = LWP::UserAgent->new; $self->{cookie_jar} = HTTP::Cookies->new(); $self->{ua}->cookie_jar( $self->{cookie_jar} ); $self->{host} = $parm->{host} || "127.0.0.1"; $self->{user} = $parm->{user} || "ADMIN"; $self->{password} = $parm->{password} || "ADMIN"; $self->{name} = $parm->{name} || $self->{host}; $self->{bmc_url} = $parm->{bmc_url} || 'https://' . $self->{host}; $self->{ua}->ssl_opts( SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE, verify_hostname => 0, ); if ( defined $parm->{proxy_host} ) { $self->{ua}->proxy( [qw(http https)] => "socks://" . $parm->{proxy_host} . ":" . $parm->{proxy_port} ); $self->{proxy_host} = $parm->{proxy_host}; $self->{proxy_port} = $parm->{proxy_port}; } $self->{logged_in} = undef; return bless $self, $class; } 1;