#!/usr/bin/env perl IO::Socket::SSL::set_ctx_defaults( SSL_verify_mode => SSL_VERIFY_NONE ); package INF::DSRx020; use IO::Socket::SSL qw(); use HTML::TreeBuilder; use HTTP::Request::Common; use LWP::UserAgent; use LWP::Protocol::socks; use URI::Escape; use File::Temp qw/ tempfile tempdir /; use XML::Simple; use Data::Dumper; my $prefix = "/usr/local/share/inf/avocent/"; my $jars = [ $prefix . "avctLinuxLib.jar", $prefix . "avctMacOSXLib.jar", $prefix . "avctSolarisLib.jar", $prefix . "avctVideo.jar", $prefix . "avctVM.jar", $prefix . "avctWin32Lib.jar", $prefix . "avmLinuxLib.jar", $prefix . "avmMacOSXLib.jar", $prefix . "avmSolarisLib.jar", $prefix . "avmWin32Lib.jar", $prefix . "jpcscdll.jar", $prefix . "jpcscso.jar" ]; sub trim($) { my $s = shift; if ( defined $s ) { $s =~ s/^[\s\t\n\r\xa0]+//s; $s =~ s/[\t\n\r\s\xa0]+$//s; } return $s; } sub flush($) { my $self = shift; delete $self->{names}; delete $self->{ports}; delete $self->{statuses}; } sub login($) { my $self = shift; my $post = POST( $self->{kvm_url} . 'file=login', [ action => "SAVE", saveParms => "login", filename => "login", spcDevice => "(NULL)", spcSocket => "(NULL)", spcInlet => "(NULL)", userindex => "(NULL)", id => "(NULL)", index => "(NULL)", loginUsername => $self->{user}, loginPassword => $self->{password}, htmlLanguage => '0' ] ); my $res = $self->{ua}->request($post); unless ( $res->is_success ) { print STDERR "Login failed - did not get 200\n"; $self->{uid} = undef; return -1; } my $content = $res->content; if ( $content =~ /errorMsg/ ) { print STDERR "Login failed - invalid password\n"; $self->{uid} = undef; return -1; } my $refresh = $res->headers->header('refresh'); unless ( $refresh =~ /userid=(\d+)$/i ) { print STDERR "Login failed - no uid found\n"; $self->{uid} = undef; return -1; } $self->{uid} = $1; # print "userid = ", $self->{uid}, "\n"; return 0; } sub do_get($$) { my ( $self, $args ) = @_; my @uea; my $url; do { $url = $self->{kvm_url}; @uea = (); for my $key ( keys %$args ) { push @uea, $key . '=' . uri_escape( $args->{$key} ); } push @uea, 'userID=' . $self->{uid}; $url .= join( '&', @uea ); # print STDERR "get from url=$url\n"; my $get = GET($url); my $res = $self->{ua}->request($get); return undef unless $res->is_success; return $res unless $res->content =~ /form name="loginForm"/; return undef if $self->login; } while (1); } sub do_post($$$) { my ( $self, $args, $postargs ) = @_; my @uea; my $url; do { $url = $self->{kvm_url}; @uea = (); for my $key ( keys %$args ) { push @uea, $key . '=' . uri_escape( $args->{$key} ); } push @uea, 'userID=' . $self->{uid}; $url .= join( '&', @uea ); # print STDERR "post to url=$url\n"; my $post = POST( $url, [ @{$postargs}, userID => $self->{uid} ] ); my $res = $self->{ua}->request($post); return undef unless $res->is_success; return $res unless $res->content =~ /form name="loginForm"/; return undef if $self->login; } while (1); } sub spc_set_power($$$$) { my ( $self, $spc, $socket, $action ) = @_; my $res = $self->do_post( { file => 'overview-spc', spcDevice => $spc, spcSocket => $socket }, [ file => 'overview-spc', saveParms => 'overview-spc', spcDevice => $spc, spcSocket => $socket, action => $action, ] ); $self->flush(); #name=>Test+power+port #oids=>','.$socket return undef unless $res->is_success; return "Success"; } sub port_cycle($$) { my ( $self, $port ) = @_; if ( $port =~ /P(\d+)\.(\d+)/ ) { return $self->spc_set_power( $1, $2, 'POWER_CYCLE' ); } else { return undef; } } sub port_on($$) { my ( $self, $port ) = @_; if ( $port =~ /P(\d+)\.(\d+)/ ) { return $self->spc_set_power( $1, $2, 'POWER_ON' ); } else { return undef; } } sub port_off($$) { my ( $self, $port ) = @_; if ( $port =~ /P(\d+)\.(\d+)/ ) { return $self->spc_set_power( $1, $2, 'POWER_OFF' ); } else { return undef; } } sub spc_get_state($$$) { my ( $self, $spc, $socket ) = @_; my $res = $self->do_get( { file => 'overview-spc', spcDevice => $spc, spcSocket => $socket } ); return undef unless defined $res; my $tree = HTML::TreeBuilder->new; $tree->parse( $res->content ); my $thing = $tree->look_down( 'id' => 'status-label-power-1' ); if ( not defined $thing ) { $tree->delete; return undef; } my $ret = ""; for my $c ( $thing->content_list ) { $ret .= $c unless ref $c; } $tree->delete; $ret = trim($ret); return $ret; } sub spc_get_name($$$) { my ( $self, $spc, $socket ) = @_; my $res = $self->do_get( { file => 'overview-spc', spcDevice => $spc, spcSocket => $socket } ); return undef unless defined $res; my $tree = HTML::TreeBuilder->new; $tree->parse( $res->content ); my $thing = $tree->look_down( 'name' => 'spcsocketname' ); if ( not defined $thing ) { $tree->delete; return undef; } my $ret = $thing->attr('value'); print "ret=$ret\n"; $tree->delete; $ret = trim($ret); return $ret; } sub spc_set_name($$$$) { my ( $self, $spc, $socket, $name ) = @_; my ( $self, $spc, $socket, $action ) = @_; my $res = $self->do_post( { file => 'overview-spc', spcDevice => $spc, spcSocket => $socket }, [ file => 'overview-spc', saveParms => 'overview-spc', spcDevice => $spc, spcSocket => $socket, spcsocketname => $name, action => 'SAVE', ] ); $self->flush(); return undef unless $res->is_success; return "Success"; } sub elm_to_txt($) { my $e = shift; my $ret = ""; for my $c ( $e->content_list ) { $ret .= $c unless ref $c; } return $ret; } sub elm_to_txt_with_spaces($) { my $e = shift; my $ret = ""; for my $c ( $e->content_list ) { $ret .= $c unless ref $c; $ret .= " "; } return $ret; } sub get_names($) { my $self = shift; my $res = $self->do_get( { file => 'view-units-by-target-devices' } ); return -1 unless defined $res; my $tree = HTML::TreeBuilder->new; $tree->parse( $res->content ); $self->{ports} = []; $self->{names} = {}; $self->{statuses} = {}; for my $thing ( $tree->look_down( 'title' => 'Unique name of target device. Clicking moves to the Unit Overview window for the TD.' ) ) { my $p = $thing->parent(); my $tr = $p->parent->parent; my $href = $p->attr('href'); my $status = ""; my $name = elm_to_txt($thing); for my $span ( $tr->look_down( sub { $_[0]->attr('id') =~ /status-label-unit/ } ) ) { $status .= elm_to_txt_with_spaces($span); } $name = trim($name); $status = trim($status); $status =~ s/Idle/On/; #print "href=$href name=$name\n"; #$p->dump(); if ( $href =~ /file=overview-spc.*spcDevice=(\d+)&spcSocket=(\d+)/ ) { my $port = 'P' . $1 . '.' . $2; push @{ $self->{ports} }, $port; $self->{names}->{$port} = $name; $self->{statuses}->{$port} = $status; } if ( $href =~ /file=overview-dsr.*index=(\d+)/ ) { my $port = 'K' . $1; push @{ $self->{ports} }, $port; $self->{names}->{$port} = $name; $self->{statuses}->{$port} = $status; } } $tree->delete; return 0; } sub port_name_set($$$) { my ( $self, $port, $name ) = @_; if ( $port =~ /P(\d+)\.(\d+)/ ) { return $self->spc_set_name( $1, $2, $name ); } elsif ( $port =~ /K(\d+)/ ) { my $index = $1; my $res = $self->do_post( { file => 'view-units-by-target-devices' }, [ file => 'overview-dsr', saveParms => 'overview-dsr', name => '', index => $index, targetname => $name, action => 'SAVE', ] ); #oid => '141057', $self->flush(); return undef unless $res->is_success; return "Success"; } else { return undef; } } sub port_state_get_no_cache($$) { my ( $self, $port ) = @_; if ( $port =~ /P(\d+)\.(\d+)/ ) { return $self->spc_get_state( $1, $2 ); } else { return "Unknown"; } } sub port_state_get($$) { my ( $self, $port ) = @_; return $self->{statuses}->{$port} if defined $self->{statuses}; return $self->port_state_get_no_cache($port); } sub port_name_get($$) { my ( $self, $port ) = @_; return $self->{names}->{$port} if defined $self->{ports}; $self->get_names(); return $self->{names}->{$port}; } sub port_id_get_by_number($$) { my ( $self, $n ) = @_; return $self->{ports}->[ $n - 1 ]; } sub port_count($) { my $self = shift; return scalar( @{ $self->{ports} } ) if defined $self->{ports}; $self->get_names(); return scalar( @{ $self->{ports} } ); } sub port_get_topology($$) { my ( $self, $port ) = @_; return unless $port =~ /K(\d+)/; my $res = $self->do_get( { file => 'jnlp', index => $1 } ); return undef unless defined $res; 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; my $ret = undef; for my $arg (@$args) { next unless $arg =~ /^path=a:[^,]*,p:([0-9]+),c:([0-9]+),/; print $arg, " -> ", $1, " ", $2, "\n"; return [ $1, $2 ]; } return undef; } sub view($$) { my ( $self, $port ) = @_; return unless $port =~ /K(\d+)/; my $res = $self->do_get( { file => 'jnlp', index => $1 } ); return undef unless defined $res; 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; my $cp = join( ':', @$jars ); mkdir $ENV{HOME} . "/.avocent_kvm"; chdir $ENV{HOME} . "/.avocent_kvm"; #print join( ' ', ( "java", "-cp", $cp, "com.avocent.video.Stingray", @$args ) ), "\n"; if ( $self->{proxy_host} ) { system( "echo", "java", "-Djava.security.disableSystemPropertiesFile=1", "-Djava.security.properties=/usr/local/share/inf/avocent/crypto.properties", "-Djava.net.preferIPv4Stack=true", "-Djava.net.useSystemProxies=false", "-DsocksProxyVersion=4", "-DsocksProxySet=true", "-DsocksProxyHost=" . $self->{proxy_host}, "-DsocksProxyPort=" . $self->{proxy_port}, "-cp", $cp, "com.avocent.video.Stingray", @$args ); system( "java", "-Djava.security.disableSystemPropertiesFile=1", "-Djava.security.properties=/usr/local/share/inf/avocent/crypto.properties", "-Djava.net.preferIPv4Stack=true", "-Djava.net.useSystemProxies=false", "-DsocksProxyVersion=4", "-DsocksProxySet=true", "-DsocksProxyHost=" . $self->{proxy_host}, "-DsocksProxyPort=" . $self->{proxy_port}, "-cp", $cp, "com.avocent.video.Stingray", @$args ); } else { system( "echo", "java", "-Djava.security.disableSystemPropertiesFile=1", "-Djava.security.properties=/usr/local/share/inf/avocent/crypto.properties", "-cp", $cp, "com.avocent.video.Stingray", @$args ); system( "java", "-Djava.security.disableSystemPropertiesFile=1", "-Djava.security.properties=/usr/local/share/inf/avocent/crypto.properties", "-cp", $cp, "com.avocent.video.Stingray", @$args ); } } sub pdu_load_get($) { my $self = shift; my $ret = []; my $res = $self->do_get( { file => 'view-spcs' } ); return undef unless defined $res; my $tree = HTML::TreeBuilder->new; $tree->parse( $res->content ); my $t1 = $tree->look_down( 'class' => 'list-filter-row' ); return undef unless defined $t1; my $div = $t1->parent; return undef unless defined $div; my $tables = []; for my $t ( $div->content_list ) { next unless ref $t; push @$tables, $t if $t->tag eq 'table'; } my $t2 = $tables->[1]; return undef unless defined $t2; my $t3 = [ $t2->look_down( "_tag" => 'table' ) ]; return undef unless scalar(@$t3) > 1; my $table = $t3->[1]; return undef unless defined $table; my $rows = [ $table->look_down( "_tag" => 'tr' ) ]; return undef unless scalar(@$rows) > 1; my $row_data = []; for my $row (@$rows) { push @$row_data, [ $row->look_down( "_tag" => 'td' ) ]; } my $top_row = shift @$row_data; for ( my $i = 0 ; $i < scalar(@$top_row) ; ++$i ) { my $title = $top_row->[$i]; next unless defined $title; $title = elm_to_txt($title); next unless $title =~ /current/i; for my $row (@$row_data) { my $value = $row->[$i]; next unless $value; $value = elm_to_txt($value); trim($value); next unless length($value); next if $value =~ /N\/A/; push @$ret, sprintf( "%.2f", $value ); } } push @$ret, "N/A" if scalar(@$ret) == 0; return join( ",", @$ret ); } sub psu_status($) { return undef; } sub name_get($) { my $self = shift; my $res = $self->do_get( { file => 'appliance-overview' } ); return undef unless defined $res; my $tree = HTML::TreeBuilder->new; $tree->parse( $res->content ); my $thing = $tree->look_down( 'name' => 'appliancename' ); return undef unless defined $thing; my $ret = $thing->attr('value'); $tree->delete; $ret = trim($ret); return $ret; } sub name_set($$) { my ( $self, $name ) = @_; my $res = $self->do_post( { file => 'appliance-overview' }, [ file => 'appliance-overview', saveParms => 'appliance-overview', appliancename => $name, spcSocket => $socket, action => 'SAVE', ] ); #name=>Test+power+port #oid=>'148737', #oids=>'' return -1 unless $res->is_success; return 0; } sub logout($) { my $self = shift; } sub new ($;$) { my ( $class, $parm ) = @_; my $self; $self->{ua} = my $ua = LWP::UserAgent->new; $self->{host} = $parm->{host} || "127.0.0.1"; 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->{user} = $parm->{user} || "Admin"; $self->{password} = $parm->{password} || ""; $self->{kvm_url} = $parm->{kvm_url} || 'https://' . $self->{host} . '/cgi-bin/kvm.cgi?&'; $self->{userid} = undef; $self->{ua}->ssl_opts( SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE, verify_hostname => 0, ); return bless $self, $class; } 1; #my $dsr = new DSRx020( # { # # host => 'https://freyja-brisingamen.uk.bromium.net', # #host => 'http://localhost:81', # host => 'https://10.32.95.20', # user => 'Admin', # password=>'Woow7See' # } #); # ##$dsr->login(); # ##print "1.8 is called ", $dsr->spc_get_name( 1, 8 ), "\n"; ##print "1.8 is state ", $dsr->spc_get_state( 1, 8 ), "\n"; ##print "1.8 power off: ", $dsr->spc_power_off( 1, 8 ), "\n"; ##print "1.8 is state ", $dsr->spc_get_state( 1, 8 ), "\n"; # #$dsr->get_names();