Implement KVM in webui

This patchset adds the infrastructure to allow KVM sessions
through the webui. A websocket capable VNC/RFB connection
on the BMC is needed for KVM sessions.

To access, navigate to Server control -> KVM.

Tested: Ran obmc-ikvm on the BMC, added a KVM Handler to
        Phosphor Rest Server, and was able to establish a
        KVM session in the webui on a Witherspoon.
Change-Id: I7dda5bec41d270ae8d0913697714d4df4ec3a257
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
Signed-off-by: Gunnar Mills <gmills@us.ibm.com>
diff --git a/app/server-control/controllers/kvm-controller.html b/app/server-control/controllers/kvm-controller.html
new file mode 100644
index 0000000..40e4d97
--- /dev/null
+++ b/app/server-control/controllers/kvm-controller.html
@@ -0,0 +1,5 @@
+<div id="noVNC_container">
+    <div id="noVNC_status_bar">
+        <div id="noVNC_left_dummy_elem"></div>
+    </div>
+</div>
diff --git a/app/server-control/controllers/kvm-controller.js b/app/server-control/controllers/kvm-controller.js
new file mode 100644
index 0000000..a43f169
--- /dev/null
+++ b/app/server-control/controllers/kvm-controller.js
@@ -0,0 +1,55 @@
+/**
+ * Controller for KVM (Kernel-based Virtual Machine)
+ *
+ * @module app/serverControl
+ * @exports kvmController
+ * @name kvmController
+ */
+
+import RFB from '@novnc/novnc/core/rfb.js';
+
+window.angular && (function(angular) {
+  'use strict';
+
+  angular.module('app.serverControl').controller('kvmController', [
+    '$scope', '$location', '$log',
+    function($scope, $location, $log) {
+      var rfb;
+
+      $scope.$on('$destroy', function() {
+        if (rfb) {
+          rfb.disconnect();
+        }
+      });
+
+      function sendCtrlAltDel() {
+        rfb.sendCtrlAltDel();
+        return false;
+      };
+
+      function connected(e) {
+        $log.debug('RFB Connected');
+      }
+      function disconnected(e) {
+        $log.debug('RFB disconnected');
+      }
+
+      var host = $location.host();
+      var port = $location.port();
+      var target =
+          angular.element(document.querySelector('#noVNC_container'))[0];
+
+      try {
+        rfb = new RFB(target, 'wss://' + host + ':' + port + '/kvm/0', {});
+
+        rfb.addEventListener('connect', connected);
+        rfb.addEventListener('disconnect', disconnected);
+      } catch (exc) {
+        $log.error(exc);
+        updateState(
+            null, 'fatal', null, 'Unable to create RFB client -- ' + exc);
+        return;  // don't continue trying to connect
+      };
+    }
+  ]);
+})(angular);
diff --git a/app/server-control/index.js b/app/server-control/index.js
index 739bd1e..1b8aad5 100644
--- a/app/server-control/index.js
+++ b/app/server-control/index.js
@@ -48,6 +48,11 @@
                 'controller': 'remoteConsoleWindowController',
                 authenticated: true
               })
+              .when('/server-control/kvm', {
+                'template': require('./controllers/kvm-controller.html'),
+                'controller': 'kvmController',
+                authenticated: true
+              })
               .when('/server-control', {
                 'template':
                     require('./controllers/power-operations-controller.html'),
diff --git a/app/server-control/styles/index.scss b/app/server-control/styles/index.scss
index f6b15ab..5e8a995 100644
--- a/app/server-control/styles/index.scss
+++ b/app/server-control/styles/index.scss
@@ -3,3 +3,4 @@
 @import "./remote-console.scss";
 @import "./server-led.scss";
 @import "./power-usage.scss";
+@import "./kvm.scss";
diff --git a/app/server-control/styles/kvm.scss b/app/server-control/styles/kvm.scss
new file mode 100644
index 0000000..2f9e2c0
--- /dev/null
+++ b/app/server-control/styles/kvm.scss
@@ -0,0 +1,11 @@
+
+.noNVC_shown {
+  display: inline;
+}
+.noVNC_hidden {
+  display: none;
+}
+
+#noVNC_left_dummy_elem {
+  flex: 1;
+}