Advertisement
Guest User

Untitled

a guest
Oct 22nd, 2016
116
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 8.02 KB | None | 0 0
  1. #!/usr/bin/env perl
  2. # Gets dom0s from nagios and acts as a WS proxy between noVNC and XVP
  3. #
  4. # https://github.com/jberger/Mojo-Websockify
  5. # http://xapi-project.github.io/xen-api/
  6. # https://wiki.openstack.org/wiki/VNCConsoleCleanup
  7. # https://tools.ietf.org/html/rfc6143
  8.  
  9. use Mojolicious::Lite;
  10. use DBIx::Connector;
  11.  
  12. #app->attr('novnc_url' => 'http://kanaka.github.io/noVNC/noVNC/vnc_auto.html');
  13. app->attr('novnc_url' => '/noVNC/vnc_auto.html');
  14. app->static->paths->[0] = $ENV{PWD};
  15.  
  16. helper api_call => sub {
  17.   my $args;
  18.   (my $c, my $cb, $args->{hostname}, $args->{method}, @{$args->{params}} ) = @_;
  19.   $args->{params_string} .= '<param><value><string>'.$_.'</string></value></param>' foreach @{ $args->{params} };
  20.   $c->ua->post(  $args->{hostname}
  21.               => { Content_Type => 'text/xml' }
  22.                 => '<?xml version="1.0" encoding="us-ascii"?><methodCall><methodName>'.$args->{method}.'</methodName><params>'.$args->{params_string}.'</params></methodCall>'
  23.               => sub { my ($ua, $tx) = @_;
  24.                        $tx->{args} = $args;
  25.                        $c->$cb($tx);
  26.                      }
  27.               );
  28. };
  29.  
  30. # Database helper methods
  31. helper connector => sub {
  32.   state $db = DBIx::Connector->new("dbi:SQLite:database.db") or die "Could not connect";
  33. };
  34.  
  35. helper db => sub { shift->connector->dbh };
  36.  
  37. helper create_table => sub {
  38.   my $c = shift;
  39.   $c->app->log->info("Creating table 'pools'");
  40.   $c->db->do('CREATE TABLE pools (name TEXT NOT NULL UNIQUE, ip TEXT NOT NULL);');
  41. };
  42.  
  43. helper select => sub {
  44.   my $c = shift;
  45.   my $sth = eval { $c->db->prepare('SELECT * FROM pools') } || return undef;
  46.   $sth->execute;
  47.   return $sth->fetchall_arrayref;
  48. };
  49.  
  50. helper insert => sub {
  51.   my $c = shift;
  52.   my $sth = eval { $c->db->prepare('INSERT OR IGNORE INTO pools VALUES (?,?)') } || return undef;
  53.   $sth->execute(@_);
  54.   return 1;
  55. };
  56.  
  57. # Create BBDD/table if it doen't exist
  58. app->select || app->create_table;
  59.  
  60. # Fetch Dom0s info from Nagios
  61. websocket '/fetch' => sub {
  62.   my $c = shift;
  63.   $c->app->log->debug("Fetching...");
  64.   $c->on( json => sub {
  65.     my ($ws, $row) = @_;
  66.     $c->ua->get('http://user:pass@host/nagios/cgi-bin/config.cgi?type=services' => sub {
  67.       my ($ua, $tx) = @_;
  68.       my %tmphash = ();
  69.       my $body = $tx->res->body;
  70.       $tmphash{$1} = $2 while $body =~ /check_xen!([^!]+)!([^!]+)!HOSTS/sgi;
  71.       map{ $c->insert($_, $tmphash{$_}) } keys %tmphash;
  72.       $ws->send({ json => 'Got '.(scalar keys %tmphash).' pools from nagios.' });
  73.     });
  74.   });
  75. };
  76.  
  77. # Ask the Dom0s for a RFB console and get up the proxy
  78. websocket '/*target' => sub {
  79.   my $c = shift;
  80.   $c->render_later;
  81.   $c->inactivity_timeout(300);
  82.   $c->on(finish => sub { warn 'websocket closing' });
  83.   $c->tx->with_protocols('binary');
  84.   my $tx = $c->tx;
  85.  
  86.   my $target = $c->stash('target');
  87.   say "[0] Target is '$target'";
  88.  
  89.   $c->delay(
  90.     sub {
  91.       my $delay = shift;
  92.       my $rows = $c->select;
  93.       foreach my $host (@$rows) {
  94.         say "[1] Authenticating to $host->[0]";
  95.         $c->api_call($delay->begin, $host->[0], 'session.login_with_password', 'root', $host->[1]);
  96.       }
  97.     },
  98.     sub {
  99.       my ($delay, @results) = @_;
  100.       foreach my $res (@results) {
  101.         my $token = $res->res->dom->find('member value')->last->text;
  102.         say "[2] Got token '$token from ".$res->original_remote_address;
  103.         $delay->data->{$res->original_remote_address}->{token} =  $token;
  104.         $c->api_call($delay->begin, $res->original_remote_address, 'VM.get_by_name_label', $token, $target );
  105.       }
  106.     },
  107.     sub {
  108.       my ($delay, @results) = @_;
  109.       foreach my $res (@results) {
  110.         my $body = $res->res->body;
  111.         my ($vmref) = $body =~ />(OpaqueRef:[^<]+)</;
  112.         next unless $vmref;
  113.         say "[3] Found '$target' with vmref '$vmref' on ".$res->original_remote_address;
  114.         my $token = $delay->data->{$res->original_remote_address}->{token};
  115.         $c->api_call($delay->begin, $res->original_remote_address, 'VM.get_consoles', $token, $vmref );
  116.       }
  117.     },
  118.     sub {
  119.       my ($delay, @results) = @_;
  120.       foreach my $res (@results) {
  121.         my $token = $delay->data->{$res->original_remote_address}->{token};
  122.         foreach my $conref ($res->res->dom->find('data value')->map('text')->each) {
  123.           say "[4] Got conref '$conref' on '$target'";
  124.         $c->api_call($delay->begin, $res->original_remote_address, 'console.get_record', $token, $conref );
  125.         }
  126.       }
  127.     },
  128.     sub {
  129.       my ($delay, @results) = @_;
  130.       foreach my $res (@results) {
  131.         my $record;
  132.         $res->res->dom->find('value')->each(sub {$record->{$_->previous->text} = $_->text});
  133.         say "[5] Got the $record->{protocol} url '$record->{location}'";
  134.         next unless $record->{protocol} eq 'rfb';
  135.         $record->{token} = $delay->data->{$res->original_remote_address}->{token};
  136.         ($record->{address}) = $record->{location} =~ m|://([^/]+)/|;
  137.         $delay->pass($record);
  138.       }
  139.     },
  140.     sub {
  141.       my ($delay, @results) = @_;
  142.       foreach my $res (@results) {
  143.         my $call = "CONNECT /console?uuid=$res->{uuid}&session_id=$res->{token} HTTP/1.0";
  144.         say "[6] Connect string is: '$call'";
  145.         Mojo::IOLoop->client(address => $res->{address}, port => 80, sub {
  146.           my ($loop, $err, $tcp) = @_;
  147.  
  148.           $tx->finish(4500, "TCP connection error: $err") if $err;
  149.           $tcp->on(error => sub { $tx->finish(4500, "TCP error: $_[1]") });
  150.  
  151.           my $ok = 0;
  152.           $tcp->once(read => sub {
  153.             my ($tcp, $bytes) = @_;
  154.             say "ONCE";
  155.             say "-$bytes-";
  156.             my $length = length($bytes);
  157.             say $length;
  158.             $tcp->timeout(300);
  159.             #$tcp->write("RFB 000.000\n") if $length > 78;
  160.             $tcp->reset if $length > 78;
  161.             $tcp->on(read => sub {
  162.               my ($tcp, $bytes) = @_;
  163.                 $tx->send({binary => $bytes});
  164.             });
  165.           });
  166.  
  167.           $tx->on(binary => sub {
  168.             my ($tx, $bytes) = @_;
  169.             $tcp->write($bytes);
  170.           });
  171.  
  172.           $tx->on(finish => sub {
  173.             $tcp->close;
  174.             undef $tcp;
  175.             undef $tx;
  176.           });
  177.  
  178.           #Perform XVP Auth
  179.           $tcp->write("$call\r\n\r\n");
  180.         });
  181.       }
  182.     }
  183.   );
  184.  
  185. };
  186.  
  187. # Base route
  188. any '/' => sub {
  189.   my $c = shift;
  190.   my $host = $c->tx->local_address;
  191.   my $port = $c->tx->local_port;
  192.   my $novnc_url = app->novnc_url()."?autoconnect=true&host=$host&port=$port&path=";
  193.   $c->render('index', novnc_url => $novnc_url);
  194. };
  195.  
  196. app->start;
  197.  
  198. __DATA__
  199.  
  200. @@ index.html.ep
  201.  
  202. <!DOCTYPE html>
  203. <html>
  204. <head>
  205.   <title>noVNC Proxy</title>
  206.   %= stylesheet 'https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.4/semantic.min.css'
  207.   <style type="text/css">
  208.     #container { height: 100%; }
  209.     #overlay { position: absolute; }
  210.     #vnc {
  211.         height: 100%;
  212.         width: 100%;
  213.         border: 0;
  214.     }
  215.   </style>
  216. </head>
  217. <body>
  218.   <div id="container">
  219.     <div id="overlay">
  220.       <div class="ui mini right labeled left icon input">
  221.         <i id="fetchbutton" onclick="fetch()" class="refresh icon"></i>
  222.         <input id="choose" placeholder="Enter a Xen VM name..." type="text">
  223.         <a class="ui tag label">Connect</a>
  224.       </div>
  225.     </div>
  226.     <iframe id="vnc" src="<%= $novnc_url %>oslo"></iframe>
  227.   </div>
  228.   %= javascript 'https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js'
  229.   %= javascript begin
  230.     function fetch () {
  231.       if (!("WebSocket" in window)) {
  232.         alert('Your browser does not support WebSockets!');
  233.       }
  234.       var ws = new WebSocket("<%== url_for('fetch')->to_abs %>");
  235.       ws.onopen = function () {
  236.         ws.send(0);
  237.       };
  238.       ws.onmessage = function (evt) {
  239.         var data = JSON.parse(evt.data);
  240.         alert(evt.data);
  241.       };
  242.     }
  243.     $("#vnc").focus();
  244.     $('#choose').val('');
  245.     $('.ui.tag.label').click(function() {
  246.       window.open("<%== $novnc_url %>" + $('#choose').val());
  247.     });
  248.   %= end
  249. </body>
  250. </html>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement