You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@livy.apache.org by js...@apache.org on 2017/06/27 06:39:22 UTC

[41/50] [abbrv] incubator-livy git commit: LIVY-342. Create Livy UI: Create Web UI Servlet and All Sessions Page (#319)

http://git-wip-us.apache.org/repos/asf/incubator-livy/blob/61b206e0/server/src/main/resources/com/cloudera/livy/server/ui/static/sessions-table.html
----------------------------------------------------------------------
diff --git a/server/src/main/resources/com/cloudera/livy/server/ui/static/sessions-table.html b/server/src/main/resources/com/cloudera/livy/server/ui/static/sessions-table.html
new file mode 100644
index 0000000..3452b4c
--- /dev/null
+++ b/server/src/main/resources/com/cloudera/livy/server/ui/static/sessions-table.html
@@ -0,0 +1,53 @@
+<!--
+ Licensed to Cloudera, Inc. under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  Cloudera, Inc. licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<h4 id="interactive-sessions-header" class="sessions-template">Interactive Sessions</h4>
+
+<table class="table table-striped sessions-table sessions-template">
+  <thead class="sessions-table-head">
+  <tr>
+    <th>Session Id</th>
+    <th>Application Id</th>
+    <th>
+      <span data-toggle="tooltip" title="Remote user who submitted this session">
+        Owner
+      </span>
+    </th>
+    <th>
+      <span data-toggle="tooltip" title="User to impersonate when running">
+        Proxy User
+      </span>
+    </th>
+    <th>
+      <span data-toggle="tooltip"
+            title="Session kind (spark, pyspark, pyspark3, or sparkr)">
+        Session Kind
+      </span>
+    </th>
+    <th>
+      <span data-toggle="tooltip"
+            title="Session State (not_started, starting, idle, busy,
+            shutting_down, error, dead, success)">
+        State
+      </span>
+    </th>
+  </tr>
+  </thead>
+  <tbody class="sessions-table-body">
+  </tbody>
+</table>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-livy/blob/61b206e0/server/src/main/scala/com/cloudera/livy/LivyConf.scala
----------------------------------------------------------------------
diff --git a/server/src/main/scala/com/cloudera/livy/LivyConf.scala b/server/src/main/scala/com/cloudera/livy/LivyConf.scala
index 8fc4777..233aa63 100644
--- a/server/src/main/scala/com/cloudera/livy/LivyConf.scala
+++ b/server/src/main/scala/com/cloudera/livy/LivyConf.scala
@@ -64,6 +64,8 @@ object LivyConf {
   val SERVER_PORT = Entry("livy.server.port", 8998)
   val CSRF_PROTECTION = LivyConf.Entry("livy.server.csrf-protection.enabled", false)
 
+  val UI_ENABLED = Entry("livy.ui.enabled", true)
+
   val IMPERSONATION_ENABLED = Entry("livy.impersonation.enabled", false)
   val SUPERUSERS = Entry("livy.superusers", null)
 

http://git-wip-us.apache.org/repos/asf/incubator-livy/blob/61b206e0/server/src/main/scala/com/cloudera/livy/server/LivyServer.scala
----------------------------------------------------------------------
diff --git a/server/src/main/scala/com/cloudera/livy/server/LivyServer.scala b/server/src/main/scala/com/cloudera/livy/server/LivyServer.scala
index 4bd5635..7bfe9ce 100644
--- a/server/src/main/scala/com/cloudera/livy/server/LivyServer.scala
+++ b/server/src/main/scala/com/cloudera/livy/server/LivyServer.scala
@@ -30,12 +30,14 @@ import org.apache.hadoop.security.authentication.server._
 import org.eclipse.jetty.servlet.FilterHolder
 import org.scalatra.metrics.MetricsBootstrap
 import org.scalatra.metrics.MetricsSupportExtensions._
+import org.scalatra.ScalatraServlet
 import org.scalatra.servlet.{MultipartConfig, ServletApiImplicits}
 
 import com.cloudera.livy._
 import com.cloudera.livy.server.batch.BatchSessionServlet
 import com.cloudera.livy.server.interactive.InteractiveSessionServlet
 import com.cloudera.livy.server.recovery.{SessionStore, StateStore}
+import com.cloudera.livy.server.ui.UIServlet
 import com.cloudera.livy.sessions.{BatchSessionManager, InteractiveSessionManager}
 import com.cloudera.livy.sessions.SessionManager.SESSION_RECOVERY_MODE_OFF
 import com.cloudera.livy.utils.LivySparkUtils._
@@ -142,6 +144,20 @@ class LivyServer extends Logging {
       }
     }
 
+    // Servlet for hosting static files such as html, css, and js
+    // Necessary since Jetty cannot set it's resource base inside a jar
+    val staticResourceServlet = new ScalatraServlet {
+      get("/*") {
+        getClass.getResourceAsStream("ui/static/" + params("splat"))
+      }
+    }
+
+    def uiRedirectServlet(path: String) = new ScalatraServlet {
+      get("/") {
+        redirect(path)
+      }
+    }
+
     server.context.addEventListener(
       new ServletContextListener() with MetricsBootstrap with ServletApiImplicits {
 
@@ -167,7 +183,16 @@ class LivyServer extends Logging {
             val batchServlet = new BatchSessionServlet(batchSessionManager, sessionStore, livyConf)
             mount(context, batchServlet, "/batches/*")
 
-            context.mountMetricsAdminServlet("/")
+            if (livyConf.getBoolean(UI_ENABLED)) {
+              val uiServlet = new UIServlet
+              mount(context, uiServlet, "/ui/*")
+              mount(context, staticResourceServlet, "/static/*")
+              mount(context, uiRedirectServlet("/ui/"), "/*")
+            } else {
+              mount(context, uiRedirectServlet("/metrics"), "/*")
+            }
+
+            context.mountMetricsAdminServlet("/metrics")
 
             mount(context, livyVersionServlet, "/version/*")
           } catch {

http://git-wip-us.apache.org/repos/asf/incubator-livy/blob/61b206e0/server/src/main/scala/com/cloudera/livy/server/ui/UIServlet.scala
----------------------------------------------------------------------
diff --git a/server/src/main/scala/com/cloudera/livy/server/ui/UIServlet.scala b/server/src/main/scala/com/cloudera/livy/server/ui/UIServlet.scala
new file mode 100644
index 0000000..ca2d4a1
--- /dev/null
+++ b/server/src/main/scala/com/cloudera/livy/server/ui/UIServlet.scala
@@ -0,0 +1,76 @@
+/*
+ * Licensed to Cloudera, Inc. under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  Cloudera, Inc. licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.cloudera.livy.server.ui
+
+import scala.xml.Node
+
+import org.scalatra.ScalatraServlet
+
+import com.cloudera.livy.LivyConf
+
+class UIServlet extends ScalatraServlet {
+  before() { contentType = "text/html" }
+
+  def getHeader(title: String): Seq[Node] =
+    <head>
+      <link rel="stylesheet" href="/static/bootstrap.min.css" type="text/css"/>
+      <link rel="stylesheet" href="/static/livy-ui.css" type="text/css"/>
+      <script src="/static/jquery-3.2.1.min.js"></script>
+      <script src="/static/bootstrap.min.js"></script>
+      <script src="/static/all-sessions.js"></script>
+      <title>{title}</title>
+    </head>
+
+  def navBar(pageName: String): Seq[Node] =
+    <nav class="navbar navbar-default">
+      <div class="container-fluid">
+        <div class="navbar-header">
+          <a class="navbar-brand" href="#">
+            <img alt="Livy" src="/static/livy-mini-logo.png"/>
+          </a>
+        </div>
+        <div class="collapse navbar-collapse">
+          <ul class="nav navbar-nav">
+            <li><a href="#">{pageName}</a></li>
+          </ul>
+        </div>
+      </div>
+    </nav>
+
+  def createPage(pageName: String, pageContents: Seq[Node]): Seq[Node] =
+    <html>
+      {getHeader("Livy - " + pageName)}
+      <body>
+        <div class="container">
+          {navBar(pageName)}
+          {pageContents}
+        </div>
+      </body>
+    </html>
+
+  get("/") {
+    val content =
+      <div id="all-sessions">
+        <div id="interactive-sessions"></div>
+        <div id="batches"></div>
+      </div>
+
+    createPage("Sessions", content)
+  }
+}