Merge remote-tracking branch 'upstream/master'
# Conflicts: # docs/interpreter/cassandra.md # docs/interpreter/spark.md # docs/manual/interpreters.md
24
.github/PULL_REQUEST_TEMPLATE
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
### What is this PR for?
|
||||
A few sentences describing the overall goals of the pull request's commits.
|
||||
First time? Check out the contributing guide - https://github.com/apache/incubator-zeppelin/blob/master/CONTRIBUTING.md
|
||||
|
||||
|
||||
### What type of PR is it?
|
||||
[Bug Fix | Improvement | Feature | Documentation | Hot Fix | Refactoring]
|
||||
|
||||
### Todos
|
||||
* [ ] - Task
|
||||
|
||||
### What is the Jira issue?
|
||||
* Open an issue on Jira https://issues.apache.org/jira/browse/ZEPPELIN/
|
||||
* Put link here, and add [ZEPPELIN-*Jira number*] in PR title, eg. [ZEPPELIN-533]
|
||||
|
||||
### How should this be tested?
|
||||
Outline the steps to test the PR here.
|
||||
|
||||
### Screenshots (if appropriate)
|
||||
|
||||
### Questions:
|
||||
* Does the licenses files need update?
|
||||
* Is there breaking changes for older versions?
|
||||
* Does this needs documentation?
|
||||
8
.gitignore
vendored
|
|
@ -8,6 +8,13 @@
|
|||
# interpreter
|
||||
/interpreter/
|
||||
|
||||
# interpreter temp files
|
||||
spark/derby.log
|
||||
spark/metastore_db
|
||||
spark-1.*-bin-hadoop*
|
||||
|
||||
lens/lens-cli-hist.log
|
||||
|
||||
# conf file
|
||||
conf/zeppelin-env.sh
|
||||
conf/zeppelin-site.xml
|
||||
|
|
@ -45,6 +52,7 @@ zeppelin-web/bower_components
|
|||
**/data/
|
||||
**/build/
|
||||
**/testing/
|
||||
!/testing/
|
||||
|
||||
# OS generated files #
|
||||
######################
|
||||
|
|
|
|||
21
.travis.yml
|
|
@ -19,27 +19,31 @@ matrix:
|
|||
include:
|
||||
# Test all modules
|
||||
- jdk: "oraclejdk7"
|
||||
env: SPARK_VER="1.6.0" HADOOP_VER="2.3" PROFILE="-Pspark-1.6 -Phadoop-2.3 -Ppyspark -Pscalding" BUILD_FLAG="package -Pbuild-distr" TEST_FLAG="verify -Pusing-packaged-distr"
|
||||
env: SPARK_VER="1.6.0" HADOOP_VER="2.3" PROFILE="-Pspark-1.6 -Phadoop-2.3 -Ppyspark -Pscalding" BUILD_FLAG="package -Pbuild-distr" TEST_FLAG="verify -Pusing-packaged-distr" TEST_PROJECTS=""
|
||||
|
||||
# Test spark module for 1.5.2
|
||||
- jdk: "oraclejdk7"
|
||||
env: SPARK_VER="1.5.2" HADOOP_VER="2.3" PROFILE="-Pspark-1.5 -Phadoop-2.3 -Ppyspark" BUILD_FLAG="package -DskipTests" TEST_FLAG="verify"
|
||||
env: SPARK_VER="1.5.2" HADOOP_VER="2.3" PROFILE="-Pspark-1.5 -Phadoop-2.3 -Ppyspark" BUILD_FLAG="package -DskipTests" TEST_FLAG="verify" TEST_PROJECTS="-pl zeppelin-interpreter,zeppelin-zengine,zeppelin-server,zeppelin-display,spark-dependencies,spark -Dtest=org.apache.zeppelin.rest.*Test,org.apache.zeppelin.spark* -DfailIfNoTests=false"
|
||||
|
||||
# Test spark module for 1.4.1
|
||||
- jdk: "oraclejdk7"
|
||||
env: SPARK_VER="1.4.1" HADOOP_VER="2.3" PROFILE="-Pspark-1.4 -Phadoop-2.3 -Ppyspark" BUILD_FLAG="package -DskipTests" TEST_FLAG="verify"
|
||||
env: SPARK_VER="1.4.1" HADOOP_VER="2.3" PROFILE="-Pspark-1.4 -Phadoop-2.3 -Ppyspark" BUILD_FLAG="package -DskipTests" TEST_FLAG="verify" TEST_PROJECTS="-pl zeppelin-interpreter,zeppelin-zengine,zeppelin-server,zeppelin-display,spark-dependencies,spark -Dtest=org.apache.zeppelin.rest.*Test,org.apache.zeppelin.spark* -DfailIfNoTests=false"
|
||||
|
||||
# Test spark module for 1.3.1
|
||||
- jdk: "oraclejdk7"
|
||||
env: SPARK_VER="1.3.1" HADOOP_VER="2.3" PROFILE="-Pspark-1.3 -Phadoop-2.3 -Ppyspark" BUILD_FLAG="package -DskipTests" TEST_FLAG="verify"
|
||||
env: SPARK_VER="1.3.1" HADOOP_VER="2.3" PROFILE="-Pspark-1.3 -Phadoop-2.3 -Ppyspark" BUILD_FLAG="package -DskipTests" TEST_FLAG="verify" TEST_PROJECTS="-pl zeppelin-interpreter,zeppelin-zengine,zeppelin-server,zeppelin-display,spark-dependencies,spark -Dtest=org.apache.zeppelin.rest.*Test,org.apache.zeppelin.spark* -DfailIfNoTests=false"
|
||||
|
||||
# Test spark module for 1.2.1
|
||||
# Test spark module for 1.2.2
|
||||
- jdk: "oraclejdk7"
|
||||
env: SPARK_VER="1.2.1" HADOOP_VER="2.3" PROFILE="-Pspark-1.2 -Phadoop-2.3 -Ppyspark" BUILD_FLAG="package -DskipTests" TEST_FLAG="verify"
|
||||
env: SPARK_VER="1.2.2" HADOOP_VER="2.3" PROFILE="-Pspark-1.2 -Phadoop-2.3 -Ppyspark" BUILD_FLAG="package -DskipTests" TEST_FLAG="verify" TEST_PROJECTS="-pl zeppelin-interpreter,zeppelin-zengine,zeppelin-server,zeppelin-display,spark-dependencies,spark -Dtest=org.apache.zeppelin.rest.*Test,org.apache.zeppelin.spark* -DfailIfNoTests=false"
|
||||
|
||||
# Test spark module for 1.1.1
|
||||
- jdk: "oraclejdk7"
|
||||
env: SPARK_VER="1.1.1" HADOOP_VER="2.3" PROFILE="-Pspark-1.1 -Phadoop-2.3 -Ppyspark" BUILD_FLAG="package -DskipTests" TEST_FLAG="verify"
|
||||
env: SPARK_VER="1.1.1" HADOOP_VER="2.3" PROFILE="-Pspark-1.1 -Phadoop-2.3 -Ppyspark" BUILD_FLAG="package -DskipTests" TEST_FLAG="verify" TEST_PROJECTS="-pl zeppelin-interpreter,zeppelin-zengine,zeppelin-server,zeppelin-display,spark-dependencies,spark -Dtest=org.apache.zeppelin.rest.*Test,org.apache.zeppelin.spark* -DfailIfNoTests=false"
|
||||
|
||||
# Test selenium with spark module for 1.6.0
|
||||
- jdk: "oraclejdk7"
|
||||
env: TEST_SELENIUM="true" SPARK_VER="1.6.0" HADOOP_VER="2.3" PROFILE="-Pspark-1.6 -Phadoop-2.3 -Ppyspark" BUILD_FLAG="package -DskipTests" TEST_FLAG="verify" TEST_PROJECTS="-pl zeppelin-interpreter,zeppelin-zengine,zeppelin-server,zeppelin-display,spark-dependencies,spark -Dtest=org.apache.zeppelin.AbstractFunctionalSuite -DfailIfNoTests=false"
|
||||
|
||||
before_install:
|
||||
- "export DISPLAY=:99.0"
|
||||
|
|
@ -49,11 +53,12 @@ install:
|
|||
- mvn $BUILD_FLAG $PROFILE -B
|
||||
|
||||
before_script:
|
||||
- travis_retry ./testing/downloadSpark.sh $SPARK_VER $HADOOP_VER
|
||||
- ./testing/startSparkCluster.sh $SPARK_VER $HADOOP_VER
|
||||
- echo "export SPARK_HOME=`pwd`/spark-$SPARK_VER-bin-hadoop$HADOOP_VER" > conf/zeppelin-env.sh
|
||||
|
||||
script:
|
||||
- mvn $TEST_FLAG $PROFILE -B
|
||||
- mvn $TEST_FLAG $PROFILE -B $TEST_PROJECTS
|
||||
|
||||
after_failure:
|
||||
- cat target/rat.txt
|
||||
|
|
|
|||
|
|
@ -7,12 +7,17 @@ Contributing to Zeppelin (Source code, Documents, Image, Website) means you agre
|
|||
2. If not, create a ticket describing the change you're proposing in the [Jira issue tracker](https://issues.apache.org/jira/browse/ZEPPELIN)
|
||||
3. Contribute your patch via Pull Request.
|
||||
|
||||
Before you start, please read the [Code of Conduct](http://www.apache.org/foundation/policies/conduct.html) carefully, familiarize yourself with it and refer to it whenever you need it.
|
||||
|
||||
For those of you who are not familiar with Apache project, understanding [How it works](http://www.apache.org/foundation/how-it-works.html) would be quite helpful.
|
||||
|
||||
## Creating a Pull Request
|
||||
In order to make the review process easier, please follow this template when making a Pull Request:
|
||||
|
||||
```
|
||||
### What is this PR for?
|
||||
A few sentences describing the overall goals of the pull request's commits.
|
||||
First time? Check out the contributing guide - https://github.com/apache/incubator-zeppelin/blob/master/CONTRIBUTING.md
|
||||
|
||||
### What type of PR is it?
|
||||
[Bug Fix | Improvement | Feature | Documentation | Hot Fix | Refactoring]
|
||||
|
|
@ -20,7 +25,9 @@ A few sentences describing the overall goals of the pull request's commits.
|
|||
### Todos
|
||||
* [ ] - Task
|
||||
|
||||
### Is there a relevant Jira issue?
|
||||
### What is the Jira issue?
|
||||
* Open an issue on Jira https://issues.apache.org/jira/browse/ZEPPELIN/
|
||||
* Put link here, and add [ZEPPELIN-*Jira number*] in PR title, eg. [ZEPPELIN-533]
|
||||
|
||||
### How should this be tested?
|
||||
Outline the steps to test the PR here.
|
||||
|
|
@ -33,11 +40,28 @@ Outline the steps to test the PR here.
|
|||
* Does this needs documentation?
|
||||
```
|
||||
|
||||
You can also use this small bookmarklet tool to fill your Pull Request fields automatically:
|
||||
## Testing a Pull Request
|
||||
You can also test and review a particular Pull Request. Here are two useful ways.
|
||||
|
||||
```
|
||||
javascript:(function() {var e = document.getElementById('pull_request_body');if (e) {e.value += '### What is this PR for?\nA few sentences describing the overall goals of the pull request\'s commits.\n\n### What type of PR is it?\n[Bug Fix | Improvement | Feature | Documentation | Hot Fix | Refactoring]\n\n### Todos\n* [ ] - Task\n\n### Is there a relevant Jira issue?\n\n### How should this be tested?\nOutline the steps to test the PR here.\n\n### Screenshots (if appropriate)\n\n### Questions:\n* Does the licenses files need update?\n* Is there breaking changes for older versions?\n* Does this needs documentation?';}})();
|
||||
```
|
||||
* Using a utility provided from Zeppelin.
|
||||
|
||||
```
|
||||
dev/test_zeppelin_pr.py [# of PR]
|
||||
```
|
||||
|
||||
For example, if you want to test `#513`, then the command will be:
|
||||
|
||||
```
|
||||
dev/test_zeppelin_pr.py 513
|
||||
```
|
||||
|
||||
* Another way is using [github/hub](https://github.com/github/hub).
|
||||
|
||||
```
|
||||
hub checkout https://github.com/apache/incubator-zeppelin/pull/[# of PR]
|
||||
```
|
||||
|
||||
The above two methods will help you test and review Pull Requests.
|
||||
|
||||
## Source Control Workflow
|
||||
Zeppelin follows [Fork & Pull] (https://github.com/sevntu-checkstyle/sevntu.checkstyle/wiki/Development-workflow-with-Git:-Fork,-Branching,-Commits,-and-Pull-Request) model.
|
||||
|
|
@ -53,7 +77,6 @@ When a Pull Request is submitted, it is being merged or rejected by following re
|
|||
* Committer can initiate lazy consensus ("Merge if there is no more discussion") and the code can be merged after certain time (normally 24 hours) when there is no review exists.
|
||||
* Contributor can ping reviewers (including committer) by commenting 'Ready to review' or suitable indication.
|
||||
|
||||
|
||||
## Becoming a Committer
|
||||
|
||||
The PPMC adds new committers from the active contributors, based on their contribution to Zeppelin. The qualifications for new committers include:
|
||||
|
|
@ -122,13 +145,13 @@ First of all, you need the Zeppelin source code. The official location for Zeppe
|
|||
Get the source code on your development machine using git.
|
||||
|
||||
```
|
||||
git clone http://git.apache.org/incubator-zeppelin.git zeppelin
|
||||
git clone git://git.apache.org/incubator-zeppelin.git zeppelin
|
||||
```
|
||||
|
||||
You may also want to develop against a specific release. For example, for branch-0.1
|
||||
You may also want to develop against a specific branch. For example, for branch-0.5.6
|
||||
|
||||
```
|
||||
git clone -b branch-0.1 http://git.apache.org/incubator-zeppelin.git zeppelin
|
||||
git clone -b branch-0.5.6 git://git.apache.org/incubator-zeppelin.git zeppelin
|
||||
```
|
||||
|
||||
or with write access
|
||||
|
|
|
|||
2
NOTICE
|
|
@ -1,5 +1,5 @@
|
|||
Apache Zeppelin (incubating)
|
||||
Copyright 2015 The Apache Software Foundation
|
||||
Copyright 2015 - 2016 The Apache Software Foundation
|
||||
|
||||
This product includes software developed at
|
||||
The Apache Software Foundation (http://www.apache.org/).
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#Zeppelin
|
||||
|
||||
**Documentation:** [User Guide](http://zeppelin.incubator.apache.org/docs/index.html)<br/>
|
||||
**Documentation:** [User Guide](http://zeppelin.incubator.apache.org/docs/latest/index.html)<br/>
|
||||
**Mailing Lists:** [User and Dev mailing list](http://zeppelin.incubator.apache.org/community.html)<br/>
|
||||
**Continuous Integration:** [](https://travis-ci.org/apache/incubator-zeppelin) <br/>
|
||||
**Contributing:** [Contribution Guide](https://github.com/apache/incubator-zeppelin/blob/master/CONTRIBUTING.md)<br/>
|
||||
|
|
@ -104,7 +104,7 @@ minor version can be adjusted by `-Dhadoop.version=x.x.x`
|
|||
##### -Pyarn (optional)
|
||||
|
||||
enable YARN support for local mode
|
||||
|
||||
> YARN for local mode is not supported for Spark v1.5.0 or higher. Set SPARK_HOME instead.
|
||||
|
||||
##### -Ppyspark (optional)
|
||||
|
||||
|
|
|
|||
|
|
@ -33,11 +33,8 @@ The scope of this PR is to require credentials to access Zeppelin. To achieve th
|
|||
Apache Shiro sits as a servlet filter between the browser and the exposed services and handles the required authentication without any programming required. (See Apache Shiro for more info).
|
||||
## Websocket security
|
||||
Securing the HTTP endpoints is not enough, since Zeppelin also communicates with the browser through websockets. To secure this channel, we take the following approach:
|
||||
1. The browser on startup requests a ticket through HTTP
|
||||
2. The Apache Shiro Servlet filter handles the user auth
|
||||
3. Once the user is authenticated, a ticket is assigned to this user and the ticket is returned to the browser
|
||||
1. The browser on startup requests a ticket through HTTP
|
||||
2. The Apache Shiro Servlet filter handles the user auth
|
||||
3. Once the user is authenticated, a ticket is assigned to this user and the ticket is returned to the browser
|
||||
|
||||
All websockets communications require the username and ticket to be submitted by the browser. Upon receiving a websocket message, the server checks that the ticket received is the one assigned to the username through the HTTP request (step 3 above).
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
200
alluxio/pom.xml
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
~ contributor license agreements. See the NOTICE file distributed with
|
||||
~ this work for additional information regarding copyright ownership.
|
||||
~ The ASF 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.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>zeppelin</artifactId>
|
||||
<groupId>org.apache.zeppelin</groupId>
|
||||
<version>0.6.0-incubating-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>org.apache.zeppelin</groupId>
|
||||
<artifactId>zeppelin-alluxio</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>0.6.0-incubating-SNAPSHOT</version>
|
||||
<name>Zeppelin: Alluxio interpreter</name>
|
||||
<url>http://www.apache.org</url>
|
||||
|
||||
<properties>
|
||||
<alluxio.version>1.0.0</alluxio.version>
|
||||
<powermock.version>1.6.1</powermock.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.zeppelin</groupId>
|
||||
<artifactId>zeppelin-interpreter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.alluxio</groupId>
|
||||
<artifactId>alluxio-shell</artifactId>
|
||||
<version>${alluxio.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- TEST -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<version>1.10.8</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-api-mockito</artifactId>
|
||||
<version>${powermock.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-core</artifactId>
|
||||
<version>${powermock.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-module-junit4</artifactId>
|
||||
<version>${powermock.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-reflect</artifactId>
|
||||
<version>${powermock.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.alluxio</groupId>
|
||||
<artifactId>alluxio-core-server</artifactId>
|
||||
<version>${alluxio.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.alluxio</groupId>
|
||||
<artifactId>alluxio-minicluster</artifactId>
|
||||
<version>${alluxio.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.alluxio</groupId>
|
||||
<artifactId>alluxio-underfs-local</artifactId>
|
||||
<version>${alluxio.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<version>2.7</version>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<artifactId>maven-enforcer-plugin</artifactId>
|
||||
<version>1.3.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>enforce</id>
|
||||
<phase>none</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>2.8</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-dependencies</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.directory}/../../interpreter/alluxio</outputDirectory>
|
||||
<overWriteReleases>false</overWriteReleases>
|
||||
<overWriteSnapshots>false</overWriteSnapshots>
|
||||
<overWriteIfNewer>true</overWriteIfNewer>
|
||||
<includeScope>runtime</includeScope>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-artifact</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.directory}/../../interpreter/alluxio</outputDirectory>
|
||||
<overWriteReleases>false</overWriteReleases>
|
||||
<overWriteSnapshots>false</overWriteSnapshots>
|
||||
<overWriteIfNewer>true</overWriteIfNewer>
|
||||
<includeScope>runtime</includeScope>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>${project.artifactId}</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>${project.packaging}</type>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,254 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF 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 org.apache.zeppelin.alluxio;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.zeppelin.interpreter.Interpreter;
|
||||
import org.apache.zeppelin.interpreter.InterpreterContext;
|
||||
import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult.Code;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import alluxio.Configuration;
|
||||
import alluxio.shell.AlluxioShell;
|
||||
|
||||
/**
|
||||
* Alluxio interpreter for Zeppelin.
|
||||
*/
|
||||
public class AlluxioInterpreter extends Interpreter {
|
||||
|
||||
Logger logger = LoggerFactory.getLogger(AlluxioInterpreter.class);
|
||||
|
||||
protected static final String ALLUXIO_MASTER_HOSTNAME = "alluxio.master.hostname";
|
||||
protected static final String ALLUXIO_MASTER_PORT = "alluxio.master.port";
|
||||
|
||||
private AlluxioShell fs;
|
||||
|
||||
private int totalCommands = 0;
|
||||
private int completedCommands = 0;
|
||||
|
||||
private final String alluxioMasterHostname;
|
||||
private final String alluxioMasterPort;
|
||||
|
||||
protected final List<String> keywords = Arrays.asList("cat", "chgrp",
|
||||
"chmod", "chown", "copyFromLocal", "copyToLocal", "count",
|
||||
"createLineage", "deleteLineage", "du", "fileInfo", "free",
|
||||
"getCapacityBytes", "getUsedBytes", "listLineages", "load",
|
||||
"loadMetadata", "location", "ls", "mkdir", "mount", "mv",
|
||||
"persist", "pin", "report", "rm", "setTtl", "tail", "touch",
|
||||
"unmount", "unpin", "unsetTtl");
|
||||
|
||||
public AlluxioInterpreter(Properties property) {
|
||||
super(property);
|
||||
|
||||
alluxioMasterHostname = property.getProperty(ALLUXIO_MASTER_HOSTNAME);
|
||||
alluxioMasterPort = property.getProperty(ALLUXIO_MASTER_PORT);
|
||||
}
|
||||
|
||||
static {
|
||||
Interpreter.register("alluxio", "alluxio",
|
||||
AlluxioInterpreter.class.getName(),
|
||||
new InterpreterPropertyBuilder()
|
||||
.add(ALLUXIO_MASTER_HOSTNAME, "localhost", "Alluxio master hostname")
|
||||
.add(ALLUXIO_MASTER_PORT, "19998", "Alluxio master port")
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() {
|
||||
logger.info("Starting Alluxio shell to connect to " + alluxioMasterHostname +
|
||||
" on port " + alluxioMasterPort);
|
||||
|
||||
System.setProperty(ALLUXIO_MASTER_HOSTNAME, alluxioMasterHostname);
|
||||
System.setProperty(ALLUXIO_MASTER_PORT, alluxioMasterPort);
|
||||
fs = new AlluxioShell(new Configuration());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
logger.info("Closing Alluxio shell");
|
||||
|
||||
try {
|
||||
fs.close();
|
||||
} catch (IOException e) {
|
||||
logger.error("Cannot close connection", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InterpreterResult interpret(String st, InterpreterContext context) {
|
||||
String[] lines = splitAndRemoveEmpty(st, "\n");
|
||||
return interpret(lines, context);
|
||||
}
|
||||
|
||||
private InterpreterResult interpret(String[] commands, InterpreterContext context) {
|
||||
boolean isSuccess = true;
|
||||
totalCommands = commands.length;
|
||||
completedCommands = 0;
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
PrintStream ps = new PrintStream(baos);
|
||||
PrintStream old = System.out;
|
||||
|
||||
System.setOut(ps);
|
||||
|
||||
for (String command : commands) {
|
||||
int commandResuld = 1;
|
||||
String[] args = splitAndRemoveEmpty(command, " ");
|
||||
if (args.length > 0 && args[0].equals("help")) {
|
||||
System.out.println(getCommandList());
|
||||
} else {
|
||||
commandResuld = fs.run(args);
|
||||
}
|
||||
if (commandResuld != 0) {
|
||||
isSuccess = false;
|
||||
break;
|
||||
} else {
|
||||
completedCommands += 1;
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
System.out.flush();
|
||||
System.setOut(old);
|
||||
|
||||
if (isSuccess) {
|
||||
return new InterpreterResult(Code.SUCCESS, baos.toString());
|
||||
} else {
|
||||
return new InterpreterResult(Code.ERROR, baos.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private String[] splitAndRemoveEmpty(String st, String splitSeparator) {
|
||||
String[] voices = st.split(splitSeparator);
|
||||
ArrayList<String> result = new ArrayList<String>();
|
||||
for (String voice : voices) {
|
||||
if (!voice.trim().isEmpty()) {
|
||||
result.add(voice);
|
||||
}
|
||||
}
|
||||
return result.toArray(new String[result.size()]);
|
||||
}
|
||||
|
||||
private String[] splitAndRemoveEmpty(String[] sts, String splitSeparator) {
|
||||
ArrayList<String> result = new ArrayList<String>();
|
||||
for (String st : sts) {
|
||||
result.addAll(Arrays.asList(splitAndRemoveEmpty(st, splitSeparator)));
|
||||
}
|
||||
return result.toArray(new String[result.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel(InterpreterContext context) { }
|
||||
|
||||
@Override
|
||||
public FormType getFormType() {
|
||||
return FormType.NATIVE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProgress(InterpreterContext context) {
|
||||
return completedCommands * 100 / totalCommands;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> completion(String buf, int cursor) {
|
||||
String[] words = splitAndRemoveEmpty(splitAndRemoveEmpty(buf, "\n"), " ");
|
||||
String lastWord = "";
|
||||
if (words.length > 0) {
|
||||
lastWord = words[ words.length - 1 ];
|
||||
}
|
||||
ArrayList<String> voices = new ArrayList<String>();
|
||||
for (String command : keywords) {
|
||||
if (command.startsWith(lastWord)) {
|
||||
voices.add(command);
|
||||
}
|
||||
}
|
||||
return voices;
|
||||
}
|
||||
|
||||
private String getCommandList() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Commands list:");
|
||||
sb.append("\n\t[help] - List all available commands.");
|
||||
sb.append("\n\t[cat <path>] - Prints the file's contents to the console.");
|
||||
sb.append("\n\t[chgrp [-R] <group> <path>] - Changes the group of a file or directory " +
|
||||
"specified by args. Specify -R to change the group recursively.");
|
||||
sb.append("\n\t[chmod -R <mode> <path>] - Changes the permission of a file or directory " +
|
||||
"specified by args. Specify -R to change the permission recursively.");
|
||||
sb.append("\n\t[chown -R <owner> <path>] - Changes the owner of a file or directory " +
|
||||
"specified by args. Specify -R to change the owner recursively.");
|
||||
sb.append("\n\t[copyFromLocal <src> <remoteDst>] - Copies a file or a directory from " +
|
||||
"local filesystem to Alluxio filesystem.");
|
||||
sb.append("\n\t[copyToLocal <src> <localDst>] - Copies a file or a directory from the " +
|
||||
"Alluxio filesystem to the local filesystem.");
|
||||
sb.append("\n\t[count <path>] - Displays the number of files and directories matching " +
|
||||
"the specified prefix.");
|
||||
sb.append("\n\t[createLineage <inputFile1,...> <outputFile1,...> " +
|
||||
"[<cmd_arg1> <cmd_arg2> ...]] - Creates a lineage.");
|
||||
sb.append("\n\t[deleteLineage <lineageId> <cascade(true|false)>] - Deletes a lineage. If " +
|
||||
"cascade is specified as true, dependent lineages will also be deleted.");
|
||||
sb.append("\n\t[du <path>] - Displays the size of the specified file or directory.");
|
||||
sb.append("\n\t[fileInfo <path>] - Displays all block info for the specified file.");
|
||||
sb.append("\n\t[free <file path|folder path>] - Removes the file or directory(recursively) " +
|
||||
"from Alluxio memory space.");
|
||||
sb.append("\n\t[getCapacityBytes] - Gets the capacity of the Alluxio file system.");
|
||||
sb.append("\n\t[getUsedBytes] - Gets number of bytes used in the Alluxio file system.");
|
||||
sb.append("\n\t[listLineages] - Lists all lineages.");
|
||||
sb.append("\n\t[load <path>] - Loads a file or directory in Alluxio space, makes it " +
|
||||
"resident in memory.");
|
||||
sb.append("\n\t[loadMetadata <path>] - Loads metadata for the given Alluxio path from the " +
|
||||
"under file system.");
|
||||
sb.append("\n\t[location <path>] - Displays the list of hosts storing the specified file.");
|
||||
sb.append("\n\t[ls [-R] <path>] - Displays information for all files and directories " +
|
||||
"directly under the specified path. Specify -R to display files and " +
|
||||
"directories recursively.");
|
||||
sb.append("\n\t[mkdir <path1> [path2] ... [pathn]] - Creates the specified directories, " +
|
||||
"including any parent directories that are required.");
|
||||
sb.append("\n\t[mount <alluxioPath> <ufsURI>] - Mounts a UFS path onto an Alluxio path.");
|
||||
sb.append("\n\t[mv <src> <dst>] - Renames a file or directory.");
|
||||
sb.append("\n\t[persist <alluxioPath>] - Persists a file or directory currently stored " +
|
||||
"only in Alluxio to the UnderFileSystem.");
|
||||
sb.append("\n\t[pin <path>] - Pins the given file or directory in memory (works " +
|
||||
"recursively for directories). Pinned files are never evicted from memory, unless " +
|
||||
"TTL is set.");
|
||||
sb.append("\n\t[report <path>] - Reports to the master that a file is lost.");
|
||||
sb.append("\n\t[rm [-R] <path>] - Removes the specified file. Specify -R to remove file or " +
|
||||
"directory recursively.");
|
||||
sb.append("\n\t[setTtl <path> <time to live(in milliseconds)>] - Sets a new TTL value for " +
|
||||
"the file at path.");
|
||||
sb.append("\n\t[tail <path>] - Prints the file's last 1KB of contents to the console.");
|
||||
sb.append("\n\t[touch <path>] - Creates a 0 byte file. The file will be written to the " +
|
||||
"under file system.");
|
||||
sb.append("\n\t[unmount <alluxioPath>] - Unmounts an Alluxio path.");
|
||||
sb.append("\n\t[unpin <path>] - Unpins the given file or folder from memory " +
|
||||
"(works recursively for a directory).");
|
||||
sb.append("\n\\t[unsetTtl <path>] - Unsets the TTL value for the given path.");
|
||||
sb.append("\n\t[unpin <path>] - Unpin the given file to allow Alluxio to evict " +
|
||||
"this file again. If the given path is a directory, it recursively unpins " +
|
||||
"all files contained and any new files created within this directory.");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,463 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF 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 org.apache.zeppelin.alluxio;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import alluxio.client.WriteType;
|
||||
import alluxio.client.file.URIStatus;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult.Code;
|
||||
import org.junit.*;
|
||||
|
||||
import alluxio.Constants;
|
||||
import alluxio.AlluxioURI;
|
||||
import alluxio.client.FileSystemTestUtils;
|
||||
import alluxio.client.file.FileInStream;
|
||||
import alluxio.client.file.FileSystem;
|
||||
import alluxio.exception.ExceptionMessage;
|
||||
import alluxio.exception.AlluxioException;
|
||||
import alluxio.master.LocalAlluxioCluster;
|
||||
import alluxio.shell.command.CommandUtils;
|
||||
import alluxio.util.FormatUtils;
|
||||
import alluxio.util.io.BufferUtils;
|
||||
import alluxio.util.io.PathUtils;
|
||||
|
||||
|
||||
public class AlluxioInterpreterTest {
|
||||
private AlluxioInterpreter alluxioInterpreter;
|
||||
private static final int SIZE_BYTES = Constants.MB * 10;
|
||||
private LocalAlluxioCluster mLocalAlluxioCluster = null;
|
||||
private FileSystem fs = null;
|
||||
|
||||
@After
|
||||
public final void after() throws Exception {
|
||||
if (alluxioInterpreter != null) {
|
||||
alluxioInterpreter.close();
|
||||
}
|
||||
mLocalAlluxioCluster.stop();
|
||||
}
|
||||
|
||||
@Before
|
||||
public final void before() throws Exception {
|
||||
mLocalAlluxioCluster = new LocalAlluxioCluster(SIZE_BYTES, 1000);
|
||||
mLocalAlluxioCluster.start();
|
||||
fs = mLocalAlluxioCluster.getClient();
|
||||
|
||||
final Properties props = new Properties();
|
||||
props.put(AlluxioInterpreter.ALLUXIO_MASTER_HOSTNAME, mLocalAlluxioCluster.getMasterHostname());
|
||||
props.put(AlluxioInterpreter.ALLUXIO_MASTER_PORT, mLocalAlluxioCluster.getMasterPort() + "");
|
||||
alluxioInterpreter = new AlluxioInterpreter(props);
|
||||
alluxioInterpreter.open();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompletion() {
|
||||
List<String> expectedResultOne = Arrays.asList("cat", "chgrp",
|
||||
"chmod", "chown", "copyFromLocal", "copyToLocal", "count",
|
||||
"createLineage");
|
||||
List<String> expectedResultTwo = Arrays.asList("copyFromLocal",
|
||||
"copyToLocal", "count");
|
||||
List<String> expectedResultThree = Arrays.asList("copyFromLocal", "copyToLocal");
|
||||
List<String> expectedResultNone = new ArrayList<String>();
|
||||
|
||||
List<String> resultOne = alluxioInterpreter.completion("c", 0);
|
||||
List<String> resultTwo = alluxioInterpreter.completion("co", 0);
|
||||
List<String> resultThree = alluxioInterpreter.completion("copy", 0);
|
||||
List<String> resultNotMatch = alluxioInterpreter.completion("notMatch", 0);
|
||||
List<String> resultAll = alluxioInterpreter.completion("", 0);
|
||||
|
||||
Assert.assertEquals(expectedResultOne, resultOne);
|
||||
Assert.assertEquals(expectedResultTwo, resultTwo);
|
||||
Assert.assertEquals(expectedResultThree, resultThree);
|
||||
Assert.assertEquals(expectedResultNone, resultNotMatch);
|
||||
Assert.assertEquals(alluxioInterpreter.keywords, resultAll);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void catDirectoryTest() throws IOException {
|
||||
String expected = "Successfully created directory /testDir\n\n" +
|
||||
"Path /testDir must be a file\n";
|
||||
|
||||
InterpreterResult output = alluxioInterpreter.interpret("mkdir /testDir" +
|
||||
"\ncat /testDir", null);
|
||||
|
||||
Assert.assertEquals(Code.ERROR, output.code());
|
||||
Assert.assertEquals(expected, output.message());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void catNotExistTest() throws IOException {
|
||||
InterpreterResult output = alluxioInterpreter.interpret("cat /testFile", null);
|
||||
Assert.assertEquals(Code.ERROR, output.code());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void catTest() throws IOException {
|
||||
FileSystemTestUtils.createByteFile(fs, "/testFile", WriteType.MUST_CACHE,
|
||||
10, 10);
|
||||
InterpreterResult output = alluxioInterpreter.interpret("cat /testFile", null);
|
||||
|
||||
byte[] expected = BufferUtils.getIncreasingByteArray(10);
|
||||
|
||||
Assert.assertEquals(Code.SUCCESS, output.code());
|
||||
Assert.assertArrayEquals(expected,
|
||||
output.message().substring(0, output.message().length() - 1).getBytes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void copyFromLocalLargeTest() throws IOException, AlluxioException {
|
||||
File testFile = new File(mLocalAlluxioCluster.getAlluxioHome() + "/testFile");
|
||||
testFile.createNewFile();
|
||||
FileOutputStream fos = new FileOutputStream(testFile);
|
||||
byte[] toWrite = BufferUtils.getIncreasingByteArray(SIZE_BYTES);
|
||||
fos.write(toWrite);
|
||||
fos.close();
|
||||
|
||||
InterpreterResult output = alluxioInterpreter.interpret("copyFromLocal " +
|
||||
testFile.getAbsolutePath() + " /testFile", null);
|
||||
Assert.assertEquals(
|
||||
"Copied " + testFile.getAbsolutePath() + " to /testFile\n\n",
|
||||
output.message());
|
||||
|
||||
long fileLength = fs.getStatus(new AlluxioURI("/testFile")).getLength();
|
||||
Assert.assertEquals(SIZE_BYTES, fileLength);
|
||||
|
||||
FileInStream fStream = fs.openFile(new AlluxioURI("/testFile"));
|
||||
byte[] read = new byte[SIZE_BYTES];
|
||||
fStream.read(read);
|
||||
Assert.assertTrue(BufferUtils.equalIncreasingByteArray(SIZE_BYTES, read));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadFileTest() throws IOException, AlluxioException {
|
||||
FileSystemTestUtils.createByteFile(fs, "/testFile", WriteType.CACHE_THROUGH, 10, 10);
|
||||
|
||||
int memPercentage = fs.getStatus(new AlluxioURI("/testFile")).getInMemoryPercentage();
|
||||
Assert.assertFalse(memPercentage == 0);
|
||||
|
||||
alluxioInterpreter.interpret("load /testFile", null);
|
||||
|
||||
memPercentage = fs.getStatus(new AlluxioURI("/testFile")).getInMemoryPercentage();
|
||||
Assert.assertTrue(memPercentage == 100);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadDirTest() throws IOException, AlluxioException {
|
||||
FileSystemTestUtils.createByteFile(fs, "/testRoot/testFileA", WriteType.CACHE_THROUGH, 10, 10);
|
||||
FileSystemTestUtils.createByteFile(fs, "/testRoot/testFileB", WriteType.MUST_CACHE, 10, 10);
|
||||
|
||||
int memPercentageA = fs.getStatus(new AlluxioURI("/testRoot/testFileA")).getInMemoryPercentage();
|
||||
int memPercentageB = fs.getStatus(new AlluxioURI("/testRoot/testFileB")).getInMemoryPercentage();
|
||||
Assert.assertFalse(memPercentageA == 0);
|
||||
Assert.assertTrue(memPercentageB == 100);
|
||||
|
||||
alluxioInterpreter.interpret("load /testRoot", null);
|
||||
|
||||
memPercentageA = fs.getStatus(new AlluxioURI("/testRoot/testFileA")).getInMemoryPercentage();
|
||||
memPercentageB = fs.getStatus(new AlluxioURI("/testRoot/testFileB")).getInMemoryPercentage();
|
||||
Assert.assertTrue(memPercentageA == 100);
|
||||
Assert.assertTrue(memPercentageB == 100);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void copyFromLocalTest() throws IOException, AlluxioException {
|
||||
File testDir = new File(mLocalAlluxioCluster.getAlluxioHome() + "/testDir");
|
||||
testDir.mkdir();
|
||||
File testDirInner = new File(mLocalAlluxioCluster.getAlluxioHome() + "/testDir/testDirInner");
|
||||
testDirInner.mkdir();
|
||||
File testFile =
|
||||
generateFileContent("/testDir/testFile", BufferUtils.getIncreasingByteArray(10));
|
||||
|
||||
generateFileContent("/testDir/testDirInner/testFile2",
|
||||
BufferUtils.getIncreasingByteArray(10, 20));
|
||||
|
||||
InterpreterResult output = alluxioInterpreter.interpret("copyFromLocal " +
|
||||
testFile.getParent() + " /testDir", null);
|
||||
Assert.assertEquals(
|
||||
"Copied " + testFile.getParent() + " to /testDir\n\n",
|
||||
output.message());
|
||||
|
||||
long fileLength1 = fs.getStatus(new AlluxioURI("/testDir/testFile")).getLength();
|
||||
long fileLength2 = fs.getStatus(new AlluxioURI("/testDir/testDirInner/testFile2")).getLength();
|
||||
Assert.assertEquals(10, fileLength1);
|
||||
Assert.assertEquals(20, fileLength2);
|
||||
|
||||
FileInStream fStream1 = fs.openFile(new AlluxioURI("/testDir/testFile"));
|
||||
FileInStream fStream2 = fs.openFile(new AlluxioURI("/testDir/testDirInner/testFile2"));
|
||||
byte[] read = new byte[10];
|
||||
fStream1.read(read);
|
||||
Assert.assertTrue(BufferUtils.equalIncreasingByteArray(10, read));
|
||||
read = new byte[20];
|
||||
fStream2.read(read);
|
||||
Assert.assertTrue(BufferUtils.equalIncreasingByteArray(10, 20, read));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void copyFromLocalTestWithFullURI() throws IOException, AlluxioException {
|
||||
File testFile = generateFileContent("/srcFileURI", BufferUtils.getIncreasingByteArray(10));
|
||||
String uri = "tachyon://" + mLocalAlluxioCluster.getMasterHostname() + ":"
|
||||
+ mLocalAlluxioCluster.getMasterPort() + "/destFileURI";
|
||||
|
||||
InterpreterResult output = alluxioInterpreter.interpret("copyFromLocal " +
|
||||
testFile.getPath() + " " + uri, null);
|
||||
Assert.assertEquals(
|
||||
"Copied " + testFile.getPath() + " to " + uri + "\n\n",
|
||||
output.message());
|
||||
|
||||
long fileLength = fs.getStatus(new AlluxioURI("/destFileURI")).getLength();
|
||||
Assert.assertEquals(10L, fileLength);
|
||||
|
||||
FileInStream fStream = fs.openFile(new AlluxioURI("/destFileURI"));
|
||||
byte[] read = new byte[10];
|
||||
fStream.read(read);
|
||||
Assert.assertTrue(BufferUtils.equalIncreasingByteArray(10, read));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void copyFromLocalFileToDstPathTest() throws IOException, AlluxioException {
|
||||
String dataString = "copyFromLocalFileToDstPathTest";
|
||||
byte[] data = dataString.getBytes();
|
||||
File localDir = new File(mLocalAlluxioCluster.getAlluxioHome() + "/localDir");
|
||||
localDir.mkdir();
|
||||
File localFile = generateFileContent("/localDir/testFile", data);
|
||||
|
||||
alluxioInterpreter.interpret("mkdir /dstDir", null);
|
||||
alluxioInterpreter.interpret("copyFromLocal " + localFile.getPath() + " /dstDir", null);
|
||||
|
||||
FileInStream fStream = fs.openFile(new AlluxioURI("/dstDir/testFile"));
|
||||
long fileLength = fs.getStatus(new AlluxioURI("/dstDir/testFile")).getLength();
|
||||
|
||||
byte[] read = new byte[(int) fileLength];
|
||||
fStream.read(read);
|
||||
Assert.assertEquals(new String(read), dataString);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void copyToLocalLargeTest() throws IOException {
|
||||
copyToLocalWithBytes(SIZE_BYTES);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void copyToLocalTest() throws IOException {
|
||||
copyToLocalWithBytes(10);
|
||||
}
|
||||
|
||||
private void copyToLocalWithBytes(int bytes) throws IOException {
|
||||
FileSystemTestUtils.createByteFile(fs, "/testFile", WriteType.MUST_CACHE, 10, 10);
|
||||
|
||||
InterpreterResult output = alluxioInterpreter.interpret("copyToLocal /testFile " +
|
||||
mLocalAlluxioCluster.getAlluxioHome() + "/testFile", null);
|
||||
|
||||
Assert.assertEquals(
|
||||
"Copied /testFile to " + mLocalAlluxioCluster.getAlluxioHome() + "/testFile\n\n",
|
||||
output.message());
|
||||
fileReadTest("/testFile", 10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void countNotExistTest() throws IOException {
|
||||
InterpreterResult output = alluxioInterpreter.interpret("count /NotExistFile", null);
|
||||
Assert.assertEquals(Code.ERROR, output.code());
|
||||
Assert.assertEquals(ExceptionMessage.PATH_DOES_NOT_EXIST.getMessage("/NotExistFile") + "\n",
|
||||
output.message());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void countTest() throws IOException {
|
||||
FileSystemTestUtils.createByteFile(fs, "/testRoot/testFileA",
|
||||
WriteType.CACHE_THROUGH, 10, 10);
|
||||
FileSystemTestUtils.createByteFile(fs, "/testRoot/testDir/testFileB",
|
||||
WriteType.CACHE_THROUGH, 20, 20);
|
||||
FileSystemTestUtils.createByteFile(fs, "/testRoot/testFileB",
|
||||
WriteType.CACHE_THROUGH, 30, 30);
|
||||
|
||||
InterpreterResult output = alluxioInterpreter.interpret("count /testRoot", null);
|
||||
|
||||
String expected = "";
|
||||
String format = "%-25s%-25s%-15s\n";
|
||||
expected += String.format(format, "File Count", "Folder Count", "Total Bytes");
|
||||
expected += String.format(format, 3, 2, 60);
|
||||
expected += "\n";
|
||||
Assert.assertEquals(expected, output.message());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fileinfoNotExistTest() throws IOException {
|
||||
InterpreterResult output = alluxioInterpreter.interpret("fileInfo /NotExistFile", null);
|
||||
Assert.assertEquals(ExceptionMessage.PATH_DOES_NOT_EXIST.getMessage("/NotExistFile") + "\n",
|
||||
output.message());
|
||||
Assert.assertEquals(Code.ERROR, output.code());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void locationNotExistTest() throws IOException {
|
||||
InterpreterResult output = alluxioInterpreter.interpret("location /NotExistFile", null);
|
||||
Assert.assertEquals(ExceptionMessage.PATH_DOES_NOT_EXIST.getMessage("/NotExistFile") + "\n",
|
||||
output.message());
|
||||
Assert.assertEquals(Code.ERROR, output.code());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lsTest() throws IOException, AlluxioException {
|
||||
URIStatus[] files = new URIStatus[3];
|
||||
|
||||
FileSystemTestUtils.createByteFile(fs, "/testRoot/testFileA",
|
||||
WriteType.MUST_CACHE, 10, 10);
|
||||
FileSystemTestUtils.createByteFile(fs, "/testRoot/testDir/testFileB",
|
||||
WriteType.MUST_CACHE, 20, 20);
|
||||
FileSystemTestUtils.createByteFile(fs, "/testRoot/testFileC",
|
||||
WriteType.THROUGH, 30, 30);
|
||||
|
||||
files[0] = fs.getStatus(new AlluxioURI("/testRoot/testFileA"));
|
||||
files[1] = fs.getStatus(new AlluxioURI("/testRoot/testDir"));
|
||||
files[2] = fs.getStatus(new AlluxioURI("/testRoot/testFileC"));
|
||||
|
||||
InterpreterResult output = alluxioInterpreter.interpret("ls /testRoot", null);
|
||||
|
||||
String expected = "";
|
||||
String format = "%-10s%-25s%-15s%-5s\n";
|
||||
expected += String.format(format, FormatUtils.getSizeFromBytes(10),
|
||||
CommandUtils.convertMsToDate(files[0].getCreationTimeMs()), "In Memory", "/testRoot/testFileA");
|
||||
expected += String.format(format, FormatUtils.getSizeFromBytes(0),
|
||||
CommandUtils.convertMsToDate(files[1].getCreationTimeMs()), "", "/testRoot/testDir");
|
||||
expected += String.format(format, FormatUtils.getSizeFromBytes(30),
|
||||
CommandUtils.convertMsToDate(files[2].getCreationTimeMs()), "Not In Memory",
|
||||
"/testRoot/testFileC");
|
||||
expected += "\n";
|
||||
|
||||
Assert.assertEquals(Code.SUCCESS, output.code());
|
||||
Assert.assertEquals(expected, output.message());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lsRecursiveTest() throws IOException, AlluxioException {
|
||||
URIStatus[] files = new URIStatus[4];
|
||||
|
||||
FileSystemTestUtils.createByteFile(fs, "/testRoot/testFileA",
|
||||
WriteType.MUST_CACHE, 10, 10);
|
||||
FileSystemTestUtils.createByteFile(fs, "/testRoot/testDir/testFileB",
|
||||
WriteType.MUST_CACHE, 20, 20);
|
||||
FileSystemTestUtils.createByteFile(fs, "/testRoot/testFileC",
|
||||
WriteType.THROUGH, 30, 30);
|
||||
|
||||
files[0] = fs.getStatus(new AlluxioURI("/testRoot/testFileA"));
|
||||
files[1] = fs.getStatus(new AlluxioURI("/testRoot/testDir"));
|
||||
files[2] = fs.getStatus(new AlluxioURI("/testRoot/testDir/testFileB"));
|
||||
files[3] = fs.getStatus(new AlluxioURI("/testRoot/testFileC"));
|
||||
|
||||
InterpreterResult output = alluxioInterpreter.interpret("ls -R /testRoot", null);
|
||||
|
||||
String expected = "";
|
||||
String format = "%-10s%-25s%-15s%-5s\n";
|
||||
expected +=
|
||||
String.format(format, FormatUtils.getSizeFromBytes(10),
|
||||
CommandUtils.convertMsToDate(files[0].getCreationTimeMs()), "In Memory",
|
||||
"/testRoot/testFileA");
|
||||
expected +=
|
||||
String.format(format, FormatUtils.getSizeFromBytes(0),
|
||||
CommandUtils.convertMsToDate(files[1].getCreationTimeMs()), "", "/testRoot/testDir");
|
||||
expected +=
|
||||
String.format(format, FormatUtils.getSizeFromBytes(20),
|
||||
CommandUtils.convertMsToDate(files[2].getCreationTimeMs()), "In Memory",
|
||||
"/testRoot/testDir/testFileB");
|
||||
expected +=
|
||||
String.format(format, FormatUtils.getSizeFromBytes(30),
|
||||
CommandUtils.convertMsToDate(files[3].getCreationTimeMs()), "Not In Memory",
|
||||
"/testRoot/testFileC");
|
||||
expected += "\n";
|
||||
|
||||
Assert.assertEquals(expected, output.message());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mkdirComplexPathTest() throws IOException, AlluxioException {
|
||||
InterpreterResult output = alluxioInterpreter.interpret(
|
||||
"mkdir /Complex!@#$%^&*()-_=+[]{};\"'<>,.?/File", null);
|
||||
|
||||
boolean existsDir = fs.exists(new AlluxioURI("/Complex!@#$%^&*()-_=+[]{};\"'<>,.?/File"));
|
||||
Assert.assertEquals(
|
||||
"Successfully created directory /Complex!@#$%^&*()-_=+[]{};\"'<>,.?/File\n\n",
|
||||
output.message());
|
||||
Assert.assertTrue(existsDir);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mkdirExistingTest() throws IOException {
|
||||
String command = "mkdir /festFile1";
|
||||
Assert.assertEquals(Code.SUCCESS, alluxioInterpreter.interpret(command, null).code());
|
||||
Assert.assertEquals(Code.ERROR, alluxioInterpreter.interpret(command, null).code());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mkdirInvalidPathTest() throws IOException {
|
||||
Assert.assertEquals(
|
||||
Code.ERROR,
|
||||
alluxioInterpreter.interpret("mkdir /test File Invalid Path", null).code());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mkdirShortPathTest() throws IOException, AlluxioException {
|
||||
InterpreterResult output = alluxioInterpreter.interpret("mkdir /root/testFile1", null);
|
||||
boolean existsDir = fs.exists(new AlluxioURI("/root/testFile1"));
|
||||
Assert.assertEquals(
|
||||
"Successfully created directory /root/testFile1\n\n",
|
||||
output.message());
|
||||
Assert.assertTrue(existsDir);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mkdirTest() throws IOException, AlluxioException {
|
||||
String qualifiedPath =
|
||||
"tachyon://" + mLocalAlluxioCluster.getMasterHostname() + ":"
|
||||
+ mLocalAlluxioCluster.getMasterPort() + "/root/testFile1";
|
||||
InterpreterResult output = alluxioInterpreter.interpret("mkdir " + qualifiedPath, null);
|
||||
boolean existsDir = fs.exists(new AlluxioURI("/root/testFile1"));
|
||||
Assert.assertEquals(
|
||||
"Successfully created directory " + qualifiedPath + "\n\n",
|
||||
output.message());
|
||||
Assert.assertTrue(existsDir);
|
||||
}
|
||||
|
||||
private File generateFileContent(String path, byte[] toWrite)
|
||||
throws IOException {
|
||||
File testFile = new File(mLocalAlluxioCluster.getAlluxioHome() + path);
|
||||
testFile.createNewFile();
|
||||
FileOutputStream fos = new FileOutputStream(testFile);
|
||||
fos.write(toWrite);
|
||||
fos.close();
|
||||
return testFile;
|
||||
}
|
||||
|
||||
private void fileReadTest(String fileName, int size) throws IOException {
|
||||
File testFile = new File(PathUtils.concatPath(mLocalAlluxioCluster.getAlluxioHome(), fileName));
|
||||
FileInputStream fis = new FileInputStream(testFile);
|
||||
byte[] read = new byte[size];
|
||||
fis.read(read);
|
||||
fis.close();
|
||||
Assert.assertTrue(BufferUtils.equalIncreasingByteArray(size, read));
|
||||
}
|
||||
}
|
||||
|
|
@ -36,10 +36,6 @@ if [[ -z "${ZEPPELIN_LOG_DIR}" ]]; then
|
|||
export ZEPPELIN_LOG_DIR="${ZEPPELIN_HOME}/logs"
|
||||
fi
|
||||
|
||||
if [[ -z "${ZEPPELIN_NOTEBOOK_DIR}" ]]; then
|
||||
export ZEPPELIN_NOTEBOOK_DIR="${ZEPPELIN_HOME}/notebook"
|
||||
fi
|
||||
|
||||
if [[ -z "$ZEPPELIN_PID_DIR" ]]; then
|
||||
export ZEPPELIN_PID_DIR="${ZEPPELIN_HOME}/run"
|
||||
fi
|
||||
|
|
@ -108,7 +104,7 @@ if [[ -z "${ZEPPELIN_INTP_MEM}" ]]; then
|
|||
export ZEPPELIN_INTP_MEM="${ZEPPELIN_MEM}"
|
||||
fi
|
||||
|
||||
JAVA_INTP_OPTS+=" ${ZEPPELIN_INTP_JAVA_OPTS} -Dfile.encoding=${ZEPPELIN_ENCODING}"
|
||||
JAVA_INTP_OPTS="${ZEPPELIN_INTP_JAVA_OPTS} -Dfile.encoding=${ZEPPELIN_ENCODING}"
|
||||
export JAVA_INTP_OPTS
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Copyright 2007 The Apache Software Foundation
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
|
|
|
|||
|
|
@ -19,12 +19,11 @@
|
|||
bin=$(dirname "${BASH_SOURCE-$0}")
|
||||
bin=$(cd "${bin}">/dev/null; pwd)
|
||||
|
||||
|
||||
function usage() {
|
||||
echo "usage) $0 -p <port> -d <directory to load>"
|
||||
echo "usage) $0 -p <port> -d <interpreter dir to load> -l <local interpreter repo dir to load>"
|
||||
}
|
||||
|
||||
while getopts "hp:d:" o; do
|
||||
while getopts "hp:d:l:" o; do
|
||||
case ${o} in
|
||||
h)
|
||||
usage
|
||||
|
|
@ -36,6 +35,9 @@ while getopts "hp:d:" o; do
|
|||
p)
|
||||
PORT=${OPTARG}
|
||||
;;
|
||||
l)
|
||||
LOCAL_INTERPRETER_REPO=${OPTARG}
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
|
@ -79,7 +81,7 @@ if [[ "${INTERPRETER_ID}" == "spark" ]]; then
|
|||
export SPARK_SUBMIT="${SPARK_HOME}/bin/spark-submit"
|
||||
SPARK_APP_JAR="$(ls ${ZEPPELIN_HOME}/interpreter/spark/zeppelin-spark*.jar)"
|
||||
# This will evantually passes SPARK_APP_JAR to classpath of SparkIMain
|
||||
ZEPPELIN_CLASSPATH=${SPARK_APP_JAR}
|
||||
ZEPPELIN_CLASSPATH+=${SPARK_APP_JAR}
|
||||
|
||||
pattern="$SPARK_HOME/python/lib/py4j-*-src.zip"
|
||||
py4j=($pattern)
|
||||
|
|
@ -128,6 +130,8 @@ if [[ "${INTERPRETER_ID}" == "spark" ]]; then
|
|||
fi
|
||||
fi
|
||||
|
||||
addJarInDir "${LOCAL_INTERPRETER_REPO}"
|
||||
|
||||
CLASSPATH+=":${ZEPPELIN_CLASSPATH}"
|
||||
|
||||
if [[ -n "${SPARK_SUBMIT}" ]]; then
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Copyright 2007 The Apache Software Foundation
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
|
|
@ -21,7 +19,7 @@
|
|||
# description: Start and stop daemon script for.
|
||||
#
|
||||
|
||||
USAGE="Usage: zeppelin-daemon.sh [--config <conf-dir>] {start|stop|restart|reload|status}"
|
||||
USAGE="Usage: zeppelin-daemon.sh [--config <conf-dir>] {start|stop|upstart|restart|reload|status}"
|
||||
|
||||
if [[ "$1" == "--config" ]]; then
|
||||
shift
|
||||
|
|
@ -93,11 +91,6 @@ function initialize_default_directories() {
|
|||
echo "Pid dir doesn't exist, create ${ZEPPELIN_PID_DIR}"
|
||||
$(mkdir -p "${ZEPPELIN_PID_DIR}")
|
||||
fi
|
||||
|
||||
if [[ ! -d "${ZEPPELIN_NOTEBOOK_DIR}" ]]; then
|
||||
echo "Notebook dir doesn't exist, create ${ZEPPELIN_NOTEBOOK_DIR}"
|
||||
$(mkdir -p "${ZEPPELIN_NOTEBOOK_DIR}")
|
||||
fi
|
||||
}
|
||||
|
||||
function wait_for_zeppelin_to_die() {
|
||||
|
|
@ -159,6 +152,16 @@ function check_if_process_is_alive() {
|
|||
fi
|
||||
}
|
||||
|
||||
function upstart() {
|
||||
|
||||
# upstart() allows zeppelin to be run and managed as a service
|
||||
# for example, this could be called from an upstart script in /etc/init
|
||||
# where the service manager starts and stops the process
|
||||
initialize_default_directories
|
||||
|
||||
$ZEPPELIN_RUNNER $JAVA_OPTS -cp $ZEPPELIN_CLASSPATH_OVERRIDES:$CLASSPATH $ZEPPELIN_MAIN >> "${ZEPPELIN_OUTFILE}"
|
||||
}
|
||||
|
||||
function start() {
|
||||
local pid
|
||||
|
||||
|
|
@ -241,6 +244,9 @@ case "${1}" in
|
|||
stop)
|
||||
stop
|
||||
;;
|
||||
upstart)
|
||||
upstart
|
||||
;;
|
||||
reload)
|
||||
stop
|
||||
start
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Copyright 2007 The Apache Software Foundation
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
|
|
|
|||
|
|
@ -35,18 +35,19 @@
|
|||
<url>http://zeppelin.incubator.apache.org</url>
|
||||
|
||||
<properties>
|
||||
<cassandra.driver.version>2.1.7.1</cassandra.driver.version>
|
||||
<cassandra.driver.version>3.0.0-rc1</cassandra.driver.version>
|
||||
<snappy.version>1.0.5.4</snappy.version>
|
||||
<lz4.version>1.2.0</lz4.version>
|
||||
<scala.version>2.11.7</scala.version>
|
||||
<scala.binary.version>2.11</scala.binary.version>
|
||||
<commons-lang.version>3.4</commons-lang.version>
|
||||
<lz4.version>1.3.0</lz4.version>
|
||||
<scala.version>2.10.4</scala.version>
|
||||
<scala.binary.version>2.10</scala.binary.version>
|
||||
<commons-lang.version>3.3.2</commons-lang.version>
|
||||
<scalate.version>1.7.1</scalate.version>
|
||||
<cassandra.guava.version>16.0.1</cassandra.guava.version>
|
||||
|
||||
<!--TEST-->
|
||||
<scalatest.version>2.2.4</scalatest.version>
|
||||
<junit.version>4.12</junit.version>
|
||||
<achilles.version>3.2.2</achilles.version>
|
||||
<achilles.version>3.2.4-Zeppelin</achilles.version>
|
||||
<assertj.version>1.7.0</assertj.version>
|
||||
<mockito.version>1.9.5</mockito.version>
|
||||
</properties>
|
||||
|
|
@ -65,6 +66,12 @@
|
|||
<version>${cassandra.driver.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>${cassandra.guava.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Compression libraries for the cassandra-driver protocol. -->
|
||||
<!-- Include both compression options to make to simplify deployment. -->
|
||||
|
||||
|
|
@ -130,7 +137,7 @@
|
|||
|
||||
<dependency>
|
||||
<groupId>info.archinnov</groupId>
|
||||
<artifactId>achilles-junit</artifactId>
|
||||
<artifactId>achilles-embedded</artifactId>
|
||||
<version>${achilles.version}</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
|
|
@ -145,13 +152,6 @@
|
|||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>16.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
|
|
@ -167,7 +167,6 @@
|
|||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Plugin to compile Scala code -->
|
||||
|
|
@ -293,7 +292,6 @@
|
|||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
|
|
|||
81
cassandra/src/main/resources/scalate/aggregateDetails.ssp
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
<%--
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.
|
||||
*/
|
||||
--%>
|
||||
#import(org.apache.zeppelin.cassandra.MetaDataHierarchy._)
|
||||
<%@ val sameNameAggregateDetails: SameNameAggregateDetails %>
|
||||
<%@ val withCaption: Boolean%>
|
||||
<div class="row">
|
||||
<div class="col-md-2"/>
|
||||
<div class="col-md-8 col-offset-md-2">
|
||||
#for (aggregate <- sameNameAggregateDetails.aggregates)
|
||||
<div class="panel panel-default table-responsive table-bordered">
|
||||
<table class="table">
|
||||
#if(withCaption)
|
||||
<caption><h4 class="text-success"><i class="glyphicon glyphicon-retweet"/> ${aggregate.name}</h4></caption>
|
||||
#end
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="col-md-6"><strong>Keyspace</strong></td>
|
||||
<td class="col-md-6 text-danger">${aggregate.keyspace}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="col-md-6"><strong>Arguments</strong></td>
|
||||
<td class="col-md-6">${aggregate.arguments.toList.mkString(", ")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="col-md-6"><strong>State Function</strong></td>
|
||||
<td class="col-md-6">${aggregate.sFunc}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="col-md-6"><strong>State Type</strong></td>
|
||||
<td class="col-md-6">${aggregate.sType}</td>
|
||||
</tr>
|
||||
#if(aggregate.finalFunc.isDefined)
|
||||
<tr>
|
||||
<td class="col-md-6"><strong>Final Function</strong></td>
|
||||
<td class="col-md-6">${aggregate.finalFunc.get}</td>
|
||||
</tr>
|
||||
#end
|
||||
#if(aggregate.initCond.isDefined)
|
||||
<tr>
|
||||
<td class="col-md-6"><strong>Initial State</strong></td>
|
||||
<td class="col-md-6">${aggregate.initCond.get}</td>
|
||||
</tr>
|
||||
#end
|
||||
<tr>
|
||||
<td class="col-md-6"><strong>Return type</strong></td>
|
||||
<td class="col-md-6">${aggregate.returnType}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="panel-footer">
|
||||
<a data-toggle="collapse" data-target="#${aggregate.uniqueId}_asCQL">
|
||||
<strong>As CQL statement</strong>
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
<br/><br/>
|
||||
<div class="text-success collapse" id="${aggregate.uniqueId}_asCQL">
|
||||
<pre class="well">${aggregate.asCQL}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
#end
|
||||
</div>
|
||||
<div class="col-md-2"></div>
|
||||
</div>
|
||||
68
cassandra/src/main/resources/scalate/allAggregates.ssp
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
<%--
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.
|
||||
*/
|
||||
--%>
|
||||
|
||||
#import(org.apache.zeppelin.cassandra.MetaDataHierarchy._)
|
||||
#import(java.util.UUID)
|
||||
<%@ val allAggregates: Map[(UUID, String), List[AggregateSummary]] %>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div class="row">
|
||||
<div class="panel-group" role="tablist" aria-multiselectable="true">
|
||||
#for (((ksId,ksName), aggregates) <- allAggregates)
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab">
|
||||
<h4 class="panel-title">
|
||||
<a role="button" data-toggle="collapse" data-target="#${ksId}" aria-expanded="false">
|
||||
<span class="text-danger"><i class="glyphicon glyphicon-folder-open"/> ${ksName}</span>
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="${ksId}" class="panel-collapse collapse" role="tabpanel">
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="col-md-2"/>
|
||||
<div class="col-md-8 col-offset-md-2 table-responsive table-bordered">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Aggregate</th>
|
||||
<th>Return Type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
#for (aggregate <- aggregates)
|
||||
<tr class="text-success">
|
||||
<td>${aggregate.name + aggregate.arguments.mkString("(", ", ", ")")}</td>
|
||||
<td>${aggregate.returnType}</td>
|
||||
</tr>
|
||||
#end
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-2"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
#end
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
68
cassandra/src/main/resources/scalate/allFunctions.ssp
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
<%--
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.
|
||||
*/
|
||||
--%>
|
||||
|
||||
#import(org.apache.zeppelin.cassandra.MetaDataHierarchy._)
|
||||
#import(java.util.UUID)
|
||||
<%@ val allFunctions: Map[(UUID, String), List[FunctionSummary]] %>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div class="row">
|
||||
<div class="panel-group" role="tablist" aria-multiselectable="true">
|
||||
#for (((ksId,ksName), functions) <- allFunctions)
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab">
|
||||
<h4 class="panel-title">
|
||||
<a role="button" data-toggle="collapse" data-target="#${ksId}" aria-expanded="false">
|
||||
<span class="text-danger"><i class="glyphicon glyphicon-folder-open"/> ${ksName}</span>
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="${ksId}" class="panel-collapse collapse" role="tabpanel">
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="col-md-2"/>
|
||||
<div class="col-md-8 col-offset-md-2 table-responsive table-bordered">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Function</th>
|
||||
<th>Return Type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
#for (function <- functions)
|
||||
<tr class="text-success">
|
||||
<td>${function.name + function.arguments.mkString("(",", ", ")")}</td>
|
||||
<td>${function.returnType}</td>
|
||||
</tr>
|
||||
#end
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-2"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
#end
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<%--
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.
|
||||
*/
|
||||
--%>
|
||||
#import(org.apache.zeppelin.cassandra.MetaDataHierarchy._)
|
||||
#import(java.util.UUID)
|
||||
<%@ val allMVs: Map[(UUID,String),List[MaterializedViewSummary]] %>
|
||||
<div class="container">
|
||||
|
||||
<div class="row">
|
||||
<div class="panel-group" role="tablist" aria-multiselectable="true">
|
||||
#for (((ksId,ksName), mvs) <- allMVs)
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab">
|
||||
<h4 class="panel-title">
|
||||
<a role="button" data-toggle="collapse" data-target="#${ksId}" aria-expanded="false">
|
||||
<span class="text-danger"><i class="glyphicon glyphicon-folder-open"/> ${ksName}</span>
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="${ksId}" class="panel-collapse collapse" role="tabpanel">
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="col-md-2"/>
|
||||
<div class="col-md-8 col-offset-md-2 table-responsive table-bordered">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr><th>Materialized View</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
#for (mv <- mvs)
|
||||
|
||||
<tr class="text-primary">
|
||||
<td>
|
||||
${mv.name}
|
||||
<i class="glyphicon glyphicon-arrow-right"/>
|
||||
<i class="glyphicon glyphicon-th-list"/>
|
||||
${mv.baseTable}
|
||||
</td>
|
||||
</tr>
|
||||
#end
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-2"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
#end
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -24,7 +24,6 @@
|
|||
<div class="row">
|
||||
<div class="panel-group" role="tablist" aria-multiselectable="true">
|
||||
#for (((ksId,ksName), tables) <- allTables)
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab">
|
||||
<h4 class="panel-title">
|
||||
|
|
@ -38,8 +37,6 @@
|
|||
<div class="row">
|
||||
<div class="col-md-2"/>
|
||||
<div class="col-md-8 col-offset-md-2 table-responsive table-bordered">
|
||||
#if (tables.nonEmpty)
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr><th>Tables</th></tr>
|
||||
|
|
@ -52,10 +49,6 @@
|
|||
|
||||
</tbody>
|
||||
</table>
|
||||
#else
|
||||
<span><h4>No Table</h4></span>
|
||||
|
||||
#end
|
||||
</div>
|
||||
<div class="col-md-2"/>
|
||||
</div>
|
||||
|
|
@ -63,7 +56,6 @@
|
|||
</div>
|
||||
</div>
|
||||
#end
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
61
cassandra/src/main/resources/scalate/allUDTs.ssp
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<%--
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.
|
||||
*/
|
||||
--%>
|
||||
#import(org.apache.zeppelin.cassandra.MetaDataHierarchy._)
|
||||
#import(java.util.UUID)
|
||||
<%@ val allUDTs: Map[(UUID,String),List[String]] %>
|
||||
<div class="container">
|
||||
|
||||
<div class="row">
|
||||
<div class="panel-group" role="tablist" aria-multiselectable="true">
|
||||
#for (((ksId,ksName), udts) <- allUDTs)
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab">
|
||||
<h4 class="panel-title">
|
||||
<a role="button" data-toggle="collapse" data-target="#${ksId}" aria-expanded="false">
|
||||
<span class="text-danger"><i class="glyphicon glyphicon-folder-open"/> ${ksName}</span>
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="${ksId}" class="panel-collapse collapse" role="tabpanel">
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="col-md-2"/>
|
||||
<div class="col-md-8 col-offset-md-2 table-responsive table-bordered">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr><th>UDT</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
#for (udt <- udts)
|
||||
|
||||
<tr class="text-warning"><td>${udt}</td></tr>
|
||||
#end
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-2"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
#end
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -35,8 +35,19 @@
|
|||
#end
|
||||
#end
|
||||
|
||||
#if(ksContent.udts.nonEmpty)
|
||||
#if(ksContent.views.nonEmpty)
|
||||
<li role="separator" class="divider text-muted"></li>
|
||||
<li class="dropdown-header"><span class="text-primary">Materialized Views</span></li>
|
||||
#for((id,name,_) <- ksContent.views)
|
||||
<li>
|
||||
<a role="button" data-toggle="collapse" data-target="#${id}">
|
||||
<span class="text-primary"><i class="glyphicon glyphicon-eye-open"/> ${name}</span>
|
||||
</a>
|
||||
</li>
|
||||
#end
|
||||
#end
|
||||
|
||||
#if(ksContent.udts.nonEmpty)
|
||||
<li role="separator" class="divider text-muted"></li>
|
||||
<li class="dropdown-header"><span class="text-warning">User Defined Types</span></li>
|
||||
#for((id,name,_) <- ksContent.udts)
|
||||
|
|
@ -47,6 +58,30 @@
|
|||
</li>
|
||||
#end
|
||||
#end
|
||||
|
||||
#if(ksContent.functions.nonEmpty)
|
||||
<li role="separator" class="divider text-muted"></li>
|
||||
<li class="dropdown-header"><span class="text-success">Functions</span></li>
|
||||
#for((id,name,_) <- ksContent.functions)
|
||||
<li>
|
||||
<a role="button" data-toggle="collapse" data-target="#${id}">
|
||||
<span class="text-success"><i class="glyphicon glyphicon-random"/> ${name}</span>
|
||||
</a>
|
||||
</li>
|
||||
#end
|
||||
#end
|
||||
|
||||
#if(ksContent.aggregates.nonEmpty)
|
||||
<li role="separator" class="divider text-muted"></li>
|
||||
<li class="dropdown-header"><span class="text-success">Aggregates</span></li>
|
||||
#for((id,name,_) <- ksContent.aggregates)
|
||||
<li>
|
||||
<a role="button" data-toggle="collapse" data-target="#${id}">
|
||||
<span class="text-success"><i class="glyphicon glyphicon-retweet"/> ${name}</span>
|
||||
</a>
|
||||
</li>
|
||||
#end
|
||||
#end
|
||||
</ul>
|
||||
</a>
|
||||
</li>
|
||||
77
cassandra/src/main/resources/scalate/functionDetails.ssp
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
<%--
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.
|
||||
*/
|
||||
--%>
|
||||
#import(org.apache.zeppelin.cassandra.MetaDataHierarchy._)
|
||||
<%@ val sameNameFunctionDetails: SameNameFunctionDetails %>
|
||||
<%@ val withCaption: Boolean%>
|
||||
<div class="row">
|
||||
<div class="col-md-2"/>
|
||||
<div class="col-md-8 col-offset-md-2">
|
||||
#for (function <- sameNameFunctionDetails.functions)
|
||||
<div class="panel panel-default table-responsive table-bordered">
|
||||
<table class="table">
|
||||
#if(withCaption)
|
||||
<caption><h4 class="text-success"><i class="glyphicon glyphicon-random"/> ${function.name}</h4></caption>
|
||||
#end
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="col-md-6"><strong>Keyspace</strong></td>
|
||||
<td class="col-md-6 text-danger">${function.keyspace}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="col-md-6"><strong>Arguments</strong></td>
|
||||
<td class="col-md-6">${function.arguments.toList.mkString(", ")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="col-md-6"><strong>Null Input</strong></td>
|
||||
#if(function.calledOnNullInput)
|
||||
<td class="col-md-6">CALLED ON NULL INPUT</td>
|
||||
#else
|
||||
<td class="col-md-6">RETURN NULL ON NULL INPUT</td>
|
||||
#end
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="col-md-6"><strong>Return type</strong></td>
|
||||
<td class="col-md-6">${function.returnType}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="col-md-6"><strong>Language</strong></td>
|
||||
<td class="col-md-6">${function.language}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="col-md-6"><strong>Body</strong></td>
|
||||
<td class="col-md-6">${escape(function.body)}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="panel-footer">
|
||||
<a data-toggle="collapse" data-target="#${function.uniqueId}_asCQL">
|
||||
<strong>As CQL statement</strong>
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
<br/><br/>
|
||||
<div class="text-success collapse" id="${function.uniqueId}_asCQL">
|
||||
<pre class="well">${function.asCQL}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
#end
|
||||
</div>
|
||||
<div class="col-md-2"></div>
|
||||
</div>
|
||||
|
|
@ -27,7 +27,9 @@
|
|||
<%@ val preparedStatementsId: UUID = UUIDs.random() %>
|
||||
<%@ val dynamicFormsId: UUID = UUIDs.random() %>
|
||||
<%@ val configurationId: UUID = UUIDs.random() %>
|
||||
<%@ val miscId: UUID = UUIDs.random() %>
|
||||
<%@ val sharedStatesId: UUID = UUIDs.random() %>
|
||||
<%@ val changelogId: UUID = UUIDs.random() %>
|
||||
<%@ val contactsId: UUID = UUIDs.random() %>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
|
@ -70,10 +72,20 @@
|
|||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button" data-toggle="collapse" data-target="#${miscId}">
|
||||
<span class="text-info"><i class="glyphicon glyphicon-bookmark"/> Misc</span>
|
||||
<a role="button" data-toggle="collapse" data-target="#${sharedStatesId}">
|
||||
<span class="text-info"><i class="glyphicon glyphicon-bookmark"/> Shared States</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button" data-toggle="collapse" data-target="#${changelogId}">
|
||||
<span class="text-info"><i class="glyphicon glyphicon-bookmark"/> Change Log</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button" data-toggle="collapse" data-target="#${contactsId}">
|
||||
<span class="text-info"><i class="glyphicon glyphicon-bookmark"/> Contacts & Bugs</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</a>
|
||||
</li>
|
||||
|
|
@ -91,12 +103,12 @@
|
|||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a role="button">
|
||||
<span class="text-info">Version <strong>1.0</strong></span>
|
||||
<span class="text-info">Version <strong>2.0</strong></span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button">
|
||||
<span class="text-info">Java Driver Version <strong>2.1.7.1</strong></span>
|
||||
<span class="text-info">Java Driver Version <strong>3.0.0-rc1</strong></span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
|
|
@ -237,7 +249,7 @@
|
|||
<h3>II Comments</h3>
|
||||
<p>
|
||||
It is possible to add comments between statements. Single line comments start with the
|
||||
<strong>hash</strong> sign (#). Multi-line comments are enclosed between
|
||||
<strong>hash</strong> sign (#) or <strong>double slashes</strong> (//). Multi-line comments are enclosed between
|
||||
<strong>/**</strong> and <strong>**/</strong>. Ex:
|
||||
|
||||
<br/>
|
||||
|
|
@ -246,9 +258,12 @@
|
|||
<div class="col-md-8 col-md-offset-2">
|
||||
<pre>
|
||||
|
||||
#First comment
|
||||
#Single line comment style 1
|
||||
INSERT INTO users(login,name) VALUES('jdoe','John DOE');
|
||||
|
||||
//Single line comment style 2
|
||||
|
||||
|
||||
/**
|
||||
Multi line
|
||||
comments
|
||||
|
|
@ -313,6 +328,22 @@
|
|||
<td><strong>DESCRIBE TABLES;</strong></td>
|
||||
<td>List all existing keyspaces in the cluster and for each, all the tables name</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>DESCRIBE TYPES;</strong></td>
|
||||
<td>List all existing keyspaces in the cluster and for each, all the types name</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>DESCRIBE FUNCTIONS;</strong></td>
|
||||
<td>List all existing keyspaces in the cluster and for each, all the functions name and arguments</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>DESCRIBE AGGREGATES;</strong></td>
|
||||
<td>List all existing keyspaces in the cluster and for each, all the aggregates name and arguments</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>DESCRIBE MATERIALIZED VIEWS;</strong></td>
|
||||
<td>List all existing keyspaces in the cluster and for each, all the materialized view name</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>DESCRIBE KEYSPACE <keyspace name>;</strong></td>
|
||||
<td>Describe the given keyspace configuration and all its table details (name, columns, ...)</td>
|
||||
|
|
@ -333,6 +364,30 @@
|
|||
the default <em>system</em> keyspace is used. If no type is found, an error message is raised
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>DESCRIBE FUNCTION <em>(<keyspace name>).</em><function name>;</strong></td>
|
||||
<td>
|
||||
Describe the given function. If the keyspace is not provided, the current
|
||||
<strong>logged in</strong> keyspace is used. If there is no logged in keyspace,
|
||||
the default <em>system</em> keyspace is used. If no function is found, an error message is raised
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>DESCRIBE AGGREGATE <em>(<keyspace name>).</em><aggregate name>;</strong></td>
|
||||
<td>
|
||||
Describe the given aggregate. If the keyspace is not provided, the current
|
||||
<strong>logged in</strong> keyspace is used. If there is no logged in keyspace,
|
||||
the default <em>system</em> keyspace is used. If no aggregate is found, an error message is raised
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>DESCRIBE MATERIALIZED VIEW <em>(<keyspace name>).</em><view name>;</strong></td>
|
||||
<td>
|
||||
Describe the given materialized view. If the keyspace is not provided, the current
|
||||
<strong>logged in</strong> keyspace is used. If there is no logged in keyspace,
|
||||
the default <em>system</em> keyspace is used. If no materialized view is found, an error message is raised
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<br/>
|
||||
|
|
@ -342,9 +397,7 @@
|
|||
</p>
|
||||
<h3>II Schema Display</h3>
|
||||
<p>
|
||||
The schema objects (cluster, keyspace, table & type) are displayed in a tabular format.
|
||||
There is a <strong>drop-down</strong> menu on the top left corner to expand objects details.
|
||||
On the top right menu is shown the Icon legend.
|
||||
The schema objects (cluster, keyspace, table, type, view, function & aggregate) are displayed in a tabular format. There is a <strong>drop-down</strong> menu on the top left corner to expand objects details. On the top right menu is shown the Icon legend.
|
||||
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -886,16 +939,68 @@
|
|||
<div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab">
|
||||
<h4 class="panel-title">
|
||||
<a role="button" data-toggle="collapse" data-target="#${miscId}" aria-expanded="false">
|
||||
<span class="text-info"><strong>Miscellaneous</strong></span>
|
||||
<a role="button" data-toggle="collapse" data-target="#${sharedStatesId}" aria-expanded="false">
|
||||
<span class="text-info"><strong>Shared states</strong></span>
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="${miscId}" class="panel-collapse collapse" role="tabpanel">
|
||||
<div id="${sharedStatesId}" class="panel-collapse collapse" role="tabpanel">
|
||||
<div class="panel-body">
|
||||
<h3>Execution parallelism</h3>
|
||||
It is possible to execute many paragraphs in parallel. However, at the back-end side, we’re still using <strong>synchronous</strong> queries. Asynchronous execution is only possible when it is possible to return a Future value in the <strong>InterpreterResult</strong>. It may be an interesting proposal for the Zeppelin project.
|
||||
It is possible to execute many paragraphs in parallel. However, at the back-end side, we’re still using synchronous queries. <em>Asynchronous execution</em> is only possible when it is possible to return a <strong>Future</strong> value in the <strong>InterpreterResult</strong>. It may be an interesting proposal for the <strong>Zeppelin</strong> project.
|
||||
<br/>
|
||||
Another caveat is that the same <strong>com.datastax.driver.core.Session</strong> object is used for <strong>all</strong> notebooks and paragraphs. Consequently, if you use the <em>USE keyspace name;</em> statement to log into a keyspace, it will change the keyspace for <strong>all current users</strong> of the Cassandra interpreter because we only create 1 <strong>com.datastax.driver.core.Session</strong> object per instance of <strong>Cassandra</strong> interpreter.
|
||||
<br/>
|
||||
The same remark does apply to the <strong>prepared statement hash map</strong>, it is shared by <strong>all users</strong> using the same instance of <strong>Cassandra</strong> interpreter.
|
||||
<br/>
|
||||
Until <strong>Zeppelin</strong> offers a real multi-users separation, there is a work-around to segregate user environment and states: <em>create different Cassandra interpreter instances</em>
|
||||
<br/>
|
||||
<ol>
|
||||
<li>First go to the <strong>Interpreter</strong> menu and click on the <strong>Create</strong> button</li>
|
||||
<li>In the interpreter creation form, put <strong>cass-instance2</strong> as <strong>Name</strong> and select the <strong>cassandra</strong> in the interpreter drop-down list</li>
|
||||
<li>Click on <strong>Save</strong> to create the new interpreter instance. Now you should be able to see it in the interpreter list</li>
|
||||
<li>Go back to your notebook and click on the <strong>Gear</strong> icon to configure interpreter bindings. You should be able to see and select the <strong>cass-instance2</strong> interpreter instance in the available interpreter list instead of the standard <strong>cassandra</strong> instance</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab">
|
||||
<h4 class="panel-title">
|
||||
<a role="button" data-toggle="collapse" data-target="#${changelogId}" aria-expanded="false">
|
||||
<span class="text-info"><strong>Change Log</strong></span>
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="${changelogId}" class="panel-collapse collapse" role="tabpanel">
|
||||
<div class="panel-body">
|
||||
<strong>2.0</strong> :
|
||||
<br/>
|
||||
<ul>
|
||||
<li>Update help menu and add changelog</li>
|
||||
<li>Add Support for User Defined Functions, User Defined Aggregates and Materialized Views</li>
|
||||
<li>Upgrade Java driver version to <strong>3.0.0-rc1</strong></li>
|
||||
</ul>
|
||||
<strong>1.0</strong> :
|
||||
<br/>
|
||||
<ul>
|
||||
<li>Initial version</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab">
|
||||
<h4 class="panel-title">
|
||||
<a role="button" data-toggle="collapse" data-target="#${contactsId}" aria-expanded="false">
|
||||
<span class="text-info"><strong>Contact & Bugs</strong></span>
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="${contactsId}" class="panel-collapse collapse" role="tabpanel">
|
||||
<div class="panel-body">
|
||||
If you encounter a bug for this interpreter, please create a <a href="https://issues.apache.org/jira/browse/ZEPPELIN-382?jql=project%20%3D%20ZEPPELIN" target="_blank"><strong>JIRA</strong></a> ticket and ping me on Twitter at <a href="https://twitter.com/doanduyhai" target="_blank"><strong>@doanduyhai</strong></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -57,6 +57,36 @@
|
|||
</div>
|
||||
#end
|
||||
|
||||
#if (ksContent.views.nonEmpty)
|
||||
<!-- Materialized Views -->
|
||||
<table width="100%">
|
||||
<td><hr /></td>
|
||||
<td style="width:1px; padding: 0 10px; white-space: nowrap;"><strong class="text-primary">Materialized Views</strong></td>
|
||||
<td><hr /></td>
|
||||
</table>
|
||||
<div class="row">
|
||||
<div class="panel-group" role="tablist" aria-multiselectable="true">
|
||||
#for((id,name,viewHTML) <- ksContent.views)
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab">
|
||||
<h4 class="panel-title">
|
||||
<a role="button" data-toggle="collapse" data-target="#${id}" aria-expanded="false">
|
||||
<span class="text-primary"><i class="glyphicon glyphicon-eye-open"/> ${name}</span>
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="${id}" class="panel-collapse collapse" role="tabpanel">
|
||||
<div class="panel-body">
|
||||
${unescape(viewHTML)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
#end
|
||||
</div>
|
||||
</div>
|
||||
#end
|
||||
|
||||
#if (ksContent.udts.nonEmpty)
|
||||
<!-- UDTs -->
|
||||
<table width="100%">
|
||||
|
|
@ -88,4 +118,64 @@
|
|||
</div>
|
||||
#end
|
||||
|
||||
#if (ksContent.functions.nonEmpty)
|
||||
<!-- Functions -->
|
||||
<table width="100%">
|
||||
<td><hr /></td>
|
||||
<td style="width:1px; padding: 0 10px; white-space: nowrap;"><strong class="text-success">Functions</strong></td>
|
||||
<td><hr /></td>
|
||||
</table>
|
||||
<div class="row">
|
||||
<div class="panel-group" role="tablist" aria-multiselectable="true">
|
||||
#for((id,name,functionHTML) <- ksContent.functions)
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab">
|
||||
<h4 class="panel-title">
|
||||
<a role="button" data-toggle="collapse" data-target="#${id}" aria-expanded="false">
|
||||
<span class="text-success"><i class="glyphicon glyphicon-random"/> ${name}</span>
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="${id}" class="panel-collapse collapse" role="tabpanel">
|
||||
<div class="panel-body">
|
||||
${unescape(functionHTML)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
#end
|
||||
</div>
|
||||
</div>
|
||||
#end
|
||||
|
||||
#if (ksContent.aggregates.nonEmpty)
|
||||
<!-- Aggregates -->
|
||||
<table width="100%">
|
||||
<td><hr /></td>
|
||||
<td style="width:1px; padding: 0 10px; white-space: nowrap;"><strong class="text-success">Aggregates</strong></td>
|
||||
<td><hr /></td>
|
||||
</table>
|
||||
<div class="row">
|
||||
<div class="panel-group" role="tablist" aria-multiselectable="true">
|
||||
#for((id,name,aggregateHTML) <- ksContent.aggregates)
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab">
|
||||
<h4 class="panel-title">
|
||||
<a role="button" data-toggle="collapse" data-target="#${id}" aria-expanded="false">
|
||||
<span class="text-success"><i class="glyphicon glyphicon-retweet"/> ${name}</span>
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="${id}" class="panel-collapse collapse" role="tabpanel">
|
||||
<div class="panel-body">
|
||||
${unescape(aggregateHTML)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
#end
|
||||
</div>
|
||||
</div>
|
||||
#end
|
||||
|
||||
</div>
|
||||
|
|
@ -46,7 +46,7 @@
|
|||
<tbody>
|
||||
</table>
|
||||
<div class="panel-footer">
|
||||
<a data-toggle="collapse" data-target="#${ksDetails.uniqueId}_asCQL">
|
||||
<a data-toggle="collapse" data-target="#${ksDetails.uniqueId}_asCQL" class="text-danger">
|
||||
<strong>As CQL statement</strong>
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
|
|
|
|||
109
cassandra/src/main/resources/scalate/materializedViewDetails.ssp
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
<%--
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.
|
||||
*/
|
||||
--%>
|
||||
#import(org.apache.zeppelin.cassandra.MetaDataHierarchy._)
|
||||
<%@ val mvDetails: MaterializedViewDetails %>
|
||||
<%@ val withCaption: Boolean%>
|
||||
<div class="row">
|
||||
<div class="col-md-2"/>
|
||||
<div class="col-md-8 col-offset-md-2">
|
||||
<div class="panel panel-default table-responsive table-bordered">
|
||||
<table class="table">
|
||||
#if(withCaption)
|
||||
<caption>
|
||||
<h4 class="text-primary">
|
||||
<i class="glyphicon glyphicon-eye-open"/>
|
||||
${mvDetails.name}
|
||||
<i class="glyphicon glyphicon-arrow-right"/>
|
||||
<i class="glyphicon glyphicon-th-list"/>
|
||||
${mvDetails.baseTable}
|
||||
</h4>
|
||||
</caption>
|
||||
#end
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-md-4">Column Type</th>
|
||||
<th class="col-md-4">Column Name</th>
|
||||
<th class="col-md-4">Data Type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
#for (column <- mvDetails.columns)
|
||||
#match (column.columnType)
|
||||
#case(PartitionKey)
|
||||
|
||||
<tr class="info">
|
||||
<td class="col-md-4">
|
||||
<i class="glyphicon glyphicon-fullscreen" title="Partition Key"/>
|
||||
</td>
|
||||
<td class="col-md-4">${column.name}</td>
|
||||
<td class="col-md-4">${column.dataType}</td>
|
||||
</tr>
|
||||
#case(StaticColumn)
|
||||
<tr class="warning">
|
||||
<td class="col-md-4">
|
||||
<i class="glyphicon glyphicon-pushpin" title="Static Column"/>
|
||||
</td>
|
||||
<td class="col-md-4">${column.name}</td>
|
||||
<td class="col-md-4">${column.dataType}</td>
|
||||
</tr>
|
||||
#case(ClusteringColumn(ASC))
|
||||
<tr class="success">
|
||||
<td class="col-md-4">
|
||||
<i class="glyphicon glyphicon-sort" title="Clustering Column"/>
|
||||
|
||||
<i class="glyphicon glyphicon-sort-by-attributes" title="Sort ASC"/>
|
||||
</td>
|
||||
<td class="col-md-4">${column.name}</td>
|
||||
<td class="col-md-4">${column.dataType}</td>
|
||||
</tr>
|
||||
#case(ClusteringColumn(DESC))
|
||||
<tr class="success">
|
||||
<td class="col-md-4">
|
||||
<i class="glyphicon glyphicon-sort" title="Clustering Column"/>
|
||||
|
||||
<i class="glyphicon glyphicon-sort-by-attributes-alt" title="Sort DESC"/>
|
||||
</td>
|
||||
<td class="col-md-4">${column.name}</td>
|
||||
<td class="col-md-4">${column.dataType}</td>
|
||||
</tr>
|
||||
#otherwise
|
||||
<tr>
|
||||
<td class="col-md-4"></td>
|
||||
<td class="col-md-4">${column.name}</td>
|
||||
<td class="col-md-4">${column.dataType}</td>
|
||||
</tr>
|
||||
#end
|
||||
#end
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="panel-footer">
|
||||
<a data-toggle="collapse" data-target="#${mvDetails.uniqueId}_asCQL" class="text-primary">
|
||||
<strong>As CQL statement</strong>
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
<br/><br/>
|
||||
<div class="collapse" id="${mvDetails.uniqueId}_asCQL">
|
||||
<pre class="well">${mvDetails.asCQL}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2"></div>
|
||||
</div>
|
||||
|
|
@ -54,6 +54,23 @@
|
|||
<i class="glyphicon glyphicon-th-list text-primary" /> Table
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-eye-open text-primary" /> Materialized View
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-random text-success" /> Function
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-retweet text-success" /> Aggregate
|
||||
</a>
|
||||
</li>
|
||||
<li role="separator" class="divider text-muted"></li>
|
||||
<li class="dropdown-header"><span class="text-primary">Table icons</span></li>
|
||||
<li class="bg-info">
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-fullscreen" /> Partition Key
|
||||
|
|
@ -79,11 +96,6 @@
|
|||
<i class="glyphicon glyphicon-sort-by-attributes-alt" /> Clustering Order DESC
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-info-sign" /> Indexed Column
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
|
|
|
|||
|
|
@ -42,15 +42,6 @@
|
|||
<tr class="info">
|
||||
<td class="col-md-4">
|
||||
<i class="glyphicon glyphicon-fullscreen" title="Partition Key"/>
|
||||
#match (column.index)
|
||||
#case (Some(index))
|
||||
|
||||
<i class="glyphicon glyphicon-info-sign" title="Indexed Column"/>
|
||||
<em>${index.name}</em> <strong>${index.info}</strong>
|
||||
#case (None)
|
||||
<span></span>
|
||||
#end
|
||||
|
||||
</td>
|
||||
<td class="col-md-4">${column.name}</td>
|
||||
<td class="col-md-4">${column.dataType}</td>
|
||||
|
|
@ -59,15 +50,6 @@
|
|||
<tr class="warning">
|
||||
<td class="col-md-4">
|
||||
<i class="glyphicon glyphicon-pushpin" title="Static Column"/>
|
||||
#match (column.index)
|
||||
#case (Some(index))
|
||||
|
||||
<i class="glyphicon glyphicon-info-sign" title="Indexed Column"/>
|
||||
<em>${index.name}</em> <strong>${index.info}</strong>
|
||||
#case (None)
|
||||
<span></span>
|
||||
#end
|
||||
|
||||
</td>
|
||||
<td class="col-md-4">${column.name}</td>
|
||||
<td class="col-md-4">${column.dataType}</td>
|
||||
|
|
@ -78,15 +60,6 @@
|
|||
<i class="glyphicon glyphicon-sort" title="Clustering Column"/>
|
||||
|
||||
<i class="glyphicon glyphicon-sort-by-attributes" title="Sort ASC"/>
|
||||
#match (column.index)
|
||||
#case (Some(index))
|
||||
|
||||
<i class="glyphicon glyphicon-info-sign" title="Indexed Column"/>
|
||||
<em>${index.name}</em> <strong>${index.info}</strong>
|
||||
#case (None)
|
||||
<span></span>
|
||||
#end
|
||||
|
||||
</td>
|
||||
<td class="col-md-4">${column.name}</td>
|
||||
<td class="col-md-4">${column.dataType}</td>
|
||||
|
|
@ -97,32 +70,13 @@
|
|||
<i class="glyphicon glyphicon-sort" title="Clustering Column"/>
|
||||
|
||||
<i class="glyphicon glyphicon-sort-by-attributes-alt" title="Sort DESC"/>
|
||||
#match (column.index)
|
||||
#case (Some(index))
|
||||
|
||||
<i class="glyphicon glyphicon-info-sign" title="Indexed Column"/>
|
||||
<em>${index.name}</em> <strong>${index.info}</strong>
|
||||
#case (None)
|
||||
<span></span>
|
||||
#end
|
||||
|
||||
</td>
|
||||
<td class="col-md-4">${column.name}</td>
|
||||
<td class="col-md-4">${column.dataType}</td>
|
||||
</tr>
|
||||
#otherwise
|
||||
<tr>
|
||||
<td class="col-md-4">
|
||||
#match (column.index)
|
||||
#case (Some(index))
|
||||
|
||||
<i class="glyphicon glyphicon-info-sign" title="Indexed Column"/>
|
||||
<em>${index.name}</em> <strong>${index.info}</strong>
|
||||
#case (None)
|
||||
<span></span>
|
||||
#end
|
||||
|
||||
</td>
|
||||
<td class="col-md-4"></td>
|
||||
<td class="col-md-4">${column.name}</td>
|
||||
<td class="col-md-4">${column.dataType}</td>
|
||||
</tr>
|
||||
|
|
@ -132,7 +86,7 @@
|
|||
</tbody>
|
||||
</table>
|
||||
<div class="panel-footer">
|
||||
<a data-toggle="collapse" data-target="#${tableDetails.uniqueId}_asCQL">
|
||||
<a data-toggle="collapse" data-target="#${tableDetails.uniqueId}_asCQL" class="text-primary">
|
||||
<strong>As CQL statement</strong>
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
|
|
@ -142,6 +96,39 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
#if(tableDetails.indices.size > 0)
|
||||
<hr/>
|
||||
<div class="panel panel-default table-responsive table-bordered">
|
||||
<table class="table">
|
||||
<caption><h4 class="text-danger"><i class="glyphicon glyphicon-info-sign"/> ${tableDetails.tableName}'s indices</h4>
|
||||
</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-md-6">Name</th>
|
||||
<th class="col-md-6">Target</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
#for (index <- tableDetails.indices)
|
||||
<tr>
|
||||
<td class="col-md-6">${index.name}</td>
|
||||
<td class="col-md-6">${index.target}</td>
|
||||
</tr>
|
||||
#end
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="panel-footer">
|
||||
<a data-toggle="collapse" data-target="#${tableDetails.uniqueId}_indices_asCQL" class="text-danger">
|
||||
<strong>As CQL statement</strong>
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
<br/><br/>
|
||||
<div class="collapse" id="${tableDetails.uniqueId}_indices_asCQL">
|
||||
<pre class="well">${tableDetails.indicesAsCQL}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
#end
|
||||
</div>
|
||||
<div class="col-md-2"></div>
|
||||
</div>
|
||||
|
|
@ -46,7 +46,7 @@
|
|||
<tbody>
|
||||
</table>
|
||||
<div class="panel-footer">
|
||||
<a data-toggle="collapse" data-target="#${udtDetails.uniqueId}_asCQL">
|
||||
<a data-toggle="collapse" data-target="#${udtDetails.uniqueId}_asCQL" class="text-warning">
|
||||
<strong>As CQL statement</strong>
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.datastax.driver.core
|
||||
|
||||
case class TableMetadataWrapper(val meta: TableMetadata) {
|
||||
def exportTableOnlyAsString(): String = {
|
||||
meta.asCQLQuery(true)
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,6 @@ package org.apache.zeppelin.cassandra
|
|||
|
||||
import java.util.UUID
|
||||
|
||||
import com.datastax.driver.core.ColumnMetadata.IndexMetadata
|
||||
import com.datastax.driver.core.utils.UUIDs
|
||||
import org.apache.zeppelin.cassandra.MetaDataHierarchy._
|
||||
import org.fusesource.scalate.TemplateEngine
|
||||
|
|
@ -39,16 +38,30 @@ object DisplaySystem {
|
|||
|
||||
val CLUSTER_DETAILS_TEMPLATE = "scalate/clusterDetails.ssp"
|
||||
val KEYSPACE_DETAILS_TEMPLATE = "scalate/keyspaceDetails.ssp"
|
||||
|
||||
val TABLE_DETAILS_TEMPLATE = "scalate/tableDetails.ssp"
|
||||
val ALL_TABLES_TEMPLATE = "scalate/allTables.ssp"
|
||||
|
||||
val UDT_DETAILS_TEMPLATE = "scalate/udtDetails.ssp"
|
||||
val ALL_UDTS_TEMPLATE = "scalate/allUDTs.ssp"
|
||||
|
||||
val FUNCTION_DETAILS_TEMPLATE = "scalate/functionDetails.ssp"
|
||||
val ALL_FUNCTIONS_TEMPLATE = "scalate/allFunctions.ssp"
|
||||
|
||||
val AGGREGATE_DETAILS_TEMPLATE = "scalate/aggregateDetails.ssp"
|
||||
val ALL_AGGREGATES_TEMPLATE = "scalate/allAggregates.ssp"
|
||||
|
||||
val MATERIALIZED_VIEW_DETAILS_TEMPLATE = "scalate/materializedViewDetails.ssp"
|
||||
val ALL_MATERIALIZED_VIEWS_TEMPLATE = "scalate/allMaterializedViews.ssp"
|
||||
|
||||
val MENU_TEMPLATE = "scalate/menu.ssp"
|
||||
val CLUSTER_DROPDOWN_TEMPLATE = "scalate/dropDownMenuForCluster.ssp"
|
||||
val KEYSPACE_DROPDOWN_TEMPLATE = "scalate/dropDownMenuForKeyspace.ssp"
|
||||
|
||||
val KEYSPACE_DROPDOWN_TEMPLATE = "scalate/dropDownMenuForKeyspace.ssp"
|
||||
val CLUSTER_CONTENT_TEMPLATE = "scalate/clusterContent.ssp"
|
||||
val KEYSPACE_CONTENT_TEMPLATE = "scalate/keyspaceContent.ssp"
|
||||
val ALL_TABLES_TEMPLATE = "scalate/allTables.ssp"
|
||||
|
||||
|
||||
|
||||
object TableDisplay {
|
||||
|
||||
|
|
@ -58,10 +71,12 @@ object DisplaySystem {
|
|||
|
||||
protected[DisplaySystem] def formatWithoutMenu(meta: TableMetadata, withCaption: Boolean): String = {
|
||||
val tableName: String = meta.getName
|
||||
val columnsDetails = MetaDataConverter.tableMetaToColumnDetails(meta)
|
||||
val columnsDetails = MetaDataConverter.Table.tableMetaToColumnDetails(meta)
|
||||
val indicesDetails = MetaDataConverter.Table.tableMetaToIndexDetails(meta)
|
||||
val indicesAsCQL = indicesDetails.map(_.asCQL).mkString("\n")
|
||||
|
||||
engine.layout(TABLE_DETAILS_TEMPLATE,
|
||||
Map[String, Any]("tableDetails" -> TableDetails(tableName, columnsDetails, meta.exportAsString), "withCaption" -> withCaption))
|
||||
Map[String, Any]("tableDetails" -> TableDetails(tableName, columnsDetails, indicesDetails, TableMetadataWrapper(meta).exportTableOnlyAsString(), indicesAsCQL), "withCaption" -> withCaption))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -72,13 +87,49 @@ object DisplaySystem {
|
|||
|
||||
protected[DisplaySystem] def formatWithoutMenu(userType: UserType, withCaption: Boolean): String = {
|
||||
val udtName: String = userType.getTypeName
|
||||
val columnsDetails = MetaDataConverter.userTypeToColumnDetails(userType)
|
||||
val columnsDetails = MetaDataConverter.UDT.userTypeToColumnDetails(userType)
|
||||
|
||||
engine.layout(UDT_DETAILS_TEMPLATE,
|
||||
Map[String, Any]("udtDetails" -> UDTDetails(udtName, columnsDetails, userType.exportAsString), "withCaption" -> withCaption))
|
||||
Map[String, Any]("udtDetails" -> UDTDetails(udtName,columnsDetails,userType.exportAsString()), "withCaption" -> withCaption))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
object FunctionDisplay {
|
||||
def format(statement: String, functions: List[FunctionMetadata], withCaption: Boolean): String = {
|
||||
MenuDisplay.formatMenu(statement) ++ formatWithoutMenu(functions, withCaption)
|
||||
}
|
||||
|
||||
protected[DisplaySystem] def formatWithoutMenu(functions: List[FunctionMetadata], withCaption: Boolean): String = {
|
||||
val functionDetails: List[FunctionDetails] = functions.map(MetaDataConverter.functionMetaToFunctionDetails(_))
|
||||
engine.layout(FUNCTION_DETAILS_TEMPLATE,
|
||||
Map[String, Any]("sameNameFunctionDetails" -> SameNameFunctionDetails(functionDetails), "withCaption" -> withCaption))
|
||||
}
|
||||
}
|
||||
|
||||
object AggregateDisplay {
|
||||
def format(statement: String, aggregates: List[AggregateMetadata], withCaption: Boolean, codecRegistry: CodecRegistry): String = {
|
||||
MenuDisplay.formatMenu(statement) ++ formatWithoutMenu(aggregates, withCaption, codecRegistry)
|
||||
}
|
||||
|
||||
protected[DisplaySystem] def formatWithoutMenu(aggregates: List[AggregateMetadata], withCaption: Boolean, codecRegistry: CodecRegistry): String = {
|
||||
val aggDetails: List[AggregateDetails] = aggregates.map(agg => MetaDataConverter.aggregateMetaToAggregateDetails(codecRegistry, agg))
|
||||
engine.layout(AGGREGATE_DETAILS_TEMPLATE,
|
||||
Map[String, Any]("sameNameAggregateDetails" -> SameNameAggregateDetails(aggDetails), "withCaption" -> withCaption))
|
||||
}
|
||||
}
|
||||
|
||||
object MaterializedViewDisplay {
|
||||
def format(statement: String, mv: MaterializedViewMetadata, withCaption: Boolean): String = {
|
||||
MenuDisplay.formatMenu(statement) ++ formatWithoutMenu(mv, withCaption)
|
||||
}
|
||||
|
||||
protected[DisplaySystem] def formatWithoutMenu(mv: MaterializedViewMetadata, withCaption: Boolean): String = {
|
||||
val mvDetails = MetaDataConverter.mvMetaToMaterializedViewDetails(mv)
|
||||
engine.layout(MATERIALIZED_VIEW_DETAILS_TEMPLATE,
|
||||
Map[String, Any]("mvDetails" -> mvDetails, "withCaption" -> withCaption))
|
||||
}
|
||||
}
|
||||
|
||||
object KeyspaceDisplay {
|
||||
|
||||
private def formatCQLQuery(cql: String): String = {
|
||||
|
|
@ -97,19 +148,32 @@ object DisplaySystem {
|
|||
Map[String, Any]("ksDetails" -> ksDetails, "withCaption" -> withCaption))
|
||||
}
|
||||
|
||||
def formatKeyspaceContent(statement: String, meta: KeyspaceMetadata): String = {
|
||||
def formatKeyspaceContent(statement: String, meta: KeyspaceMetadata, codecRegistry: CodecRegistry): String = {
|
||||
val ksName: String = meta.getName
|
||||
val ksDetails = formatKeyspaceOnly(meta, false)
|
||||
val ksDetails = formatKeyspaceOnly(meta, true)
|
||||
|
||||
val tableDetails: List[(UUID, String, String)] = meta.getTables.asScala.toList
|
||||
.sortBy(meta => meta.getName)
|
||||
.map(meta => (UUIDs.timeBased(), meta.getName, TableDisplay.formatWithoutMenu(meta, false)))
|
||||
.sortBy(_.getName)
|
||||
.map(table => (UUIDs.timeBased(), table.getName, TableDisplay.formatWithoutMenu(table, false)))
|
||||
|
||||
val viewDetails: List[(UUID, String, String)] = meta.getMaterializedViews.asScala.toList
|
||||
.sortBy(_.getName)
|
||||
.map(view => (UUIDs.timeBased(), view.getName, MaterializedViewDisplay.formatWithoutMenu(view, false)))
|
||||
|
||||
val udtDetails: List[(UUID, String, String)] = meta.getUserTypes.asScala.toList
|
||||
.sortBy(udt => udt.getTypeName)
|
||||
.sortBy(_.getTypeName)
|
||||
.map(udt => (UUIDs.timeBased(), udt.getTypeName, UDTDisplay.formatWithoutMenu(udt, false)))
|
||||
|
||||
val ksContent: KeyspaceContent = KeyspaceContent(ksName, ksDetails, tableDetails, udtDetails)
|
||||
val functionDetails: List[(UUID, String, String)] = meta.getFunctions.asScala.toList
|
||||
.sortBy(_.getSimpleName)
|
||||
.map(function => (UUIDs.timeBased(), function.getSimpleName, FunctionDisplay.formatWithoutMenu(List(function), false)))
|
||||
|
||||
val aggregateDetails: List[(UUID, String, String)] = meta.getAggregates.asScala.toList
|
||||
.sortBy(_.getSimpleName)
|
||||
.map(agg => (UUIDs.timeBased(), agg.getSimpleName, AggregateDisplay.formatWithoutMenu(List(agg), false, codecRegistry)))
|
||||
|
||||
val ksContent: KeyspaceContent = KeyspaceContent(ksName, ksDetails, tableDetails, viewDetails,
|
||||
udtDetails, functionDetails, aggregateDetails)
|
||||
|
||||
MenuDisplay.formatMenuForKeyspace(statement, ksContent) +
|
||||
engine.layout(KEYSPACE_CONTENT_TEMPLATE,
|
||||
|
|
@ -144,26 +208,152 @@ object DisplaySystem {
|
|||
|
||||
def formatAllTables(statement: String, meta: Metadata): String = {
|
||||
val ksMetas: List[KeyspaceMetadata] = meta.getKeyspaces.asScala.toList
|
||||
.filter(_.getTables.size > 0)
|
||||
.sortBy(ks => ks.getName)
|
||||
if(ksMetas.isEmpty) {
|
||||
NoResultDisplay.formatNoResult
|
||||
} else {
|
||||
val allTables: Map[(UUID, String), List[String]] = ListMap.empty ++
|
||||
ksMetas
|
||||
.map(ks => {
|
||||
((UUIDs.timeBased(), ks.getName),
|
||||
ks.getTables.asScala.toList.map(table => table.getName).sortBy(name => name))
|
||||
})
|
||||
.sortBy{case ((id,name), _) => name}
|
||||
|
||||
|
||||
val keyspaceDetails: List[(UUID, String, String)] = allTables
|
||||
.keySet.toList.sortBy{case(id,ksName) => ksName}
|
||||
.map{case(id,ksName) => (id,ksName, "")}
|
||||
|
||||
val clusterContent: ClusterContent = ClusterContent(meta.getClusterName, "", keyspaceDetails)
|
||||
|
||||
MenuDisplay.formatMenuForCluster(statement, clusterContent) +
|
||||
engine.layout(ALL_TABLES_TEMPLATE,
|
||||
Map[String, Any]("allTables" -> allTables))
|
||||
}
|
||||
}
|
||||
|
||||
def formatAllUDTs(statement: String, meta: Metadata): String = {
|
||||
val ksMetas: List[KeyspaceMetadata] = meta.getKeyspaces.asScala.toList
|
||||
.filter(_.getUserTypes.size > 0)
|
||||
.sortBy(ks => ks.getName)
|
||||
|
||||
val allTables: Map[(UUID, String), List[String]] = ListMap.empty ++
|
||||
ksMetas
|
||||
.map(ks => {
|
||||
((UUIDs.timeBased(), ks.getName),
|
||||
ks.getTables.asScala.toList.map(table => table.getName).sortBy(name => name))
|
||||
})
|
||||
.sortBy{case ((id,name), _) => name}
|
||||
if(ksMetas.isEmpty) {
|
||||
NoResultDisplay.formatNoResult
|
||||
} else {
|
||||
val allUDTs: Map[(UUID, String), List[String]] = ListMap.empty ++
|
||||
ksMetas
|
||||
.map(ks => {
|
||||
((UUIDs.timeBased(), ks.getName),
|
||||
ks.getUserTypes.asScala.toList.map(udt => udt.getTypeName).sortBy(name => name))
|
||||
})
|
||||
.sortBy { case ((id, name), _) => name }
|
||||
|
||||
|
||||
val keyspaceDetails: List[(UUID, String, String)] = allTables
|
||||
.keySet.toList.sortBy{case(id,ksName) => ksName}
|
||||
.map{case(id,ksName) => (id,ksName, "")}
|
||||
val keyspaceDetails: List[(UUID, String, String)] = allUDTs
|
||||
.keySet.toList.sortBy { case (id, ksName) => ksName }
|
||||
.map { case (id, ksName) => (id, ksName, "") }
|
||||
|
||||
val clusterContent: ClusterContent = ClusterContent(meta.getClusterName, "", keyspaceDetails)
|
||||
val clusterContent: ClusterContent = ClusterContent(meta.getClusterName, "", keyspaceDetails)
|
||||
|
||||
MenuDisplay.formatMenuForCluster(statement, clusterContent) +
|
||||
engine.layout(ALL_TABLES_TEMPLATE,
|
||||
Map[String, Any]("allTables" -> allTables))
|
||||
MenuDisplay.formatMenuForCluster(statement, clusterContent) +
|
||||
engine.layout(ALL_UDTS_TEMPLATE,
|
||||
Map[String, Any]("allUDTs" -> allUDTs))
|
||||
}
|
||||
}
|
||||
|
||||
def formatAllFunctions(statement: String, meta: Metadata): String = {
|
||||
val ksMetas: List[KeyspaceMetadata] = meta.getKeyspaces.asScala.toList
|
||||
.filter(_.getFunctions.size > 0)
|
||||
.sortBy(ks => ks.getName)
|
||||
|
||||
if(ksMetas.isEmpty) {
|
||||
NoResultDisplay.formatNoResult
|
||||
} else {
|
||||
val allFunctions: Map[(UUID, String), List[FunctionSummary]] = ListMap.empty ++
|
||||
ksMetas
|
||||
.map(ks => {
|
||||
((UUIDs.timeBased(), ks.getName),
|
||||
ks.getFunctions.asScala.toList
|
||||
.map(MetaDataConverter.functionMetaToFunctionSummary(_))
|
||||
.sortBy(_.name))
|
||||
})
|
||||
.sortBy { case ((id, name), _) => name }
|
||||
|
||||
val keyspaceDetails: List[(UUID, String, String)] = allFunctions
|
||||
.keySet.toList.sortBy { case (id, ksName) => ksName }
|
||||
.map { case (id, ksName) => (id, ksName, "") }
|
||||
|
||||
|
||||
val clusterContent: ClusterContent = ClusterContent(meta.getClusterName, "", keyspaceDetails)
|
||||
|
||||
MenuDisplay.formatMenuForCluster(statement, clusterContent) +
|
||||
engine.layout(ALL_FUNCTIONS_TEMPLATE,
|
||||
Map[String, Any]("allFunctions" -> allFunctions))
|
||||
}
|
||||
}
|
||||
|
||||
def formatAllAggregates(statement: String, meta: Metadata): String = {
|
||||
val ksMetas: List[KeyspaceMetadata] = meta.getKeyspaces.asScala.toList
|
||||
.filter(_.getAggregates.size > 0)
|
||||
.sortBy(ks => ks.getName)
|
||||
|
||||
if(ksMetas.isEmpty) {
|
||||
NoResultDisplay.formatNoResult
|
||||
} else {
|
||||
val allAggregates: Map[(UUID, String), List[AggregateSummary]] = ListMap.empty ++
|
||||
ksMetas
|
||||
.map(ks => {
|
||||
((UUIDs.timeBased(), ks.getName),
|
||||
ks.getAggregates.asScala.toList
|
||||
.map(MetaDataConverter.aggregateMetaToAggregateSummary(_))
|
||||
.sortBy(_.name))
|
||||
})
|
||||
.sortBy { case ((id, name), _) => name }
|
||||
|
||||
val keyspaceDetails: List[(UUID, String, String)] = allAggregates
|
||||
.keySet.toList.sortBy { case (id, ksName) => ksName }
|
||||
.map { case (id, ksName) => (id, ksName, "") }
|
||||
|
||||
|
||||
val clusterContent: ClusterContent = ClusterContent(meta.getClusterName, "", keyspaceDetails)
|
||||
|
||||
MenuDisplay.formatMenuForCluster(statement, clusterContent) +
|
||||
engine.layout(ALL_AGGREGATES_TEMPLATE,
|
||||
Map[String, Any]("allAggregates" -> allAggregates))
|
||||
}
|
||||
}
|
||||
|
||||
def formatAllMaterializedViews(statement: String, meta: Metadata): String = {
|
||||
val ksMetas: List[KeyspaceMetadata] = meta.getKeyspaces.asScala.toList
|
||||
.filter(_.getMaterializedViews.size > 0)
|
||||
.sortBy(ks => ks.getName)
|
||||
|
||||
if(ksMetas.isEmpty) {
|
||||
NoResultDisplay.formatNoResult
|
||||
} else {
|
||||
val allMVs: Map[(UUID, String), List[MaterializedViewSummary]] = ListMap.empty ++
|
||||
ksMetas
|
||||
.map(ks => {
|
||||
((UUIDs.timeBased(), ks.getName),
|
||||
ks.getMaterializedViews.asScala.toList
|
||||
.map(MetaDataConverter.mvMetaToMaterializedViewSummary(_))
|
||||
.sortBy(_.name))
|
||||
})
|
||||
.sortBy { case ((id, name), _) => name }
|
||||
|
||||
val keyspaceDetails: List[(UUID, String, String)] = allMVs
|
||||
.keySet.toList.sortBy { case (id, ksName) => ksName }
|
||||
.map { case (id, ksName) => (id, ksName, "") }
|
||||
|
||||
|
||||
val clusterContent: ClusterContent = ClusterContent(meta.getClusterName, "", keyspaceDetails)
|
||||
|
||||
MenuDisplay.formatMenuForCluster(statement, clusterContent) +
|
||||
engine.layout(ALL_MATERIALIZED_VIEWS_TEMPLATE,
|
||||
Map[String, Any]("allMVs" -> allMVs))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -233,63 +423,150 @@ class ColumnMetaWrapper(val columnMeta: ColumnMetadata) {
|
|||
*/
|
||||
object MetaDataConverter {
|
||||
|
||||
def tableMetaToColumnDetails(meta: TableMetadata): List[ColumnDetails] = {
|
||||
val partitionKeys: List[ColumnMetaWrapper] = meta.getPartitionKey.asScala.toList.map(new ColumnMetaWrapper(_))
|
||||
val clusteringColumns: List[ColumnMetaWrapper] = meta.getClusteringColumns.asScala.toList.map(new ColumnMetaWrapper(_))
|
||||
val columns: List[ColumnMetaWrapper] = meta.getColumns.asScala.toList.map(new ColumnMetaWrapper(_))
|
||||
.diff(partitionKeys).diff(clusteringColumns)
|
||||
val clusteringOrders = meta.getClusteringOrder.asScala.toList
|
||||
type DriverClusteringOrder = com.datastax.driver.core.ClusteringOrder
|
||||
|
||||
convertPartitionKeys(partitionKeys):::
|
||||
extractStaticColumns(columns):::
|
||||
convertClusteringColumns(clusteringColumns, clusteringOrders):::
|
||||
extractNormalColumns(columns)
|
||||
}
|
||||
|
||||
def userTypeToColumnDetails(userType: UserType): List[ColumnDetails] = {
|
||||
userType.getFieldNames.asScala.toList
|
||||
.map(name => new ColumnDetails(name, NormalColumn, userType.getFieldType(name), None))
|
||||
def functionMetaToFunctionDetails(function: FunctionMetadata): FunctionDetails = {
|
||||
new FunctionDetails(function.getKeyspace.getName,
|
||||
function.getSimpleName,
|
||||
function.getArguments.asScala
|
||||
.toMap
|
||||
.map{case(paramName, dataType) => paramName + " " + dataType.asFunctionParameterString()}
|
||||
.toList,
|
||||
function.isCalledOnNullInput,
|
||||
function.getReturnType.asFunctionParameterString(),
|
||||
function.getLanguage,
|
||||
function.getBody,
|
||||
function.exportAsString())
|
||||
}
|
||||
|
||||
private def extractNormalColumns(columns: List[ColumnMetaWrapper]): List[ColumnDetails] = {
|
||||
columns
|
||||
.filter(_.columnMeta.isStatic == false)
|
||||
.map(c => new ColumnDetails(c.columnMeta.getName, NormalColumn, c.columnMeta.getType, extractIndexDetail(c)))
|
||||
def functionMetaToFunctionSummary(function: FunctionMetadata): FunctionSummary = {
|
||||
new FunctionSummary(function.getKeyspace.getName,
|
||||
function.getSimpleName,
|
||||
function.getArguments.asScala.toMap
|
||||
.mapValues(dataType => dataType.asFunctionParameterString())
|
||||
.values.toList,
|
||||
function.getReturnType.asFunctionParameterString()
|
||||
)
|
||||
}
|
||||
|
||||
private def extractIndexDetail(column: ColumnMetaWrapper): Option[IndexDetails] = {
|
||||
val indexOption = Option(column.columnMeta.getIndex)
|
||||
|
||||
def buildIndexInfo(indexMeta: IndexMetadata): String = {
|
||||
if(indexMeta.isKeys) "KEYS"
|
||||
if(indexMeta.isEntries) "ENTRIES"
|
||||
if(indexMeta.isFull) "FULL"
|
||||
if(indexMeta.isCustomIndex) s"Class = ${indexMeta.getIndexClassName}"
|
||||
else ""
|
||||
def aggregateMetaToAggregateDetails(codecRegistry: CodecRegistry, aggregate: AggregateMetadata): AggregateDetails = {
|
||||
val sFunc: FunctionSummary = functionMetaToFunctionSummary(aggregate.getStateFunc)
|
||||
val finalFunc: Option[String] = Option(aggregate.getFinalFunc).map(func => {
|
||||
val finalFunction = functionMetaToFunctionSummary(func)
|
||||
finalFunction.name + finalFunction.arguments.mkString("(", ", ", ")")
|
||||
})
|
||||
val sType = aggregate.getStateType
|
||||
val initCond: Option[String] = Option(aggregate.getInitCond).map(codecRegistry.codecFor(sType).format(_))
|
||||
val returnType: String = Option(aggregate.getFinalFunc) match {
|
||||
case Some(finalFunc) => functionMetaToFunctionSummary(finalFunc).returnType
|
||||
case None => sFunc.returnType
|
||||
}
|
||||
|
||||
indexOption.map(index => IndexDetails(index.getName, buildIndexInfo(index)))
|
||||
new AggregateDetails(aggregate.getKeyspace.getName,
|
||||
aggregate.getSimpleName,
|
||||
aggregate.getArgumentTypes.asScala.toList.map(_.asFunctionParameterString()),
|
||||
sFunc.name + sFunc.arguments.mkString("(",", ", ")"),
|
||||
sType.asFunctionParameterString(),
|
||||
finalFunc,
|
||||
initCond,
|
||||
returnType,
|
||||
aggregate.exportAsString())
|
||||
}
|
||||
|
||||
private def extractStaticColumns(columns: List[ColumnMetaWrapper]): List[ColumnDetails] = {
|
||||
columns
|
||||
.filter(_.columnMeta.isStatic == true)
|
||||
.map(c => new ColumnDetails(c.columnMeta.getName, StaticColumn, c.columnMeta.getType, extractIndexDetail(c)))
|
||||
def aggregateMetaToAggregateSummary(aggregate: AggregateMetadata): AggregateSummary = {
|
||||
val returnType: String = Option(aggregate.getFinalFunc) match {
|
||||
case Some(finalFunc) => functionMetaToFunctionSummary(finalFunc).returnType
|
||||
case None => aggregate.getStateType.asFunctionParameterString()
|
||||
}
|
||||
|
||||
new AggregateSummary(aggregate.getKeyspace.getName,
|
||||
aggregate.getSimpleName,
|
||||
aggregate.getArgumentTypes.asScala.toList.map(_.asFunctionParameterString()),
|
||||
returnType
|
||||
)
|
||||
}
|
||||
|
||||
private def convertClusteringColumns(columns: List[ColumnMetaWrapper], orders: List[TableMetadata.Order]): List[ColumnDetails] = {
|
||||
columns
|
||||
.zip(orders)
|
||||
.map{case(c,order) => new ColumnDetails(c.columnMeta.getName,
|
||||
new ClusteringColumn(OrderConverter.convert(order)),
|
||||
c.columnMeta.getType, extractIndexDetail(c))}
|
||||
def mvMetaToMaterializedViewDetails(mv: MaterializedViewMetadata): MaterializedViewDetails = {
|
||||
new MaterializedViewDetails(mv.getName, MV.mvMetaToColumnDetails(mv), mv.exportAsString(), mv.getBaseTable.getName)
|
||||
}
|
||||
|
||||
def mvMetaToMaterializedViewSummary(mv: MaterializedViewMetadata): MaterializedViewSummary = {
|
||||
new MaterializedViewSummary(mv.getName, mv.getBaseTable.getName)
|
||||
}
|
||||
|
||||
trait TableOrView {
|
||||
protected def extractNormalColumns(columns: List[ColumnMetaWrapper]): List[ColumnDetails] = {
|
||||
columns
|
||||
.filter(_.columnMeta.isStatic == false)
|
||||
.map(c => new ColumnDetails(c.columnMeta.getName, NormalColumn, c.columnMeta.getType))
|
||||
}
|
||||
|
||||
protected def extractStaticColumns(columns: List[ColumnMetaWrapper]): List[ColumnDetails] = {
|
||||
columns
|
||||
.filter(_.columnMeta.isStatic == true)
|
||||
.map(c => new ColumnDetails(c.columnMeta.getName, StaticColumn, c.columnMeta.getType))
|
||||
}
|
||||
|
||||
protected def convertClusteringColumns(columns: List[ColumnMetaWrapper], orders: List[DriverClusteringOrder]): List[ColumnDetails] = {
|
||||
columns
|
||||
.zip(orders)
|
||||
.map{case(c,order) => new ColumnDetails(c.columnMeta.getName,
|
||||
new ClusteringColumn(OrderConverter.convert(order)),c.columnMeta.getType)}
|
||||
|
||||
}
|
||||
|
||||
protected def convertPartitionKeys(columns: List[ColumnMetaWrapper]): List[ColumnDetails] = {
|
||||
columns
|
||||
.map(c => new ColumnDetails(c.columnMeta.getName, PartitionKey, c.columnMeta.getType))
|
||||
}
|
||||
}
|
||||
|
||||
object Table extends TableOrView {
|
||||
def tableMetaToColumnDetails(meta: TableMetadata): List[ColumnDetails] = {
|
||||
val partitionKeys: List[ColumnMetaWrapper] = meta.getPartitionKey.asScala.toList.map(new ColumnMetaWrapper(_))
|
||||
val clusteringColumns: List[ColumnMetaWrapper] = meta.getClusteringColumns.asScala.toList.map(new ColumnMetaWrapper(_))
|
||||
val columns: List[ColumnMetaWrapper] = meta.getColumns.asScala.toList.map(new ColumnMetaWrapper(_))
|
||||
.diff(partitionKeys).diff(clusteringColumns)
|
||||
val clusteringOrders = meta.getClusteringOrder.asScala.toList
|
||||
|
||||
convertPartitionKeys(partitionKeys):::
|
||||
extractStaticColumns(columns):::
|
||||
convertClusteringColumns(clusteringColumns, clusteringOrders):::
|
||||
extractNormalColumns(columns)
|
||||
}
|
||||
|
||||
def tableMetaToIndexDetails(meta: TableMetadata): List[IndexDetails] = {
|
||||
meta.getIndexes.asScala.toList
|
||||
.map(index => IndexDetails(index.getName, index.getTarget, index.asCQLQuery()))
|
||||
.sortBy(index => index.name)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private def convertPartitionKeys(columns: List[ColumnMetaWrapper]): List[ColumnDetails] = {
|
||||
columns
|
||||
.map(c => new ColumnDetails(c.columnMeta.getName, PartitionKey, c.columnMeta.getType, extractIndexDetail(c)))
|
||||
object MV extends TableOrView {
|
||||
def mvMetaToColumnDetails(meta: MaterializedViewMetadata): List[ColumnDetails] = {
|
||||
val partitionKeys: List[ColumnMetaWrapper] = meta.getPartitionKey.asScala.toList.map(new ColumnMetaWrapper(_))
|
||||
val clusteringColumns: List[ColumnMetaWrapper] = meta.getClusteringColumns.asScala.toList.map(new ColumnMetaWrapper(_))
|
||||
val columns: List[ColumnMetaWrapper] = meta.getColumns.asScala.toList.map(new ColumnMetaWrapper(_))
|
||||
.diff(partitionKeys).diff(clusteringColumns)
|
||||
val clusteringOrders = meta.getClusteringOrder.asScala.toList
|
||||
|
||||
convertPartitionKeys(partitionKeys):::
|
||||
convertClusteringColumns(clusteringColumns, clusteringOrders):::
|
||||
extractNormalColumns(columns)
|
||||
}
|
||||
}
|
||||
|
||||
object UDT {
|
||||
def userTypeToColumnDetails(userType: UserType): List[ColumnDetails] = {
|
||||
userType.getFieldNames.asScala.toList
|
||||
.map(name => new ColumnDetails(name, NormalColumn, userType.getFieldType(name)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import com.datastax.driver.core._
|
|||
import org.apache.zeppelin.cassandra.TextBlockHierarchy._
|
||||
import org.apache.zeppelin.interpreter.InterpreterException
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
/**
|
||||
* Enhance the Java driver session
|
||||
|
|
@ -32,6 +33,9 @@ class EnhancedSession(val session: Session) {
|
|||
val keyspaceDisplay = DisplaySystem.KeyspaceDisplay
|
||||
val tableDisplay = DisplaySystem.TableDisplay
|
||||
val udtDisplay = DisplaySystem.UDTDisplay
|
||||
val functionDisplay = DisplaySystem.FunctionDisplay
|
||||
val aggregateDisplay = DisplaySystem.AggregateDisplay
|
||||
val materializedViewDisplay = DisplaySystem.MaterializedViewDisplay
|
||||
val helpDisplay = DisplaySystem.HelpDisplay
|
||||
private val noResultDisplay = DisplaySystem.NoResultDisplay
|
||||
|
||||
|
|
@ -61,8 +65,12 @@ class EnhancedSession(val session: Session) {
|
|||
|
||||
private def execute(describeKeyspace: DescribeKeyspaceCmd): String = {
|
||||
val keyspace: String = describeKeyspace.keyspace
|
||||
val metadata: KeyspaceMetadata = session.getCluster.getMetadata.getKeyspace(keyspace)
|
||||
HTML_MAGIC + keyspaceDisplay.formatKeyspaceContent(describeKeyspace.statement, metadata)
|
||||
val metadata: Option[KeyspaceMetadata] = Option(session.getCluster.getMetadata.getKeyspace(keyspace))
|
||||
metadata match {
|
||||
case Some(ksMeta) => HTML_MAGIC + keyspaceDisplay.formatKeyspaceContent(describeKeyspace.statement, ksMeta,
|
||||
session.getCluster.getConfiguration.getCodecRegistry)
|
||||
case None => throw new InterpreterException(s"Cannot find keyspace $keyspace")
|
||||
}
|
||||
}
|
||||
|
||||
private def execute(describeTable: DescribeTableCmd): String = {
|
||||
|
|
@ -76,7 +84,7 @@ class EnhancedSession(val session: Session) {
|
|||
}
|
||||
}
|
||||
|
||||
private def execute(describeUDT: DescribeUDTCmd): String = {
|
||||
private def execute(describeUDT: DescribeTypeCmd): String = {
|
||||
val metaData = session.getCluster.getMetadata
|
||||
val keyspace: String = describeUDT.keyspace.orElse(Option(session.getLoggedKeyspace)).getOrElse("system")
|
||||
val udtName: String = describeUDT.udtName
|
||||
|
|
@ -87,6 +95,87 @@ class EnhancedSession(val session: Session) {
|
|||
}
|
||||
}
|
||||
|
||||
private def execute(describeUDTs: DescribeTypesCmd): String = {
|
||||
val metadata: Metadata = session.getCluster.getMetadata
|
||||
HTML_MAGIC + clusterDisplay.formatAllUDTs(describeUDTs.statement, metadata)
|
||||
}
|
||||
|
||||
private def execute(describeFunction: DescribeFunctionCmd): String = {
|
||||
val metaData = session.getCluster.getMetadata
|
||||
val keyspaceName: String = describeFunction.keyspace.orElse(Option(session.getLoggedKeyspace)).getOrElse("system")
|
||||
val functionName: String = describeFunction.function;
|
||||
|
||||
Option(metaData.getKeyspace(keyspaceName)) match {
|
||||
case Some(keyspace) => {
|
||||
val functionMetas: List[FunctionMetadata] = keyspace.getFunctions.asScala.toList
|
||||
.filter(func => func.getSimpleName.toLowerCase == functionName.toLowerCase)
|
||||
|
||||
if(functionMetas.isEmpty) {
|
||||
throw new InterpreterException(s"Cannot find function ${keyspaceName}.$functionName")
|
||||
} else {
|
||||
HTML_MAGIC + functionDisplay.format(describeFunction.statement, functionMetas, true)
|
||||
}
|
||||
}
|
||||
case None => throw new InterpreterException(s"Cannot find function ${keyspaceName}.$functionName")
|
||||
}
|
||||
}
|
||||
|
||||
private def execute(describeFunctions: DescribeFunctionsCmd): String = {
|
||||
val metadata: Metadata = session.getCluster.getMetadata
|
||||
HTML_MAGIC + clusterDisplay.formatAllFunctions(describeFunctions.statement, metadata)
|
||||
}
|
||||
|
||||
private def execute(describeAggregate: DescribeAggregateCmd): String = {
|
||||
val metaData = session.getCluster.getMetadata
|
||||
val keyspaceName: String = describeAggregate.keyspace.orElse(Option(session.getLoggedKeyspace)).getOrElse("system")
|
||||
val aggregateName: String = describeAggregate.aggregate;
|
||||
|
||||
Option(metaData.getKeyspace(keyspaceName)) match {
|
||||
case Some(keyspace) => {
|
||||
val aggMetas: List[AggregateMetadata] = keyspace.getAggregates.asScala.toList
|
||||
.filter(agg => agg.getSimpleName.toLowerCase == aggregateName.toLowerCase)
|
||||
|
||||
if(aggMetas.isEmpty) {
|
||||
throw new InterpreterException(s"Cannot find aggregate ${keyspaceName}.$aggregateName")
|
||||
} else {
|
||||
HTML_MAGIC + aggregateDisplay.format(describeAggregate.statement, aggMetas, true,
|
||||
session
|
||||
.getCluster
|
||||
.getConfiguration
|
||||
.getCodecRegistry)
|
||||
}
|
||||
}
|
||||
case None => throw new InterpreterException(s"Cannot find aggregate ${keyspaceName}.$aggregateName")
|
||||
}
|
||||
}
|
||||
|
||||
private def execute(describeAggregates: DescribeAggregatesCmd): String = {
|
||||
val metadata: Metadata = session.getCluster.getMetadata
|
||||
HTML_MAGIC + clusterDisplay.formatAllAggregates(describeAggregates.statement, metadata)
|
||||
}
|
||||
|
||||
private def execute(describeMV: DescribeMaterializedViewCmd): String = {
|
||||
val metaData = session.getCluster.getMetadata
|
||||
val keyspaceName: String = describeMV.keyspace.orElse(Option(session.getLoggedKeyspace)).getOrElse("system")
|
||||
val viewName: String = describeMV.view
|
||||
|
||||
Option(metaData.getKeyspace(keyspaceName)) match {
|
||||
case Some(keyspace) => {
|
||||
val viewMeta: Option[MaterializedViewMetadata] = Option(keyspace.getMaterializedView(viewName))
|
||||
viewMeta match {
|
||||
case Some(vMeta) => HTML_MAGIC + materializedViewDisplay.format(describeMV.statement, vMeta, true)
|
||||
case None => throw new InterpreterException(s"Cannot find materialized view ${keyspaceName}.$viewName")
|
||||
}
|
||||
}
|
||||
case None => throw new InterpreterException(s"Cannot find materialized view ${keyspaceName}.$viewName")
|
||||
}
|
||||
}
|
||||
|
||||
private def execute(describeMVs: DescribeMaterializedViewsCmd): String = {
|
||||
val metadata: Metadata = session.getCluster.getMetadata
|
||||
HTML_MAGIC + clusterDisplay.formatAllMaterializedViews(describeMVs.statement, metadata)
|
||||
}
|
||||
|
||||
private def execute(helpCmd: HelpCmd): String = {
|
||||
HTML_MAGIC + helpDisplay.formatHelp()
|
||||
}
|
||||
|
|
@ -95,11 +184,18 @@ class EnhancedSession(val session: Session) {
|
|||
def execute(st: Any): Any = {
|
||||
st match {
|
||||
case x:DescribeClusterCmd => execute(x)
|
||||
case x:DescribeKeyspacesCmd => execute(x)
|
||||
case x:DescribeTablesCmd => execute(x)
|
||||
case x:DescribeKeyspaceCmd => execute(x)
|
||||
case x:DescribeKeyspacesCmd => execute(x)
|
||||
case x:DescribeTableCmd => execute(x)
|
||||
case x:DescribeUDTCmd => execute(x)
|
||||
case x:DescribeTablesCmd => execute(x)
|
||||
case x:DescribeTypeCmd => execute(x)
|
||||
case x:DescribeTypesCmd => execute(x)
|
||||
case x:DescribeFunctionCmd => execute(x)
|
||||
case x:DescribeFunctionsCmd => execute(x)
|
||||
case x:DescribeAggregateCmd => execute(x)
|
||||
case x:DescribeAggregatesCmd => execute(x)
|
||||
case x:DescribeMaterializedViewCmd => execute(x)
|
||||
case x:DescribeMaterializedViewsCmd => execute(x)
|
||||
case x:HelpCmd => execute(x)
|
||||
case x:Statement => session.execute(x)
|
||||
case _ => throw new InterpreterException(s"Cannot execute statement '$st' of type ${st.getClass}")
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ class InterpreterLogic(val session: Session) {
|
|||
logger.info(s"Executing CQL statements : \n\n$stringStatements\n")
|
||||
|
||||
try {
|
||||
val protocolVersion = session.getCluster.getConfiguration.getProtocolOptions.getProtocolVersionEnum
|
||||
val protocolVersion = session.getCluster.getConfiguration.getProtocolOptions.getProtocolVersion
|
||||
|
||||
val queries:List[AnyBlock] = parseInput(stringStatements)
|
||||
|
||||
|
|
@ -137,12 +137,12 @@ class InterpreterLogic(val session: Session) {
|
|||
case x:BatchStm => {
|
||||
val builtStatements: List[Statement] = x.statements.map {
|
||||
case st:SimpleStm => generateSimpleStatement(st, queryOptions, context)
|
||||
case st:BoundStm => generateBoundStatement(st, queryOptions, context)
|
||||
case st:BoundStm => generateBoundStatement(session, st, queryOptions, context)
|
||||
case _ => throw new InterpreterException(s"Unknown statement type")
|
||||
}
|
||||
generateBatchStatement(x.batchType, queryOptions, builtStatements)
|
||||
}
|
||||
case x:BoundStm => generateBoundStatement(x, queryOptions, context)
|
||||
case x:BoundStm => generateBoundStatement(session, x, queryOptions, context)
|
||||
case x:DescribeCommandStatement => x
|
||||
case x:HelpCmd => x
|
||||
case x => throw new InterpreterException(s"Unknown statement type : ${x}")
|
||||
|
|
@ -208,7 +208,7 @@ class InterpreterLogic(val session: Session) {
|
|||
row => {
|
||||
val data = columnsDefinitions.map {
|
||||
case (name, dataType) => {
|
||||
if (row.isNull(name)) null else dataType.deserialize(row.getBytesUnsafe(name), protocolVersion)
|
||||
if (row.isNull(name)) null else row.getObject(name)
|
||||
}
|
||||
}
|
||||
output.append(data.mkString("\t")).append("\n")
|
||||
|
|
@ -283,12 +283,12 @@ class InterpreterLogic(val session: Session) {
|
|||
statement
|
||||
}
|
||||
|
||||
def generateBoundStatement(st: BoundStm, options: CassandraQueryOptions,context: InterpreterContext): BoundStatement = {
|
||||
def generateBoundStatement(session: Session, st: BoundStm, options: CassandraQueryOptions,context: InterpreterContext): BoundStatement = {
|
||||
logger.debug(s"Generating bound statement with name : '${st.name}' and bound values : ${st.values}")
|
||||
preparedStatements.get(st.name) match {
|
||||
case Some(ps) => {
|
||||
val boundValues = maybeExtractVariables(st.values, context)
|
||||
createBoundStatement(st.name, ps, boundValues)
|
||||
createBoundStatement(session.getCluster.getConfiguration.getCodecRegistry, st.name, ps, boundValues)
|
||||
}
|
||||
case None => throw new InterpreterException(s"The statement '${st.name}' can not be bound to values. " +
|
||||
s"Are you sure you did prepare it with @prepare[${st.name}] ?")
|
||||
|
|
@ -342,7 +342,7 @@ class InterpreterLogic(val session: Session) {
|
|||
options.fetchSize.foreach(statement.setFetchSize(_))
|
||||
}
|
||||
|
||||
private def createBoundStatement(name: String, ps: PreparedStatement, rawBoundValues: String): BoundStatement = {
|
||||
private def createBoundStatement(codecRegistry: CodecRegistry, name: String, ps: PreparedStatement, rawBoundValues: String): BoundStatement = {
|
||||
val dataTypes = ps.getVariables.toList
|
||||
.map(cfDef => cfDef.getType)
|
||||
|
||||
|
|
@ -357,6 +357,7 @@ class InterpreterLogic(val session: Session) {
|
|||
if(value.trim == "null") {
|
||||
null
|
||||
} else {
|
||||
val codec: TypeCodec[AnyRef] = codecRegistry.codecFor[AnyRef](dataType)
|
||||
dataType.getName match {
|
||||
case (ASCII | TEXT | VARCHAR) => value.trim.replaceAll("(?<!')'","")
|
||||
case (INT | VARINT) => value.trim.toInt
|
||||
|
|
@ -369,11 +370,11 @@ class InterpreterLogic(val session: Session) {
|
|||
case INET => InetAddress.getByName(value.trim)
|
||||
case TIMESTAMP => parseDate(value.trim)
|
||||
case (UUID | TIMEUUID) => java.util.UUID.fromString(value.trim)
|
||||
case LIST => dataType.parse(boundValuesParser.parse(boundValuesParser.list, value).get)
|
||||
case SET => dataType.parse(boundValuesParser.parse(boundValuesParser.set, value).get)
|
||||
case MAP => dataType.parse(boundValuesParser.parse(boundValuesParser.map, value).get)
|
||||
case UDT => dataType.parse(boundValuesParser.parse(boundValuesParser.udt, value).get)
|
||||
case TUPLE => dataType.parse(boundValuesParser.parse(boundValuesParser.tuple, value).get)
|
||||
case LIST => codec.parse(boundValuesParser.parse(boundValuesParser.list, value).get)
|
||||
case SET => codec.parse(boundValuesParser.parse(boundValuesParser.set, value).get)
|
||||
case MAP => codec.parse(boundValuesParser.parse(boundValuesParser.map, value).get)
|
||||
case UDT => codec.parse(boundValuesParser.parse(boundValuesParser.udt, value).get)
|
||||
case TUPLE => codec.parse(boundValuesParser.parse(boundValuesParser.tuple, value).get)
|
||||
case _ => throw new InterpreterException(s"Cannot parse data of type : ${dataType.toString}")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,10 +28,10 @@ import scala.util.parsing.json.JSONObject
|
|||
*/
|
||||
object MetaDataHierarchy {
|
||||
object OrderConverter {
|
||||
def convert(clusteringOrder: TableMetadata.Order): ClusteringOrder = {
|
||||
def convert(clusteringOrder: com.datastax.driver.core.ClusteringOrder): ClusteringOrder = {
|
||||
clusteringOrder match {
|
||||
case TableMetadata.Order.ASC => ASC
|
||||
case TableMetadata.Order.DESC => DESC
|
||||
case com.datastax.driver.core.ClusteringOrder.ASC => ASC
|
||||
case com.datastax.driver.core.ClusteringOrder.DESC => DESC
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -46,9 +46,8 @@ object MetaDataHierarchy {
|
|||
case class ClusteringColumn(order: ClusteringOrder) extends ColumnType
|
||||
object StaticColumn extends ColumnType
|
||||
object NormalColumn extends ColumnType
|
||||
case class IndexDetails(name: String, info: String)
|
||||
case class ColumnDetails(name: String, columnType: ColumnType, dataType: DataType, index: Option[IndexDetails])
|
||||
|
||||
case class IndexDetails(name: String, target: String, asCQL: String)
|
||||
case class ColumnDetails(name: String, columnType: ColumnType, dataType: DataType)
|
||||
|
||||
case class ClusterDetails(name: String, partitioner: String)
|
||||
case class ClusterContent(clusterName: String, clusterDetails: String, keyspaces: List[(UUID, String, String)])
|
||||
|
|
@ -58,10 +57,26 @@ object MetaDataHierarchy {
|
|||
JSONObject(replication).toString().replaceAll(""""""","'")
|
||||
}
|
||||
}
|
||||
case class KeyspaceContent(keyspaceName: String, keyspaceDetails: String, tables: List[(UUID,String, String)], udts: List[(UUID, String, String)])
|
||||
case class TableDetails(tableName: String, columns: List[ColumnDetails], asCQL: String, uniqueId: UUID = UUIDs.timeBased())
|
||||
case class KeyspaceContent(keyspaceName: String, keyspaceDetails: String,
|
||||
tables: List[(UUID,String, String)],
|
||||
views: List[(UUID,String, String)],
|
||||
udts: List[(UUID, String, String)],
|
||||
functions: List[(UUID, String, String)],
|
||||
aggregates: List[(UUID, String, String)])
|
||||
case class TableDetails(tableName: String, columns: List[ColumnDetails], indices: List[IndexDetails], asCQL: String, indicesAsCQL: String, uniqueId: UUID = UUIDs.timeBased())
|
||||
case class UDTDetails(typeName: String, columns: List[ColumnDetails], asCQL: String, uniqueId: UUID = UUIDs.timeBased())
|
||||
|
||||
case class SameNameFunctionDetails(functions: List[FunctionDetails])
|
||||
case class FunctionDetails(keyspace:String, name: String, arguments: List[String], calledOnNullInput: Boolean, returnType: String,
|
||||
language:String, body: String, asCQL: String, uniqueId: UUID = UUIDs.timeBased())
|
||||
case class FunctionSummary(keyspace:String, name: String, arguments: List[String], returnType: String)
|
||||
|
||||
case class AggregateDetails(keyspace:String, name: String, arguments: List[String], sFunc: String, sType: String,
|
||||
finalFunc: Option[String], initCond: Option[String], returnType: String,
|
||||
asCQL: String, uniqueId: UUID = UUIDs.timeBased())
|
||||
case class AggregateSummary(keyspace:String, name: String, arguments: List[String], returnType: String)
|
||||
case class SameNameAggregateDetails(aggregates: List[AggregateDetails])
|
||||
|
||||
case class MaterializedViewDetails(name: String, columns: List[ColumnDetails], asCQL: String, baseTable: String, uniqueId: UUID = UUIDs.timeBased())
|
||||
case class MaterializedViewSummary(name: String, baseTable: String)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,10 +19,20 @@ package org.apache.zeppelin.cassandra
|
|||
import com.datastax.driver.core._
|
||||
import org.apache.zeppelin.cassandra.CassandraInterpreter._
|
||||
import org.apache.zeppelin.interpreter.InterpreterException
|
||||
import scala.util.matching.Regex
|
||||
import scala.util.parsing.combinator._
|
||||
import org.apache.zeppelin.cassandra.TextBlockHierarchy._
|
||||
|
||||
/**
|
||||
* Parser using Scala combinator parsing
|
||||
*
|
||||
* (?i) means case-insensitive mode
|
||||
* (?s) means DOT ALL mode
|
||||
* (?is) means case-insensitive and DOT ALL mode
|
||||
*
|
||||
*/
|
||||
object ParagraphParser {
|
||||
|
||||
val CONSISTENCY_LEVEL_PATTERN = ConsistencyLevel.values().toList
|
||||
.map(_.name()).filter(!_.contains("SERIAL")).mkString("""^\s*@consistency\s*=\s*(""", "|" , """)\s*$""").r
|
||||
|
||||
|
|
@ -42,22 +52,43 @@ object ParagraphParser {
|
|||
val BIND_PATTERN = """^\s*@bind\[([^]]+)\](?:=([^;]+))?""".r
|
||||
val BATCH_PATTERN = """^(?i)\s*BEGIN\s+(UNLOGGED|COUNTER)?\s*BATCH""".r
|
||||
|
||||
/**
|
||||
* Very complicated RegExp
|
||||
* (?: OR REPLACE)? -> optional presence of OR REPLACE
|
||||
* .+? -> match ANY character in RELUCTANT mode
|
||||
* (?:\s*|\n|\r|\f) -> white space OR line returns (\n, \r, \f)
|
||||
* (?:\s*|\n|\r|\f)AS(?:\s*|\n|\r|\f) -> AS preceded and followed by white space or line return
|
||||
* (?:'|\$\$) -> simple quote (') OR double dollar ($$) as source code separator
|
||||
* (?:'|\$\$).+?(?:'|\$\$)\s*; ->
|
||||
* source code separator (?:'|\$\$)
|
||||
* followed by ANY character in RELUCTANT mode (.+?)
|
||||
* followed by source code separator (?:'|\$\$)
|
||||
* followed by optional white-space(s) (\s*)
|
||||
* followed by semi-colon (;)
|
||||
*/
|
||||
val UDF_PATTERN = """(?is)\s*(CREATE(?:\s+OR REPLACE)?\s+FUNCTION(?:\s+IF\s+NOT\s+EXISTS)?.+?(?:\s+|\n|\r|\f)AS(?:\s+|\n|\r|\f)(?:'|\$\$).+?(?:'|\$\$)\s*;)""".r
|
||||
|
||||
val GENERIC_STATEMENT_PREFIX =
|
||||
"""(?is)\s*(?:INSERT|UPDATE|DELETE|SELECT|CREATE|UPDATE|
|
||||
|DROP|GRANT|REVOKE|TRUNCATE|LIST|USE)\s+""".r
|
||||
|
||||
val VALID_IDENTIFIER = "[a-z][a-z0-9_]*"
|
||||
|
||||
val DESCRIBE_CLUSTER_PATTERN = """^(?i)\s*(?:DESCRIBE|DESC)\s+CLUSTER;\s*$""".r
|
||||
val DESCRIBE_KEYSPACES_PATTERN = """^(?i)\s*(?:DESCRIBE|DESC)\s+KEYSPACES;\s*$""".r
|
||||
val DESCRIBE_TABLES_PATTERN = """^(?i)\s*(?:DESCRIBE|DESC)\s+TABLES;\s*$""".r
|
||||
val DESCRIBE_CLUSTER_PATTERN = """^(?i)\s*(?:DESCRIBE|DESC)\s+CLUSTER\s*;\s*$""".r
|
||||
|
||||
|
||||
val DESCRIBE_KEYSPACE_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+KEYSPACE\s*("""+VALID_IDENTIFIER+""");\s*$""").r
|
||||
val DESCRIBE_KEYSPACES_PATTERN = """^(?i)\s*(?:DESCRIBE|DESC)\s+KEYSPACES\s*;\s*$""".r
|
||||
|
||||
|
||||
val DESCRIBE_TABLE_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+TABLE\s*("""+VALID_IDENTIFIER+""");\s*$""").r
|
||||
val DESCRIBE_TABLE_WITH_KEYSPACE_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+TABLE\s*(""" +
|
||||
VALID_IDENTIFIER +
|
||||
""")\.(""" +
|
||||
VALID_IDENTIFIER +
|
||||
""");\s*$""").r
|
||||
val DESCRIBE_TABLES_PATTERN = """^(?i)\s*(?:DESCRIBE|DESC)\s+TABLES\s*;\s*$""".r
|
||||
|
||||
|
||||
val DESCRIBE_TYPE_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+TYPE\s*("""+VALID_IDENTIFIER+""");\s*$""").r
|
||||
val DESCRIBE_TYPE_WITH_KEYSPACE_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+TYPE\s*(""" +
|
||||
|
|
@ -65,6 +96,35 @@ object ParagraphParser {
|
|||
""")\.(""" +
|
||||
VALID_IDENTIFIER +
|
||||
""");\s*$""").r
|
||||
val DESCRIBE_TYPES_PATTERN = """^(?i)\s*(?:DESCRIBE|DESC)\s+TYPES\s*;\s*$""".r
|
||||
|
||||
|
||||
val DESCRIBE_FUNCTION_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+FUNCTION\s*("""+VALID_IDENTIFIER+""");\s*$""").r
|
||||
val DESCRIBE_FUNCTION_WITH_KEYSPACE_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+FUNCTION\s*(""" +
|
||||
VALID_IDENTIFIER +
|
||||
""")\.(""" +
|
||||
VALID_IDENTIFIER +
|
||||
""");\s*$""").r
|
||||
val DESCRIBE_FUNCTIONS_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+FUNCTIONS\s*;\s*$""").r
|
||||
|
||||
|
||||
val DESCRIBE_AGGREGATE_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+AGGREGATE\s*("""+VALID_IDENTIFIER+""");\s*$""").r
|
||||
val DESCRIBE_AGGREGATE_WITH_KEYSPACE_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+AGGREGATE\s*(""" +
|
||||
VALID_IDENTIFIER +
|
||||
""")\.(""" +
|
||||
VALID_IDENTIFIER +
|
||||
""");\s*$""").r
|
||||
val DESCRIBE_AGGREGATES_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+AGGREGATES\s*;\s*$""").r
|
||||
|
||||
|
||||
val DESCRIBE_MATERIALIZED_VIEW_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+MATERIALIZED\s+VIEW\s*("""+VALID_IDENTIFIER+""");\s*$""").r
|
||||
val DESCRIBE_MATERIALIZED_VIEW_WITH_KEYSPACE_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+MATERIALIZED\s+VIEW\s*(""" +
|
||||
VALID_IDENTIFIER +
|
||||
""")\.(""" +
|
||||
VALID_IDENTIFIER +
|
||||
""");\s*$""").r
|
||||
val DESCRIBE_MATERIALIZED_VIEWS_PATTERN = ("""^(?i)\s*(?:DESCRIBE|DESC)\s+MATERIALIZED\s+VIEWS\s*;\s*$""").r
|
||||
|
||||
|
||||
val HELP_PATTERN = """^(?i)\s*HELP;\s*$""".r
|
||||
}
|
||||
|
|
@ -74,7 +134,10 @@ class ParagraphParser extends RegexParsers{
|
|||
|
||||
import ParagraphParser._
|
||||
|
||||
def singleLineComment: Parser[Comment] = """\s*#.*""".r ^^ {case text => Comment(text.trim.replaceAll("#",""))}
|
||||
def singleLineCommentHash: Parser[Comment] = """\s*#.*""".r ^^ {case text => Comment(text.trim.replaceAll("#",""))}
|
||||
def singleLineCommentDoubleSlashes: Parser[Comment] = """\s*//.*""".r ^^ {case text => Comment(text.trim.replaceAll("//",""))}
|
||||
def singleLineComment: Parser[Comment] = singleLineCommentHash | singleLineCommentDoubleSlashes
|
||||
|
||||
def multiLineComment: Parser[Comment] = """(?s)/\*(.*)\*/""".r ^^ {case text => Comment(text.trim.replaceAll("""/\*""","").replaceAll("""\*/""",""))}
|
||||
|
||||
//Query parameters
|
||||
|
|
@ -85,7 +148,10 @@ class ParagraphParser extends RegexParsers{
|
|||
def fetchSize: Parser[FetchSize] = """\s*@fetchSize.+""".r ^^ {case x => extractFetchSize(x.trim)}
|
||||
|
||||
//Statements
|
||||
def createFunctionStatement: Parser[SimpleStm] = UDF_PATTERN ^^{case x => extractUdfStatement(x.trim)}
|
||||
def genericStatement: Parser[SimpleStm] = s"""$GENERIC_STATEMENT_PREFIX[^;]+;""".r ^^ {case x => extractSimpleStatement(x.trim)}
|
||||
// def allStatement: Parser[SimpleStm] = udfStatement | genericStatement
|
||||
|
||||
def prepare: Parser[PrepareStm] = """\s*@prepare.+""".r ^^ {case x => extractPreparedStatement(x.trim)}
|
||||
def removePrepare: Parser[RemovePrepareStm] = """\s*@remove_prepare.+""".r ^^ {case x => extractRemovePreparedStatement(x.trim)}
|
||||
def bind: Parser[BoundStm] = """\s*@bind.+""".r ^^ {case x => extractBoundStatement(x.trim)}
|
||||
|
|
@ -95,9 +161,17 @@ class ParagraphParser extends RegexParsers{
|
|||
private def describeCluster: Parser[DescribeClusterCmd] = """(?i)\s*(?:DESCRIBE|DESC)\s+CLUSTER.*""".r ^^ {extractDescribeClusterCmd(_)}
|
||||
private def describeKeyspaces: Parser[DescribeKeyspacesCmd] = """(?i)\s*(?:DESCRIBE|DESC)\s+KEYSPACES.*""".r ^^ {extractDescribeKeyspacesCmd(_)}
|
||||
private def describeTables: Parser[DescribeTablesCmd] = """(?i)\s*(?:DESCRIBE|DESC)\s+TABLES.*""".r ^^ {extractDescribeTablesCmd(_)}
|
||||
private def describeTypes: Parser[DescribeTypesCmd] = """(?i)\s*(?:DESCRIBE|DESC)\s+TYPES.*""".r ^^ {extractDescribeTypesCmd(_)}
|
||||
private def describeFunctions: Parser[DescribeFunctionsCmd] = """(?i)\s*(?:DESCRIBE|DESC)\s+FUNCTIONS.*""".r ^^ {extractDescribeFunctionsCmd(_)}
|
||||
private def describeAggregates: Parser[DescribeAggregatesCmd] = """(?i)\s*(?:DESCRIBE|DESC)\s+AGGREGATES.*""".r ^^ {extractDescribeAggregatesCmd(_)}
|
||||
private def describeMaterializedViews: Parser[DescribeMaterializedViewsCmd] = """(?i)\s*(?:DESCRIBE|DESC)\s+MATERIALIZED\s+VIEWS.*""".r ^^ {extractDescribeMaterializedViewsCmd(_)}
|
||||
private def describeKeyspace: Parser[DescribeKeyspaceCmd] = """\s*(?i)(?:DESCRIBE|DESC)\s+KEYSPACE\s+.+""".r ^^ {extractDescribeKeyspaceCmd(_)}
|
||||
private def describeTable: Parser[DescribeTableCmd] = """(?i)\s*(?:DESCRIBE|DESC)\s+TABLE\s+.+""".r ^^ {extractDescribeTableCmd(_)}
|
||||
private def describeType: Parser[DescribeUDTCmd] = """(?i)\s*(?:DESCRIBE|DESC)\s+TYPE\s+.*""".r ^^ {extractDescribeTypeCmd(_)}
|
||||
private def describeType: Parser[DescribeTypeCmd] = """(?i)\s*(?:DESCRIBE|DESC)\s+TYPE\s+.*""".r ^^ {extractDescribeTypeCmd(_)}
|
||||
private def describeFunction: Parser[DescribeFunctionCmd] = """(?i)\s*(?:DESCRIBE|DESC)\s+FUNCTION\s+.*""".r ^^ {extractDescribeFunctionCmd(_)}
|
||||
private def describeAggregate: Parser[DescribeAggregateCmd] = """(?i)\s*(?:DESCRIBE|DESC)\s+AGGREGATE\s+.*""".r ^^ {extractDescribeAggregateCmd(_)}
|
||||
private def describeMaterializedView: Parser[DescribeMaterializedViewCmd] = """(?i)\s*(?:DESCRIBE|DESC)\s+MATERIALIZED\s+VIEW\s+.*""".r ^^ {extractDescribeMaterializedViewCmd(_)}
|
||||
|
||||
|
||||
//Help
|
||||
private def helpCommand: Parser[HelpCmd] = """(?i)\s*HELP.*""".r ^^{extractHelpCmd(_)}
|
||||
|
|
@ -114,8 +188,14 @@ class ParagraphParser extends RegexParsers{
|
|||
case begin ~ cqls ~ end => BatchStm(extractBatchType(begin),cqls)}
|
||||
|
||||
def queries:Parser[List[AnyBlock]] = rep(singleLineComment | multiLineComment | consistency | serialConsistency |
|
||||
timestamp | retryPolicy | fetchSize | removePrepare | prepare | bind | batch | describeCluster | describeKeyspaces |
|
||||
describeTables | describeKeyspace | describeTable | describeType | helpCommand | genericStatement)
|
||||
timestamp | retryPolicy | fetchSize | removePrepare | prepare | bind | batch | describeCluster |
|
||||
describeKeyspace | describeKeyspaces |
|
||||
describeTable | describeTables |
|
||||
describeType | describeTypes |
|
||||
describeFunction | describeFunctions |
|
||||
describeAggregate | describeAggregates |
|
||||
describeMaterializedView | describeMaterializedViews |
|
||||
helpCommand | createFunctionStatement | genericStatement)
|
||||
|
||||
def extractConsistency(text: String): Consistency = {
|
||||
text match {
|
||||
|
|
@ -171,6 +251,13 @@ class ParagraphParser extends RegexParsers{
|
|||
}
|
||||
}
|
||||
|
||||
def extractUdfStatement(text: String): SimpleStm = {
|
||||
text match {
|
||||
case UDF_PATTERN(statement) => SimpleStm(statement)
|
||||
case _ => throw new InterpreterException(s"Invalid statement '$text' for UDF creation. Did you forget to add ; (semi-colon) at the end of each CQL statement ?")
|
||||
}
|
||||
}
|
||||
|
||||
def extractPreparedStatement(text: String): PrepareStm = {
|
||||
text match {
|
||||
case PREPARE_STATEMENT_PATTERN(name,queryString) => PrepareStm(name.trim,queryString.trim)
|
||||
|
|
@ -214,6 +301,14 @@ class ParagraphParser extends RegexParsers{
|
|||
}
|
||||
}
|
||||
|
||||
def extractDescribeKeyspaceCmd(text: String): DescribeKeyspaceCmd = {
|
||||
text match {
|
||||
case DESCRIBE_KEYSPACE_PATTERN(keyspace) => new DescribeKeyspaceCmd(keyspace)
|
||||
case _ => throw new InterpreterException(s"Invalid syntax for DESCRIBE KEYSPACE. " +
|
||||
s"""It should comply to the pattern: ${DESCRIBE_KEYSPACE_PATTERN.toString}""")
|
||||
}
|
||||
}
|
||||
|
||||
def extractDescribeKeyspacesCmd(text: String): DescribeKeyspacesCmd = {
|
||||
text match {
|
||||
case DESCRIBE_KEYSPACES_PATTERN() => new DescribeKeyspacesCmd
|
||||
|
|
@ -222,6 +317,15 @@ class ParagraphParser extends RegexParsers{
|
|||
}
|
||||
}
|
||||
|
||||
def extractDescribeTableCmd(text: String): DescribeTableCmd = {
|
||||
text match {
|
||||
case DESCRIBE_TABLE_WITH_KEYSPACE_PATTERN(keyspace,table) => new DescribeTableCmd(Option(keyspace),table)
|
||||
case DESCRIBE_TABLE_PATTERN(table) => new DescribeTableCmd(Option.empty,table)
|
||||
case _ => throw new InterpreterException(s"Invalid syntax for DESCRIBE TABLE. " +
|
||||
s"""It should comply to the patterns: ${DESCRIBE_TABLE_WITH_KEYSPACE_PATTERN.toString} or ${DESCRIBE_TABLE_PATTERN.toString}""".stripMargin)
|
||||
}
|
||||
}
|
||||
|
||||
def extractDescribeTablesCmd(text: String): DescribeTablesCmd = {
|
||||
text match {
|
||||
case DESCRIBE_TABLES_PATTERN() => new DescribeTablesCmd
|
||||
|
|
@ -230,32 +334,74 @@ class ParagraphParser extends RegexParsers{
|
|||
}
|
||||
}
|
||||
|
||||
def extractDescribeKeyspaceCmd(text: String): DescribeKeyspaceCmd = {
|
||||
def extractDescribeTypeCmd(text: String): DescribeTypeCmd = {
|
||||
text match {
|
||||
case DESCRIBE_KEYSPACE_PATTERN(keyspace) => new DescribeKeyspaceCmd(keyspace)
|
||||
case _ => throw new InterpreterException(s"Invalid syntax for DESCRIBE KEYSPACE. " +
|
||||
s"""It should comply to the pattern: ${DESCRIBE_KEYSPACE_PATTERN.toString}""")
|
||||
}
|
||||
}
|
||||
|
||||
def extractDescribeTableCmd(text: String): DescribeTableCmd = {
|
||||
text match {
|
||||
case DESCRIBE_TABLE_WITH_KEYSPACE_PATTERN(keyspace,table) => new DescribeTableCmd(Option(keyspace),table)
|
||||
case DESCRIBE_TABLE_PATTERN(table) => new DescribeTableCmd(Option.empty,table)
|
||||
case _ => throw new InterpreterException(s"Invalid syntax for DESCRIBE TABLE. " +
|
||||
s"""It should comply to the patterns: ${DESCRIBE_TABLE_WITH_KEYSPACE_PATTERN.toString} or ${DESCRIBE_TABLE_PATTERN.toString}""".stripMargin)
|
||||
}
|
||||
}
|
||||
|
||||
def extractDescribeTypeCmd(text: String): DescribeUDTCmd = {
|
||||
text match {
|
||||
case DESCRIBE_TYPE_WITH_KEYSPACE_PATTERN(keyspace,table) => new DescribeUDTCmd(Option(keyspace),table)
|
||||
case DESCRIBE_TYPE_PATTERN(table) => new DescribeUDTCmd(Option.empty,table)
|
||||
case DESCRIBE_TYPE_WITH_KEYSPACE_PATTERN(keyspace,table) => new DescribeTypeCmd(Option(keyspace),table)
|
||||
case DESCRIBE_TYPE_PATTERN(table) => new DescribeTypeCmd(Option.empty,table)
|
||||
case _ => throw new InterpreterException(s"Invalid syntax for DESCRIBE TYPE. " +
|
||||
s"""It should comply to the patterns: ${DESCRIBE_TYPE_WITH_KEYSPACE_PATTERN.toString} or ${DESCRIBE_TYPE_PATTERN.toString}""".stripMargin)
|
||||
}
|
||||
}
|
||||
|
||||
def extractDescribeTypesCmd(text: String): DescribeTypesCmd = {
|
||||
text match {
|
||||
case DESCRIBE_TYPES_PATTERN() => new DescribeTypesCmd
|
||||
case _ => throw new InterpreterException(s"Invalid syntax for DESCRIBE TYPES. " +
|
||||
s"""It should comply to the pattern: ${DESCRIBE_TYPES_PATTERN.toString}""")
|
||||
}
|
||||
}
|
||||
|
||||
def extractDescribeFunctionCmd(text: String): DescribeFunctionCmd = {
|
||||
text match {
|
||||
case DESCRIBE_FUNCTION_WITH_KEYSPACE_PATTERN(keyspace,function) => new DescribeFunctionCmd(Option(keyspace),function)
|
||||
case DESCRIBE_FUNCTION_PATTERN(function) => new DescribeFunctionCmd(Option.empty,function)
|
||||
case _ => throw new InterpreterException(s"Invalid syntax for DESCRIBE FUNCTION. " +
|
||||
s"""It should comply to the patterns: ${DESCRIBE_FUNCTION_WITH_KEYSPACE_PATTERN.toString} or ${DESCRIBE_FUNCTION_PATTERN.toString}""".stripMargin)
|
||||
}
|
||||
}
|
||||
|
||||
def extractDescribeFunctionsCmd(text: String): DescribeFunctionsCmd = {
|
||||
text match {
|
||||
case DESCRIBE_FUNCTIONS_PATTERN() => new DescribeFunctionsCmd
|
||||
case _ => throw new InterpreterException(s"Invalid syntax for DESCRIBE FUNCTIONS. " +
|
||||
s"""It should comply to the pattern: ${DESCRIBE_FUNCTIONS_PATTERN.toString}""".stripMargin)
|
||||
}
|
||||
}
|
||||
|
||||
def extractDescribeAggregateCmd(text: String): DescribeAggregateCmd = {
|
||||
text match {
|
||||
case DESCRIBE_AGGREGATE_WITH_KEYSPACE_PATTERN(keyspace,aggregate) => new DescribeAggregateCmd(Option(keyspace),aggregate)
|
||||
case DESCRIBE_AGGREGATE_PATTERN(aggregate) => new DescribeAggregateCmd(Option.empty,aggregate)
|
||||
case _ => throw new InterpreterException(s"Invalid syntax for DESCRIBE AGGREGATE. " +
|
||||
s"""It should comply to the patterns: ${DESCRIBE_AGGREGATE_WITH_KEYSPACE_PATTERN.toString} or ${DESCRIBE_AGGREGATE_PATTERN.toString}""".stripMargin)
|
||||
}
|
||||
}
|
||||
|
||||
def extractDescribeAggregatesCmd(text: String): DescribeAggregatesCmd = {
|
||||
text match {
|
||||
case DESCRIBE_AGGREGATES_PATTERN() => new DescribeAggregatesCmd
|
||||
case _ => throw new InterpreterException(s"Invalid syntax for DESCRIBE AGGREGATES. " +
|
||||
s"""It should comply to the pattern: ${DESCRIBE_AGGREGATES_PATTERN.toString}""".stripMargin)
|
||||
}
|
||||
}
|
||||
|
||||
def extractDescribeMaterializedViewCmd(text: String): DescribeMaterializedViewCmd = {
|
||||
text match {
|
||||
case DESCRIBE_MATERIALIZED_VIEW_WITH_KEYSPACE_PATTERN(keyspace,view) => new DescribeMaterializedViewCmd(Option(keyspace),view)
|
||||
case DESCRIBE_MATERIALIZED_VIEW_PATTERN(view) => new DescribeMaterializedViewCmd(Option.empty,view)
|
||||
case _ => throw new InterpreterException(s"Invalid syntax for DESCRIBE MATERIALIZED VIEW. " +
|
||||
s"""It should comply to the patterns: ${DESCRIBE_MATERIALIZED_VIEW_WITH_KEYSPACE_PATTERN.toString} or ${DESCRIBE_MATERIALIZED_VIEW_PATTERN.toString}""".stripMargin)
|
||||
}
|
||||
}
|
||||
|
||||
def extractDescribeMaterializedViewsCmd(text: String): DescribeMaterializedViewsCmd = {
|
||||
text match {
|
||||
case DESCRIBE_MATERIALIZED_VIEWS_PATTERN() => new DescribeMaterializedViewsCmd
|
||||
case _ => throw new InterpreterException(s"Invalid syntax for DESCRIBE MATERIALIZED VIEWS. " +
|
||||
s"""It should comply to the pattern: ${DESCRIBE_MATERIALIZED_VIEWS_PATTERN.toString}""".stripMargin)
|
||||
}
|
||||
}
|
||||
|
||||
def extractHelpCmd(text: String): HelpCmd = {
|
||||
text match {
|
||||
case HELP_PATTERN() => new HelpCmd
|
||||
|
|
|
|||
|
|
@ -77,10 +77,16 @@ object TextBlockHierarchy {
|
|||
object BatchStatementType extends StatementType
|
||||
object DescribeClusterStatementType extends StatementType
|
||||
object DescribeAllKeyspacesStatementType extends StatementType
|
||||
object DescribeKeyspaceStatementType extends StatementType
|
||||
object DescribeAllTablesStatementType extends StatementType
|
||||
object DescribeAllTypesStatementType extends StatementType
|
||||
object DescribeAllFunctionsStatementType extends StatementType
|
||||
object DescribeAllAggregatesStatementType extends StatementType
|
||||
object DescribeKeyspaceStatementType extends StatementType
|
||||
object DescribeTableStatementType extends StatementType
|
||||
object DescribeTypeStatementType extends StatementType
|
||||
object DescribeFunctionStatementType extends StatementType
|
||||
object DescribeAggregateStatementType extends StatementType
|
||||
object DescribeMaterializedView extends StatementType
|
||||
object HelpStatementType extends StatementType
|
||||
|
||||
abstract class QueryStatement(val statementType: StatementType) extends AnyBlock(StatementBlock) {
|
||||
|
|
@ -104,15 +110,27 @@ object TextBlockHierarchy {
|
|||
val statement: String
|
||||
}
|
||||
|
||||
class DescribeClusterCmd(override val statement: String = "DESCRIBE CLUSTER;")
|
||||
case class DescribeClusterCmd(override val statement: String = "DESCRIBE CLUSTER;")
|
||||
extends QueryStatement(DescribeClusterStatementType) with DescribeCommandStatement
|
||||
|
||||
class DescribeKeyspacesCmd(override val statement: String = "DESCRIBE KEYSPACES;")
|
||||
case class DescribeKeyspacesCmd(override val statement: String = "DESCRIBE KEYSPACES;")
|
||||
extends QueryStatement(DescribeAllKeyspacesStatementType) with DescribeCommandStatement
|
||||
|
||||
class DescribeTablesCmd(override val statement: String = "DESCRIBE TABLES;")
|
||||
case class DescribeTablesCmd(override val statement: String = "DESCRIBE TABLES;")
|
||||
extends QueryStatement(DescribeAllTablesStatementType) with DescribeCommandStatement
|
||||
|
||||
case class DescribeTypesCmd(override val statement: String = "DESCRIBE TYPES;")
|
||||
extends QueryStatement(DescribeAllTypesStatementType) with DescribeCommandStatement
|
||||
|
||||
case class DescribeFunctionsCmd(override val statement: String = "DESCRIBE FUNCTIONS;") extends QueryStatement(DescribeAllFunctionsStatementType)
|
||||
with DescribeCommandStatement
|
||||
|
||||
case class DescribeAggregatesCmd(override val statement: String = "DESCRIBE AGGREGATES;") extends QueryStatement(DescribeAllAggregatesStatementType)
|
||||
with DescribeCommandStatement
|
||||
|
||||
case class DescribeMaterializedViewsCmd(override val statement: String = "DESCRIBE MATERIALIZED VIEWS;") extends QueryStatement(DescribeAllAggregatesStatementType)
|
||||
with DescribeCommandStatement
|
||||
|
||||
case class DescribeKeyspaceCmd(keyspace: String) extends QueryStatement(DescribeKeyspaceStatementType)
|
||||
with DescribeCommandStatement {
|
||||
override val statement: String = s"DESCRIBE KEYSPACE $keyspace;"
|
||||
|
|
@ -126,7 +144,7 @@ object TextBlockHierarchy {
|
|||
}
|
||||
}
|
||||
|
||||
case class DescribeUDTCmd(keyspace:Option[String],udtName: String) extends QueryStatement(DescribeTypeStatementType)
|
||||
case class DescribeTypeCmd(keyspace:Option[String], udtName: String) extends QueryStatement(DescribeTypeStatementType)
|
||||
with DescribeCommandStatement {
|
||||
override val statement: String = keyspace match {
|
||||
case Some(ks) => s"DESCRIBE TYPE $ks.$udtName;"
|
||||
|
|
@ -134,6 +152,30 @@ object TextBlockHierarchy {
|
|||
}
|
||||
}
|
||||
|
||||
class HelpCmd extends QueryStatement(HelpStatementType)
|
||||
case class DescribeFunctionCmd(keyspace:Option[String], function: String) extends QueryStatement(DescribeFunctionStatementType)
|
||||
with DescribeCommandStatement {
|
||||
override val statement: String = keyspace match {
|
||||
case Some(ks) => s"DESCRIBE FUNCTION $ks.$function;"
|
||||
case None => s"DESCRIBE FUNCTION $function;"
|
||||
}
|
||||
}
|
||||
|
||||
case class DescribeAggregateCmd(keyspace:Option[String], aggregate: String) extends QueryStatement(DescribeAggregateStatementType)
|
||||
with DescribeCommandStatement {
|
||||
override val statement: String = keyspace match {
|
||||
case Some(ks) => s"DESCRIBE AGGREGATE $ks.$aggregate;"
|
||||
case None => s"DESCRIBE AGGREGATE $aggregate;"
|
||||
}
|
||||
}
|
||||
|
||||
case class DescribeMaterializedViewCmd(keyspace:Option[String], view: String) extends QueryStatement(DescribeMaterializedView)
|
||||
with DescribeCommandStatement {
|
||||
override val statement: String = keyspace match {
|
||||
case Some(ks) => s"DESCRIBE MATERIALIZED VIEW $ks.$view;"
|
||||
case None => s"DESCRIBE MATERIALIZED VIEW $view;"
|
||||
}
|
||||
}
|
||||
|
||||
case class HelpCmd(val statement:String = "HELP;") extends QueryStatement(HelpStatementType)
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,22 +30,20 @@ import static org.assertj.core.api.Assertions.*;
|
|||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.datastax.driver.core.Cluster;
|
||||
import com.datastax.driver.core.ProtocolVersion;
|
||||
import com.datastax.driver.core.Session;
|
||||
import info.archinnov.achilles.junit.AchillesResource;
|
||||
import info.archinnov.achilles.junit.AchillesResourceBuilder;
|
||||
|
||||
import info.archinnov.achilles.embedded.CassandraEmbeddedServerBuilder;
|
||||
|
||||
import org.apache.zeppelin.interpreter.Interpreter;
|
||||
import org.apache.zeppelin.interpreter.InterpreterContext;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult.Code;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import scala.io.Source;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
|
|
@ -58,16 +56,14 @@ public class CassandraInterpreterTest {
|
|||
|
||||
private static final String ARTISTS_TABLE = "zeppelin.artists";
|
||||
|
||||
@ClassRule
|
||||
public static AchillesResource resource = AchillesResourceBuilder
|
||||
public static Session session = CassandraEmbeddedServerBuilder
|
||||
.noEntityPackages()
|
||||
.withKeyspaceName("zeppelin")
|
||||
.withScript("prepare_schema.cql")
|
||||
.withScript("prepare_data.cql")
|
||||
.build();
|
||||
|
||||
private static Session session = resource.getNativeSession();
|
||||
|
||||
.withProtocolVersion(ProtocolVersion.V3)
|
||||
.buildNativeSessionOnly();
|
||||
// public static Session session = null;
|
||||
private static CassandraInterpreter interpreter;
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
|
|
@ -76,7 +72,8 @@ public class CassandraInterpreterTest {
|
|||
@BeforeClass
|
||||
public static void setUp() {
|
||||
Properties properties = new Properties();
|
||||
final Cluster cluster = resource.getNativeSession().getCluster();
|
||||
final Cluster cluster = session.getCluster();
|
||||
// final Cluster cluster = null;
|
||||
properties.setProperty(CASSANDRA_CLUSTER_NAME, cluster.getClusterName());
|
||||
properties.setProperty(CASSANDRA_COMPRESSION_PROTOCOL, "NONE");
|
||||
properties.setProperty(CASSANDRA_CREDENTIALS_USERNAME, "none");
|
||||
|
|
@ -131,7 +128,7 @@ public class CassandraInterpreterTest {
|
|||
@Test
|
||||
public void should_create_cluster_and_session_upon_call_to_open() throws Exception {
|
||||
assertThat(interpreter.cluster).isNotNull();
|
||||
assertThat(interpreter.cluster.getClusterName()).isEqualTo(resource.getNativeSession().getCluster().getClusterName());
|
||||
assertThat(interpreter.cluster.getClusterName()).isEqualTo(session.getCluster().getClusterName());
|
||||
assertThat(interpreter.session).isNotNull();
|
||||
assertThat(interpreter.helper).isNotNull();
|
||||
}
|
||||
|
|
@ -241,7 +238,7 @@ public class CassandraInterpreterTest {
|
|||
//Then
|
||||
assertThat(actual.code()).isEqualTo(Code.ERROR);
|
||||
assertThat(actual.message())
|
||||
.contains("Not enough replica available for query at consistency THREE (3 required but only 1 alive)");
|
||||
.contains("Not enough replicas available for query at consistency THREE (3 required but only 1 alive)");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -329,14 +326,14 @@ public class CassandraInterpreterTest {
|
|||
assertThat(actual.message()).isEqualTo(
|
||||
"login\taddresses\tage\tdeceased\tfirstname\tlast_update\tlastname\tlocation\n" +
|
||||
"jdoe\t" +
|
||||
"{street_number:3, street_name:'Beverly Hills Bld', zip_code:90209," +
|
||||
" country:'USA', extra_info:['Right on the hills', 'Next to the post box'], " +
|
||||
"phone_numbers:{'office':2015790847, 'home':2016778524}}\tnull\t" +
|
||||
"{street_number:3,street_name:'Beverly Hills Bld',zip_code:90209," +
|
||||
"country:'USA',extra_info:['Right on the hills','Next to the post box']," +
|
||||
"phone_numbers:{'office':2015790847,'home':2016778524}}\tnull\t" +
|
||||
"null\t" +
|
||||
"John\t" +
|
||||
"null\t" +
|
||||
"DOE\t" +
|
||||
"('USA', 90209, 'Beverly Hills')\n");
|
||||
"('USA',90209,'Beverly Hills')\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -553,6 +550,76 @@ public class CassandraInterpreterTest {
|
|||
assertThat(reformatHtml(actual.message())).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
//TODO activate test when using Java 8 and C* 3.x
|
||||
public void should_describe_function() throws Exception {
|
||||
//Given
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty(CASSANDRA_HOSTS, "127.0.0.1");
|
||||
properties.setProperty(CASSANDRA_PORT, "9042");
|
||||
Interpreter interpreter = new CassandraInterpreter(properties);
|
||||
interpreter.open();
|
||||
|
||||
String createFunction = "CREATE FUNCTION zeppelin.maxof(val1 int,val2 int) " +
|
||||
"RETURNS NULL ON NULL INPUT " +
|
||||
"RETURNS int " +
|
||||
"LANGUAGE java " +
|
||||
"AS $$" +
|
||||
" return Math.max(val1, val2);\n" +
|
||||
"$$;";
|
||||
interpreter.interpret(createFunction, intrContext);
|
||||
String query = "DESCRIBE FUNCTION zeppelin.maxOf;";
|
||||
|
||||
//When
|
||||
final InterpreterResult actual = interpreter.interpret(query, intrContext);
|
||||
|
||||
//Then
|
||||
assertThat(actual.code()).isEqualTo(Code.SUCCESS);
|
||||
assertThat(actual.message()).isEqualTo("xxxxx");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
//TODO activate test when using Java 8 and C* 3.x
|
||||
public void should_describe_aggregate() throws Exception {
|
||||
//Given
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty(CASSANDRA_HOSTS, "127.0.0.1");
|
||||
properties.setProperty(CASSANDRA_PORT, "9042");
|
||||
Interpreter interpreter = new CassandraInterpreter(properties);
|
||||
interpreter.open();
|
||||
|
||||
final String query = "DESCRIBE AGGREGATES;";
|
||||
|
||||
//When
|
||||
final InterpreterResult actual = interpreter.interpret(query, intrContext);
|
||||
|
||||
//Then
|
||||
assertThat(actual.code()).isEqualTo(Code.SUCCESS);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
//TODO activate test when using Java 8 and C* 3.x
|
||||
public void should_describe_materialized_view() throws Exception {
|
||||
//Given
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty(CASSANDRA_HOSTS, "127.0.0.1");
|
||||
properties.setProperty(CASSANDRA_PORT, "9042");
|
||||
Interpreter interpreter = new CassandraInterpreter(properties);
|
||||
interpreter.open();
|
||||
|
||||
final String query = "DESCRIBE MATERIALIZED VIEWS;";
|
||||
|
||||
//When
|
||||
final InterpreterResult actual = interpreter.interpret(query, intrContext);
|
||||
|
||||
//Then
|
||||
assertThat(actual.code()).isEqualTo(Code.SUCCESS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_describe_table() throws Exception {
|
||||
//Given
|
||||
|
|
@ -601,6 +668,7 @@ public class CassandraInterpreterTest {
|
|||
|
||||
assertThat(reformatHtml(actual.message())).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_error_describing_non_existing_table() throws Exception {
|
||||
//Given
|
||||
|
|
@ -647,8 +715,8 @@ public class CassandraInterpreterTest {
|
|||
return rawHtml
|
||||
.replaceAll("\\s*\n\\s*","")
|
||||
.replaceAll(">\\s+<", "><")
|
||||
.replaceAll("(?s)data-target=\"#[a-f0-9-]+(?:_asCQL)?\"", "")
|
||||
.replaceAll("(?s)id=\"[a-f0-9-]+(?:_asCQL)?\"", "")
|
||||
.replaceAll("(?s)data-target=\"#[a-f0-9-]+(?:_asCQL|_indices_asCQL)?\"", "")
|
||||
.replaceAll("(?s)id=\"[a-f0-9-]+(?:_asCQL|_indices_asCQL)?\"", "")
|
||||
.trim();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -302,10 +302,10 @@ public class InterpreterLogicTest {
|
|||
}
|
||||
|
||||
private <A> scala.collection.immutable.List<A> toScalaList(java.util.List<A> list) {
|
||||
return scala.collection.JavaConverters.asScalaBufferConverter(list).asScala().toList();
|
||||
return scala.collection.JavaConversions.asScalaIterable(list).toList();
|
||||
}
|
||||
|
||||
private <A> java.util.List<A> toJavaList(scala.collection.immutable.List<A> list){
|
||||
return scala.collection.JavaConverters.seqAsJavaListConverter(list).asJava();
|
||||
return scala.collection.JavaConversions.asJavaList(list);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,98 +1 @@
|
|||
<br/>
|
||||
<br/>
|
||||
<nav class="navbar navbar-default">
|
||||
<ul class="nav navbar-nav">
|
||||
|
||||
<li>
|
||||
<a><strong>DESCRIBE CLUSTER;</strong></a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li class="dropdown">
|
||||
<a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
|
||||
<strong>Legend</strong>
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-dashboard text-muted" /> Cluster
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-folder-open text-danger" /> Keyspace
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-copyright-mark text-warning" /> UDT
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-th-list text-primary" /> Table
|
||||
</a>
|
||||
</li>
|
||||
<li class="bg-info">
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-fullscreen" /> Partition Key
|
||||
</a>
|
||||
</li>
|
||||
<li class="bg-warning">
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-pushpin" /> Static Column
|
||||
</a>
|
||||
</li>
|
||||
<li class="bg-success">
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-sort" /> Clustering Column
|
||||
</a>
|
||||
</li>
|
||||
<li class="bg-success">
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-sort-by-attributes" /> Clustering Order ASC
|
||||
</a>
|
||||
</li>
|
||||
<li class="bg-success">
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-sort-by-attributes-alt" /> Clustering Order DESC
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-info-sign" /> Indexed Column
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<hr/>
|
||||
<div class="row">
|
||||
<div class="col-md-4"></div>
|
||||
<div class="col-md-4 col-offset-md-4">
|
||||
<div class="table-responsive table-bordered">
|
||||
<table class="table">
|
||||
<caption>
|
||||
<h4 class="text-muted">
|
||||
<i class="glyphicon glyphicon-dashboard"/> Test Cluster
|
||||
</h4>
|
||||
</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Partitioner</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>org.apache.cassandra.dht.Murmur3Partitioner</td>
|
||||
</tr>
|
||||
<tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br/><br/><nav class="navbar navbar-default"><ul class="nav navbar-nav"><li><a><strong>DESCRIBE CLUSTER;</strong></a></li></ul><ul class="nav navbar-nav navbar-right"><li class="dropdown"><a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><strong>Legend</strong><span class="caret"></span></a><ul class="dropdown-menu"><li><a role="button"><i class="glyphicon glyphicon-dashboard text-muted" /> Cluster</a></li><li><a role="button"><i class="glyphicon glyphicon-folder-open text-danger" /> Keyspace</a></li><li><a role="button"><i class="glyphicon glyphicon-copyright-mark text-warning" /> UDT</a></li><li><a role="button"><i class="glyphicon glyphicon-th-list text-primary" /> Table</a></li><li><a role="button"><i class="glyphicon glyphicon-eye-open text-primary" /> Materialized View</a></li><li><a role="button"><i class="glyphicon glyphicon-random text-success" /> Function</a></li><li><a role="button"><i class="glyphicon glyphicon-retweet text-success" /> Aggregate</a></li><li role="separator" class="divider text-muted"></li><li class="dropdown-header"><span class="text-primary">Table icons</span></li><li class="bg-info"><a role="button"><i class="glyphicon glyphicon-fullscreen" /> Partition Key</a></li><li class="bg-warning"><a role="button"><i class="glyphicon glyphicon-pushpin" /> Static Column</a></li><li class="bg-success"><a role="button"><i class="glyphicon glyphicon-sort" /> Clustering Column</a></li><li class="bg-success"><a role="button"><i class="glyphicon glyphicon-sort-by-attributes" /> Clustering Order ASC</a></li><li class="bg-success"><a role="button"><i class="glyphicon glyphicon-sort-by-attributes-alt" /> Clustering Order DESC</a></li></ul></li><li><a href="#"></a></li></ul></nav><hr/><div class="row"><div class="col-md-4"></div><div class="col-md-4 col-offset-md-4"><div class="table-responsive table-bordered"><table class="table"><caption><h4 class="text-muted"><i class="glyphicon glyphicon-dashboard"/> Test Cluster</h4></caption><thead><tr><th>Partitioner</th></tr></thead><tbody><tr><td>org.apache.cassandra.dht.Murmur3Partitioner</td></tr><tbody></table></div></div></div>
|
||||
|
|
@ -1,137 +1 @@
|
|||
<br/>
|
||||
<br/>
|
||||
<nav class="navbar navbar-default">
|
||||
<ul class="nav navbar-nav">
|
||||
|
||||
<li>
|
||||
<a><strong>DESCRIBE TYPE live_data.address;</strong></a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li class="dropdown">
|
||||
<a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
|
||||
<strong>Legend</strong>
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-dashboard text-muted" /> Cluster
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-folder-open text-danger" /> Keyspace
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-copyright-mark text-warning" /> UDT
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-th-list text-primary" /> Table
|
||||
</a>
|
||||
</li>
|
||||
<li class="bg-info">
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-fullscreen" /> Partition Key
|
||||
</a>
|
||||
</li>
|
||||
<li class="bg-warning">
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-pushpin" /> Static Column
|
||||
</a>
|
||||
</li>
|
||||
<li class="bg-success">
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-sort" /> Clustering Column
|
||||
</a>
|
||||
</li>
|
||||
<li class="bg-success">
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-sort-by-attributes" /> Clustering Order ASC
|
||||
</a>
|
||||
</li>
|
||||
<li class="bg-success">
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-sort-by-attributes-alt" /> Clustering Order DESC
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-info-sign" /> Indexed Column
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<hr/>
|
||||
<div class="row">
|
||||
<div class="col-md-3"></div>
|
||||
<div class="col-md-6 col-offset-md-3">
|
||||
<div class="panel panel-default table-responsive table-bordered">
|
||||
<table class="table">
|
||||
|
||||
<caption><h4 class="text-warning"><i class="glyphicon glyphicon-copyright-mark"/> address</h4></caption>
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-md-6">Column Name</th>
|
||||
<th class="col-md-6">Data Type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td class="col-md-6">number</td>
|
||||
<td class="col-md-6">int</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="col-md-6">street</td>
|
||||
<td class="col-md-6">text</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="col-md-6">zip</td>
|
||||
<td class="col-md-6">int</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="col-md-6">city</td>
|
||||
<td class="col-md-6">text</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="col-md-6">country</td>
|
||||
<td class="col-md-6">text</td>
|
||||
</tr>
|
||||
|
||||
<tbody>
|
||||
</table>
|
||||
<div class="panel-footer">
|
||||
<a data-toggle="collapse" data-target="#984da320-350d-11e5-a7a9-8f0ea8ae1a37_asCQL">
|
||||
<strong>As CQL statement</strong>
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
<br/><br/>
|
||||
<div class="collapse" id="984da320-350d-11e5-a7a9-8f0ea8ae1a37_asCQL">
|
||||
<pre class="well">CREATE TYPE live_data.address (
|
||||
number int,
|
||||
street text,
|
||||
zip int,
|
||||
city text,
|
||||
country text
|
||||
);</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3"></div>
|
||||
</div>
|
||||
<br/><br/><nav class="navbar navbar-default"><ul class="nav navbar-nav"><li><a><strong>DESCRIBE TYPE live_data.address;</strong></a></li></ul><ul class="nav navbar-nav navbar-right"><li class="dropdown"><a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><strong>Legend</strong><span class="caret"></span></a><ul class="dropdown-menu"><li><a role="button"><i class="glyphicon glyphicon-dashboard text-muted" /> Cluster</a></li><li><a role="button"><i class="glyphicon glyphicon-folder-open text-danger" /> Keyspace</a></li><li><a role="button"><i class="glyphicon glyphicon-copyright-mark text-warning" /> UDT</a></li><li><a role="button"><i class="glyphicon glyphicon-th-list text-primary" /> Table</a></li><li><a role="button"><i class="glyphicon glyphicon-eye-open text-primary" /> Materialized View</a></li><li><a role="button"><i class="glyphicon glyphicon-random text-success" /> Function</a></li><li><a role="button"><i class="glyphicon glyphicon-retweet text-success" /> Aggregate</a></li><li role="separator" class="divider text-muted"></li><li class="dropdown-header"><span class="text-primary">Table icons</span></li><li class="bg-info"><a role="button"><i class="glyphicon glyphicon-fullscreen" /> Partition Key</a></li><li class="bg-warning"><a role="button"><i class="glyphicon glyphicon-pushpin" /> Static Column</a></li><li class="bg-success"><a role="button"><i class="glyphicon glyphicon-sort" /> Clustering Column</a></li><li class="bg-success"><a role="button"><i class="glyphicon glyphicon-sort-by-attributes" /> Clustering Order ASC</a></li><li class="bg-success"><a role="button"><i class="glyphicon glyphicon-sort-by-attributes-alt" /> Clustering Order DESC</a></li></ul></li><li><a href="#"></a></li></ul></nav><hr/><div class="row"><div class="col-md-3"></div><div class="col-md-6 col-offset-md-3"><div class="panel panel-default table-responsive table-bordered"><table class="table"><caption><h4 class="text-warning"><i class="glyphicon glyphicon-copyright-mark"/> address</h4></caption><thead><tr><th class="col-md-6">Column Name</th><th class="col-md-6">Data Type</th></tr></thead><tbody><tr><td class="col-md-6">number</td><td class="col-md-6">int</td></tr><tr><td class="col-md-6">street</td><td class="col-md-6">text</td></tr><tr><td class="col-md-6">zip</td><td class="col-md-6">int</td></tr><tr><td class="col-md-6">city</td><td class="col-md-6">text</td></tr><tr><td class="col-md-6">country</td><td class="col-md-6">text</td></tr><tbody></table><div class="panel-footer"><a data-toggle="collapse" class="text-warning"><strong>As CQL statement</strong><span class="caret"></span></a><br/><br/><div class="collapse" ><pre class="well">CREATE TYPE live_data.address (number int,street text,zip int,city text,country text);</pre></div></div></div></div><div class="col-md-3"></div></div>
|
||||
|
|
@ -1,137 +1 @@
|
|||
<br/>
|
||||
<br/>
|
||||
<nav class="navbar navbar-default">
|
||||
<ul class="nav navbar-nav">
|
||||
|
||||
<li>
|
||||
<a><strong>DESCRIBE TYPE address;</strong></a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li class="dropdown">
|
||||
<a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
|
||||
<strong>Legend</strong>
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-dashboard text-muted" /> Cluster
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-folder-open text-danger" /> Keyspace
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-copyright-mark text-warning" /> UDT
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-th-list text-primary" /> Table
|
||||
</a>
|
||||
</li>
|
||||
<li class="bg-info">
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-fullscreen" /> Partition Key
|
||||
</a>
|
||||
</li>
|
||||
<li class="bg-warning">
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-pushpin" /> Static Column
|
||||
</a>
|
||||
</li>
|
||||
<li class="bg-success">
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-sort" /> Clustering Column
|
||||
</a>
|
||||
</li>
|
||||
<li class="bg-success">
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-sort-by-attributes" /> Clustering Order ASC
|
||||
</a>
|
||||
</li>
|
||||
<li class="bg-success">
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-sort-by-attributes-alt" /> Clustering Order DESC
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a role="button">
|
||||
<i class="glyphicon glyphicon-info-sign" /> Indexed Column
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<hr/>
|
||||
<div class="row">
|
||||
<div class="col-md-3"></div>
|
||||
<div class="col-md-6 col-offset-md-3">
|
||||
<div class="panel panel-default table-responsive table-bordered">
|
||||
<table class="table">
|
||||
|
||||
<caption><h4 class="text-warning"><i class="glyphicon glyphicon-copyright-mark"/> address</h4></caption>
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-md-6">Column Name</th>
|
||||
<th class="col-md-6">Data Type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td class="col-md-6">number</td>
|
||||
<td class="col-md-6">int</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="col-md-6">street</td>
|
||||
<td class="col-md-6">text</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="col-md-6">zip</td>
|
||||
<td class="col-md-6">int</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="col-md-6">city</td>
|
||||
<td class="col-md-6">text</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="col-md-6">country</td>
|
||||
<td class="col-md-6">text</td>
|
||||
</tr>
|
||||
|
||||
<tbody>
|
||||
</table>
|
||||
<div class="panel-footer">
|
||||
<a data-toggle="collapse" data-target="#984da320-350d-11e5-a7a9-8f0ea8ae1a37_asCQL">
|
||||
<strong>As CQL statement</strong>
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
<br/><br/>
|
||||
<div class="collapse" id="984da320-350d-11e5-a7a9-8f0ea8ae1a37_asCQL">
|
||||
<pre class="well">CREATE TYPE live_data.address (
|
||||
number int,
|
||||
street text,
|
||||
zip int,
|
||||
city text,
|
||||
country text
|
||||
);</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3"></div>
|
||||
</div>
|
||||
<br/><br/><nav class="navbar navbar-default"><ul class="nav navbar-nav"><li><a><strong>DESCRIBE TYPE address;</strong></a></li></ul><ul class="nav navbar-nav navbar-right"><li class="dropdown"><a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><strong>Legend</strong><span class="caret"></span></a><ul class="dropdown-menu"><li><a role="button"><i class="glyphicon glyphicon-dashboard text-muted" /> Cluster</a></li><li><a role="button"><i class="glyphicon glyphicon-folder-open text-danger" /> Keyspace</a></li><li><a role="button"><i class="glyphicon glyphicon-copyright-mark text-warning" /> UDT</a></li><li><a role="button"><i class="glyphicon glyphicon-th-list text-primary" /> Table</a></li><li><a role="button"><i class="glyphicon glyphicon-eye-open text-primary" /> Materialized View</a></li><li><a role="button"><i class="glyphicon glyphicon-random text-success" /> Function</a></li><li><a role="button"><i class="glyphicon glyphicon-retweet text-success" /> Aggregate</a></li><li role="separator" class="divider text-muted"></li><li class="dropdown-header"><span class="text-primary">Table icons</span></li><li class="bg-info"><a role="button"><i class="glyphicon glyphicon-fullscreen" /> Partition Key</a></li><li class="bg-warning"><a role="button"><i class="glyphicon glyphicon-pushpin" /> Static Column</a></li><li class="bg-success"><a role="button"><i class="glyphicon glyphicon-sort" /> Clustering Column</a></li><li class="bg-success"><a role="button"><i class="glyphicon glyphicon-sort-by-attributes" /> Clustering Order ASC</a></li><li class="bg-success"><a role="button"><i class="glyphicon glyphicon-sort-by-attributes-alt" /> Clustering Order DESC</a></li></ul></li><li><a href="#"></a></li></ul></nav><hr/><div class="row"><div class="col-md-3"></div><div class="col-md-6 col-offset-md-3"><div class="panel panel-default table-responsive table-bordered"><table class="table"><caption><h4 class="text-warning"><i class="glyphicon glyphicon-copyright-mark"/> address</h4></caption><thead><tr><th class="col-md-6">Column Name</th><th class="col-md-6">Data Type</th></tr></thead><tbody><tr><td class="col-md-6">number</td><td class="col-md-6">int</td></tr><tr><td class="col-md-6">street</td><td class="col-md-6">text</td></tr><tr><td class="col-md-6">zip</td><td class="col-md-6">int</td></tr><tr><td class="col-md-6">city</td><td class="col-md-6">text</td></tr><tr><td class="col-md-6">country</td><td class="col-md-6">text</td></tr><tbody></table><div class="panel-footer"><a data-toggle="collapse" class="text-warning"><strong>As CQL statement</strong><span class="caret"></span></a><br/><br/><div class="collapse" ><pre class="well">CREATE TYPE live_data.address (number int,street text,zip int,city text,country text);</pre></div></div></div></div><div class="col-md-3"></div></div>
|
||||
|
|
@ -23,6 +23,8 @@ import org.scalatest.{BeforeAndAfterEach, FlatSpec, Matchers}
|
|||
import org.apache.zeppelin.cassandra.ParagraphParser._
|
||||
import org.apache.zeppelin.cassandra.TextBlockHierarchy._
|
||||
|
||||
import scala.Option
|
||||
|
||||
class ParagraphParserTest extends FlatSpec
|
||||
with BeforeAndAfterEach
|
||||
with Matchers
|
||||
|
|
@ -57,34 +59,46 @@ class ParagraphParserTest extends FlatSpec
|
|||
|
||||
val parsed = parser.parse(parser.queries,query)
|
||||
|
||||
parsed.get should be(List(
|
||||
SimpleStm("SELECT * FROM albums LIMIT 10;"),
|
||||
BatchStm(BatchStatement.Type.UNLOGGED,
|
||||
List(
|
||||
SimpleStm("INSERT INTO users(id) VALUES(10);"),
|
||||
BoundStm("test","'a',12.34")
|
||||
)
|
||||
),
|
||||
SimpleStm("SELECT * FROM users LIMIT 10;"),
|
||||
BatchStm(BatchStatement.Type.LOGGED,
|
||||
List(
|
||||
SimpleStm("Insert INTO users(id) VALUES(11);"),
|
||||
SimpleStm("INSERT INTO users(id) VALUES(12);")
|
||||
)
|
||||
),
|
||||
BoundStm("toto","'a',12.34"),
|
||||
DescribeTableCmd(Option("zeppelin"),"users"),
|
||||
DescribeKeyspaceCmd("zeppelin")
|
||||
))
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(
|
||||
SimpleStm("SELECT * FROM albums LIMIT 10;"),
|
||||
BatchStm(BatchStatement.Type.UNLOGGED,
|
||||
List(
|
||||
SimpleStm("INSERT INTO users(id) VALUES(10);"),
|
||||
BoundStm("test","'a',12.34")
|
||||
)
|
||||
),
|
||||
SimpleStm("SELECT * FROM users LIMIT 10;"),
|
||||
BatchStm(BatchStatement.Type.LOGGED,
|
||||
List(
|
||||
SimpleStm("Insert INTO users(id) VALUES(11);"),
|
||||
SimpleStm("INSERT INTO users(id) VALUES(12);")
|
||||
)
|
||||
),
|
||||
BoundStm("toto","'a',12.34"),
|
||||
DescribeTableCmd(Some("zeppelin"),"users"),
|
||||
DescribeKeyspaceCmd("zeppelin")
|
||||
),_) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "parse single-line comment" in {
|
||||
"Parser" should "parse hash single-line comment" in {
|
||||
val query :CharSequence="""#This is a comment""".stripMargin
|
||||
|
||||
val parsed = parser.parseAll[Comment](parser.singleLineComment, query)
|
||||
parsed.get should be(Comment("This is a comment"))
|
||||
parsed should matchPattern {
|
||||
case parser.Success(Comment("This is a comment"), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "parse double slashes single-line comment" in {
|
||||
val query :CharSequence="""//This is another comment""".stripMargin
|
||||
|
||||
val parsed = parser.parseAll[Comment](parser.singleLineComment, query)
|
||||
parsed should matchPattern {
|
||||
case parser.Success(Comment("This is another comment"), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "parse multi-line comment" in {
|
||||
val query:String =
|
||||
|
|
@ -96,13 +110,17 @@ class ParagraphParserTest extends FlatSpec
|
|||
""".stripMargin
|
||||
|
||||
val parsed = parser.parseAll(parser.multiLineComment, query)
|
||||
parsed.get should be(Comment("This is a comment\nline1\nline2\nline3\n"))
|
||||
parsed should matchPattern {
|
||||
case parser.Success(Comment("This is a comment\nline1\nline2\nline3\n"), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "parse consistency level" in {
|
||||
val query:String =""" @consistency=ONE""".stripMargin
|
||||
val parsed = parser.parseAll(parser.consistency, query)
|
||||
parsed.get should be(Consistency(ConsistencyLevel.ONE))
|
||||
parsed should matchPattern {
|
||||
case parser.Success(Consistency(ConsistencyLevel.ONE), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "fails parsing unknown consistency level" in {
|
||||
|
|
@ -116,7 +134,9 @@ class ParagraphParserTest extends FlatSpec
|
|||
"Parser" should "parse serial consistency level" in {
|
||||
val query:String =""" @serialConsistency=LOCAL_SERIAL""".stripMargin
|
||||
val parsed = parser.parseAll(parser.serialConsistency, query)
|
||||
parsed.get should be(SerialConsistency(ConsistencyLevel.LOCAL_SERIAL))
|
||||
parsed should matchPattern {
|
||||
case parser.Success(SerialConsistency(ConsistencyLevel.LOCAL_SERIAL), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "fails parsing unknown serial consistency level" in {
|
||||
|
|
@ -130,7 +150,8 @@ class ParagraphParserTest extends FlatSpec
|
|||
"Parser" should "parse timestamp" in {
|
||||
val query:String =""" @timestamp=111""".stripMargin
|
||||
val parsed = parser.parseAll(parser.timestamp, query)
|
||||
parsed.get should be(Timestamp(111L))
|
||||
parsed should matchPattern {
|
||||
case parser.Success(Timestamp(111L), _) => }
|
||||
}
|
||||
|
||||
"Parser" should "fails parsing invalid timestamp" in {
|
||||
|
|
@ -144,7 +165,7 @@ class ParagraphParserTest extends FlatSpec
|
|||
"Parser" should "parse retry policy" in {
|
||||
val query:String ="@retryPolicy="+CassandraInterpreter.DOWNGRADING_CONSISTENCY_RETRY
|
||||
val parsed = parser.parseAll(parser.retryPolicy, query)
|
||||
parsed.get should be(DowngradingRetryPolicy)
|
||||
parsed should matchPattern {case parser.Success(DowngradingRetryPolicy, _) => }
|
||||
}
|
||||
|
||||
"Parser" should "fails parsing invalid retry policy" in {
|
||||
|
|
@ -158,7 +179,7 @@ class ParagraphParserTest extends FlatSpec
|
|||
"Parser" should "parse fetch size" in {
|
||||
val query:String ="@fetchSize=100"
|
||||
val parsed = parser.parseAll(parser.fetchSize, query)
|
||||
parsed.get should be(FetchSize(100))
|
||||
parsed should matchPattern { case parser.Success(FetchSize(100), _) =>}
|
||||
}
|
||||
|
||||
"Parser" should "fails parsing invalid fetch size" in {
|
||||
|
|
@ -177,7 +198,7 @@ class ParagraphParserTest extends FlatSpec
|
|||
val parsed = parser.parseAll(parser.genericStatement, query)
|
||||
|
||||
//Then
|
||||
parsed.get should be(SimpleStm("sElecT * FROM users LIMIT ? ;"))
|
||||
parsed should matchPattern { case parser.Success(SimpleStm("sElecT * FROM users LIMIT ? ;"), _) =>}
|
||||
}
|
||||
|
||||
"Parser" should "parse prepare" in {
|
||||
|
|
@ -188,7 +209,7 @@ class ParagraphParserTest extends FlatSpec
|
|||
val parsed = parser.parseAll(parser.prepare, query)
|
||||
|
||||
//Then
|
||||
parsed.get should be(PrepareStm("select_users","SELECT * FROM users LIMIT ?"))
|
||||
parsed should matchPattern { case parser.Success(PrepareStm("select_users","SELECT * FROM users LIMIT ?"), _) => }
|
||||
}
|
||||
|
||||
"Parser" should "fails parsing invalid prepared statement" in {
|
||||
|
|
@ -207,7 +228,7 @@ class ParagraphParserTest extends FlatSpec
|
|||
val parsed = parser.parseAll(parser.removePrepare, query)
|
||||
|
||||
//Then
|
||||
parsed.get should be(RemovePrepareStm("select_users"))
|
||||
parsed should matchPattern { case parser.Success(RemovePrepareStm("select_users"), _) => }
|
||||
}
|
||||
|
||||
"Parser" should "fails parsing invalid remove prepared statement" in {
|
||||
|
|
@ -226,7 +247,7 @@ class ParagraphParserTest extends FlatSpec
|
|||
val parsed = parser.parseAll(parser.bind, query)
|
||||
|
||||
//Then
|
||||
parsed.get should be(BoundStm("select_users","10,'toto'"))
|
||||
parsed should matchPattern { case parser.Success(BoundStm("select_users","10,'toto'"), _) => }
|
||||
}
|
||||
|
||||
"Parser" should "fails parsing invalid bind statement" in {
|
||||
|
|
@ -251,17 +272,17 @@ class ParagraphParserTest extends FlatSpec
|
|||
val parsed = parser.parseAll(parser.batch, query)
|
||||
|
||||
//Then
|
||||
parsed.get should be(
|
||||
BatchStm(
|
||||
BatchStatement.Type.LOGGED,
|
||||
List[QueryStatement](
|
||||
SimpleStm("Insert INTO users(id) VALUES(10);"),
|
||||
BoundStm("select_users", "10,'toto'"),
|
||||
SimpleStm("update users SET name ='John DOE' WHERE id=10;"),
|
||||
SimpleStm("dElEtE users WHERE id=11;")
|
||||
parsed should matchPattern {
|
||||
case parser.Success(BatchStm(
|
||||
BatchStatement.Type.LOGGED,
|
||||
List(
|
||||
SimpleStm("Insert INTO users(id) VALUES(10);"),
|
||||
BoundStm("select_users", "10,'toto'"),
|
||||
SimpleStm("update users SET name ='John DOE' WHERE id=10;"),
|
||||
SimpleStm("dElEtE users WHERE id=11;")
|
||||
)
|
||||
)
|
||||
)
|
||||
), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "fails parsing invalid batch type" in {
|
||||
|
|
@ -280,10 +301,12 @@ class ParagraphParserTest extends FlatSpec
|
|||
|
||||
val parsed = parser.parseAll(parser.queries, query)
|
||||
|
||||
parsed.get should be (List(
|
||||
SerialConsistency(ConsistencyLevel.SERIAL),
|
||||
SimpleStm("SELECT * FROM zeppelin.artists LIMIT 1;")
|
||||
))
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(
|
||||
SerialConsistency(ConsistencyLevel.SERIAL),
|
||||
SimpleStm("SELECT * FROM zeppelin.artists LIMIT 1;")
|
||||
), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "parse multi-line single statement" in {
|
||||
|
|
@ -292,13 +315,15 @@ class ParagraphParserTest extends FlatSpec
|
|||
" title text PRIMARY KEY,\n" +
|
||||
" artist text,\n" +
|
||||
" year int\n" +
|
||||
");\n";
|
||||
");\n"
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, query)
|
||||
|
||||
parsed.get should be (List(
|
||||
SimpleStm("CREATE TABLE IF NOT EXISTS zeppelin.albums(\n title text PRIMARY KEY,\n artist text,\n year int\n);")
|
||||
))
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(
|
||||
SimpleStm("CREATE TABLE IF NOT EXISTS zeppelin.albums(\n title text PRIMARY KEY,\n artist text,\n year int\n);")
|
||||
), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "parse multi-line statements" in {
|
||||
|
|
@ -316,11 +341,12 @@ class ParagraphParserTest extends FlatSpec
|
|||
"APPLY BATCH;\n"+
|
||||
"@timestamp=10\n" +
|
||||
"@retryPolicy=DOWNGRADING_CONSISTENCY\n" +
|
||||
"SELECT * FROM zeppelin.albums;";
|
||||
"SELECT * FROM zeppelin.albums;"
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, query)
|
||||
|
||||
parsed.get should be (List(
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(
|
||||
SimpleStm("CREATE TABLE IF NOT EXISTS zeppelin.albums(\n title text PRIMARY KEY,\n artist text,\n year int\n);"),
|
||||
Consistency(ConsistencyLevel.THREE),
|
||||
SerialConsistency(ConsistencyLevel.SERIAL),
|
||||
|
|
@ -334,7 +360,8 @@ class ParagraphParserTest extends FlatSpec
|
|||
Timestamp(10L),
|
||||
DowngradingRetryPolicy,
|
||||
SimpleStm("SELECT * FROM zeppelin.albums;")
|
||||
))
|
||||
), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "parse mixed single-line and multi-line statements" in {
|
||||
|
|
@ -349,11 +376,12 @@ class ParagraphParserTest extends FlatSpec
|
|||
" INSERT INTO zeppelin.albums(title,artist,year) VALUES('The Way You Are','Tears for Fears',1983);"+
|
||||
" INSERT INTO zeppelin.albums(title,artist,year) VALUES('Primitive','Soulfly',2003);\n"+
|
||||
"APPLY BATCH;"+
|
||||
"SELECT * FROM zeppelin.albums;";
|
||||
"SELECT * FROM zeppelin.albums;"
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, query)
|
||||
|
||||
parsed.get should be (List(
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(
|
||||
SimpleStm("CREATE TABLE IF NOT EXISTS zeppelin.albums(\n title text PRIMARY KEY,\n artist text,\n year int\n);"),
|
||||
BatchStm(BatchStatement.Type.LOGGED,
|
||||
List(
|
||||
|
|
@ -363,7 +391,8 @@ class ParagraphParserTest extends FlatSpec
|
|||
)
|
||||
),
|
||||
SimpleStm("SELECT * FROM zeppelin.albums;")
|
||||
))
|
||||
), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "parse a block queries with comments" in {
|
||||
|
|
@ -395,7 +424,8 @@ class ParagraphParserTest extends FlatSpec
|
|||
|
||||
val parsed = parser.parseAll(parser.queries, query)
|
||||
|
||||
parsed.get should be (List(
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(
|
||||
Comment("\n This example show how to force a\n timestamp on the query\n "),
|
||||
Comment("Timestamp in the past"),
|
||||
Timestamp(10L),
|
||||
|
|
@ -409,8 +439,9 @@ class ParagraphParserTest extends FlatSpec
|
|||
SimpleStm("INSERT INTO spark_demo.ts(key,value) VALUES(1,'val2');"),
|
||||
Comment("Check the result"),
|
||||
SimpleStm("SELECT * FROM spark_demo.ts WHERE key=1;")
|
||||
)
|
||||
)
|
||||
), _
|
||||
) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "remove prepared statement" in {
|
||||
|
|
@ -426,13 +457,14 @@ class ParagraphParserTest extends FlatSpec
|
|||
|
||||
val parsed = parser.parseAll(parser.queries, queries)
|
||||
|
||||
parsed.get should be(List(
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(
|
||||
Comment("Removing an unknown statement should has no side effect"),
|
||||
RemovePrepareStm("unknown_statement"),
|
||||
RemovePrepareStm("select_artist_by_name"),
|
||||
Comment("This should fail because the 'select_artist_by_name' has been removed"),
|
||||
BoundStm("select_artist_by_name","'The Beatles'")
|
||||
))
|
||||
), _) => }
|
||||
}
|
||||
|
||||
"Parser" should "parse only parameter" in {
|
||||
|
|
@ -441,7 +473,9 @@ class ParagraphParserTest extends FlatSpec
|
|||
|
||||
val parsed = parser.parseAll(parser.queries, queries)
|
||||
|
||||
parsed.get should be(List(FetchSize(1000)))
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(FetchSize(1000)), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -450,7 +484,9 @@ class ParagraphParserTest extends FlatSpec
|
|||
|
||||
val parsed = parser.parseAll(parser.queries, queries)
|
||||
|
||||
parsed.get(0) shouldBe a [DescribeClusterCmd]
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(DescribeClusterCmd("DESCRIBE CLUSTER;")), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "fail parsing describe cluster" in {
|
||||
|
|
@ -462,46 +498,14 @@ class ParagraphParserTest extends FlatSpec
|
|||
ex.getMessage should be(s"Invalid syntax for DESCRIBE CLUSTER. It should comply to the pattern: ${DESCRIBE_CLUSTER_PATTERN.toString}")
|
||||
}
|
||||
|
||||
"Parser" should "parse describe keyspaces" in {
|
||||
val queries ="Describe KeYsPaCeS;"
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, queries)
|
||||
|
||||
parsed.get(0) shouldBe a [DescribeKeyspacesCmd]
|
||||
}
|
||||
|
||||
"Parser" should "fail parsing describe keyspaces" in {
|
||||
val queries ="Describe KeYsPaCeS"
|
||||
|
||||
val ex = intercept[InterpreterException] {
|
||||
parser.parseAll(parser.queries, queries)
|
||||
}
|
||||
ex.getMessage should be(s"Invalid syntax for DESCRIBE KEYSPACES. It should comply to the pattern: ${DESCRIBE_KEYSPACES_PATTERN.toString}")
|
||||
}
|
||||
|
||||
"Parser" should "parse describe tables" in {
|
||||
val queries ="Describe TaBlEs;"
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, queries)
|
||||
|
||||
parsed.get(0) shouldBe a [DescribeTablesCmd]
|
||||
}
|
||||
|
||||
"Parser" should "fail parsing describe tables" in {
|
||||
val queries ="Describe TaBlEs"
|
||||
|
||||
val ex = intercept[InterpreterException] {
|
||||
parser.parseAll(parser.queries, queries)
|
||||
}
|
||||
ex.getMessage should be(s"Invalid syntax for DESCRIBE TABLES. It should comply to the pattern: ${DESCRIBE_TABLES_PATTERN.toString}")
|
||||
}
|
||||
|
||||
"Parser" should "parse describe keyspace" in {
|
||||
val queries ="Describe KeYsPaCe toto;"
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, queries)
|
||||
|
||||
parsed.get should be(List(DescribeKeyspaceCmd("toto")))
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(DescribeKeyspaceCmd("toto")), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "fail parsing describe keyspace" in {
|
||||
|
|
@ -513,6 +517,25 @@ class ParagraphParserTest extends FlatSpec
|
|||
ex.getMessage should be(s"Invalid syntax for DESCRIBE KEYSPACE. It should comply to the pattern: ${DESCRIBE_KEYSPACE_PATTERN.toString}")
|
||||
}
|
||||
|
||||
"Parser" should "parse describe keyspaces" in {
|
||||
val queries ="Describe KeYsPaCeS;"
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, queries)
|
||||
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(DescribeKeyspacesCmd("DESCRIBE KEYSPACES;")), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "fail parsing describe keyspaces" in {
|
||||
val queries ="Describe KeYsPaCeS"
|
||||
|
||||
val ex = intercept[InterpreterException] {
|
||||
parser.parseAll(parser.queries, queries)
|
||||
}
|
||||
ex.getMessage should be(s"Invalid syntax for DESCRIBE KEYSPACES. It should comply to the pattern: ${DESCRIBE_KEYSPACES_PATTERN.toString}")
|
||||
}
|
||||
|
||||
"Parser" should "parse describe table" in {
|
||||
val queries ="Describe TaBlE toto;"
|
||||
|
||||
|
|
@ -539,12 +562,33 @@ class ParagraphParserTest extends FlatSpec
|
|||
s"${DESCRIBE_TABLE_WITH_KEYSPACE_PATTERN.toString} or ${DESCRIBE_TABLE_PATTERN.toString}")
|
||||
}
|
||||
|
||||
"Parser" should "parse describe tables" in {
|
||||
val queries ="Describe TaBlEs;"
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, queries)
|
||||
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(DescribeTablesCmd("DESCRIBE TABLES;")), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "fail parsing describe tables" in {
|
||||
val queries ="Describe TaBlEs"
|
||||
|
||||
val ex = intercept[InterpreterException] {
|
||||
parser.parseAll(parser.queries, queries)
|
||||
}
|
||||
ex.getMessage should be(s"Invalid syntax for DESCRIBE TABLES. It should comply to the pattern: ${DESCRIBE_TABLES_PATTERN.toString}")
|
||||
}
|
||||
|
||||
"Parser" should "parse describe type" in {
|
||||
val queries ="Describe Type toto;"
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, queries)
|
||||
|
||||
parsed.get should be(List(DescribeUDTCmd(None,"toto")))
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(DescribeTypeCmd(None, "toto")), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "parse describe type with keyspace" in {
|
||||
|
|
@ -552,7 +596,9 @@ class ParagraphParserTest extends FlatSpec
|
|||
|
||||
val parsed = parser.parseAll(parser.queries, queries)
|
||||
|
||||
parsed.get should be(List(DescribeUDTCmd(Some("ks"),"toto")))
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(DescribeTypeCmd(Some("ks"), "toto")), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "fail parsing describe type" in {
|
||||
|
|
@ -565,12 +611,186 @@ class ParagraphParserTest extends FlatSpec
|
|||
s"${DESCRIBE_TYPE_WITH_KEYSPACE_PATTERN.toString} or ${DESCRIBE_TYPE_PATTERN.toString}")
|
||||
}
|
||||
|
||||
"Parser" should "parse describe types" in {
|
||||
val queries ="Describe types;"
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, queries)
|
||||
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(DescribeTypesCmd("DESCRIBE TYPES;")), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "fail parsing describe types" in {
|
||||
val queries ="Describe types"
|
||||
|
||||
val ex = intercept[InterpreterException] {
|
||||
parser.parseAll(parser.queries, queries)
|
||||
}
|
||||
ex.getMessage should be(s"Invalid syntax for DESCRIBE TYPES. It should comply to the pattern: ${DESCRIBE_TYPES_PATTERN.toString}")
|
||||
}
|
||||
|
||||
"Parser" should "parse describe function" in {
|
||||
val queries ="Describe function toto;"
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, queries)
|
||||
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(DescribeFunctionCmd(None,"toto")),_) =>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
"Parser" should "parse describe function with keyspace" in {
|
||||
val queries ="Describe function ks.toto;"
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, queries)
|
||||
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(DescribeFunctionCmd(Some("ks"), "toto")), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "fail parsing describe function" in {
|
||||
val queries ="Describe function toto"
|
||||
|
||||
val ex = intercept[InterpreterException] {
|
||||
parser.parseAll(parser.queries, queries)
|
||||
}
|
||||
ex.getMessage should be(s"Invalid syntax for DESCRIBE FUNCTION. It should comply to the patterns: " +
|
||||
s"${DESCRIBE_FUNCTION_WITH_KEYSPACE_PATTERN.toString} or ${DESCRIBE_FUNCTION_PATTERN.toString}")
|
||||
}
|
||||
|
||||
"Parser" should "parse describe functions" in {
|
||||
val queries ="Describe functions;"
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, queries)
|
||||
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(DescribeFunctionsCmd("DESCRIBE FUNCTIONS;")), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "fail parsing describe functions" in {
|
||||
val queries ="Describe functions toto"
|
||||
|
||||
val ex = intercept[InterpreterException] {
|
||||
parser.parseAll(parser.queries, queries)
|
||||
}
|
||||
ex.getMessage should be(s"Invalid syntax for DESCRIBE FUNCTIONS. It should comply to the pattern: " +
|
||||
s"${DESCRIBE_FUNCTIONS_PATTERN.toString}")
|
||||
}
|
||||
|
||||
|
||||
"Parser" should "parse describe aggregate" in {
|
||||
val queries ="Describe aggregate toto;"
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, queries)
|
||||
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(DescribeAggregateCmd(None, "toto")), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "parse describe aggregate with keyspace" in {
|
||||
val queries ="Describe aggregate ks.toto;"
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, queries)
|
||||
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(DescribeAggregateCmd(Some("ks"),"toto")), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "fail parsing describe aggregate" in {
|
||||
val queries ="Describe aggregate toto"
|
||||
|
||||
val ex = intercept[InterpreterException] {
|
||||
parser.parseAll(parser.queries, queries)
|
||||
}
|
||||
ex.getMessage should be(s"Invalid syntax for DESCRIBE AGGREGATE. It should comply to the patterns: " +
|
||||
s"${DESCRIBE_AGGREGATE_WITH_KEYSPACE_PATTERN.toString} or ${DESCRIBE_AGGREGATE_PATTERN.toString}")
|
||||
}
|
||||
|
||||
"Parser" should "parse describe aggregates" in {
|
||||
val queries ="Describe aggregates;"
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, queries)
|
||||
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(DescribeAggregatesCmd("DESCRIBE AGGREGATES;")), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "fail parsing describe aggregates" in {
|
||||
val queries ="Describe aggregates toto"
|
||||
|
||||
val ex = intercept[InterpreterException] {
|
||||
parser.parseAll(parser.queries, queries)
|
||||
}
|
||||
ex.getMessage should be(s"Invalid syntax for DESCRIBE AGGREGATES. It should comply to the pattern: " +
|
||||
s"${DESCRIBE_AGGREGATES_PATTERN.toString}")
|
||||
}
|
||||
|
||||
"Parser" should "parse describe materialized view" in {
|
||||
val queries ="Describe materialized view toto;"
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, queries)
|
||||
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(DescribeMaterializedViewCmd(None, "toto")), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "parse describe materialized view with keyspace" in {
|
||||
val queries ="Describe materialized view ks.toto;"
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, queries)
|
||||
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(DescribeMaterializedViewCmd(Some("ks"), "toto")), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "fail parsing describe materialized view" in {
|
||||
val queries ="Describe materialized view toto"
|
||||
|
||||
val ex = intercept[InterpreterException] {
|
||||
parser.parseAll(parser.queries, queries)
|
||||
}
|
||||
ex.getMessage should be(s"Invalid syntax for DESCRIBE MATERIALIZED VIEW. It should comply to the patterns: " +
|
||||
s"${DESCRIBE_MATERIALIZED_VIEW_WITH_KEYSPACE_PATTERN.toString} or ${DESCRIBE_MATERIALIZED_VIEW_PATTERN.toString}")
|
||||
}
|
||||
|
||||
"Parser" should "parse describe materialized views" in {
|
||||
val queries ="Describe materialized views;"
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, queries)
|
||||
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(DescribeMaterializedViewsCmd("DESCRIBE MATERIALIZED VIEWS;")), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "fail parsing describe materialized views" in {
|
||||
val queries ="Describe materialized views toto"
|
||||
|
||||
val ex = intercept[InterpreterException] {
|
||||
parser.parseAll(parser.queries, queries)
|
||||
}
|
||||
ex.getMessage should be(s"Invalid syntax for DESCRIBE MATERIALIZED VIEWS. It should comply to the pattern: " +
|
||||
s"${DESCRIBE_MATERIALIZED_VIEWS_PATTERN.toString}")
|
||||
}
|
||||
|
||||
"Parser" should "parse help" in {
|
||||
val queries ="hElp;"
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, queries)
|
||||
|
||||
parsed.get(0) shouldBe a [HelpCmd]
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(HelpCmd("HELP;")), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "fail parsing help" in {
|
||||
|
|
@ -582,4 +802,146 @@ class ParagraphParserTest extends FlatSpec
|
|||
ex.getMessage should be(s"Invalid syntax for HELP. It should comply to the patterns: " +
|
||||
s"${HELP_PATTERN.toString}")
|
||||
}
|
||||
|
||||
"Parser" should "parse CREATE FUNCTION" in {
|
||||
val query = "CREATE FUNCTION keyspace.udf xxx AS 'return true;';"
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, query)
|
||||
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(SimpleStm(query)), _) =>
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
"Parser" should "parse CREATE OR REPLACE FUNCTION" in {
|
||||
val query = "CREATE or Replace FUNCTION keyspace.udf xxx AS 'return true;';"
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, query)
|
||||
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(SimpleStm(query)), _) =>
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
"Parser" should "parse CREATE FUNCTION IF NOT Exists" in {
|
||||
val query = "CREATE FUNCTION IF NOT EXISTS keyspace.udf xxx AS 'return true;';"
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, query)
|
||||
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(SimpleStm(query)), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "parse CREATE FUNCTION multiline with simple quote" in {
|
||||
val query =
|
||||
"""CREATE FUNCTION IF NOT EXISTS keyspace.udf(input text) xxx
|
||||
| CALLED ON NULL INPUT
|
||||
| RETURN text
|
||||
| LANGUAGE java
|
||||
| AS '
|
||||
| return input.toLowerCase("abc");
|
||||
| ';""".stripMargin
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, query)
|
||||
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(SimpleStm(query)), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "parse CREATE FUNCTION multiline with double dollar" in {
|
||||
val query =
|
||||
"""CREATE FUNCTION IF NOT EXISTS keyspace.udf(input text) xxx
|
||||
| CALLED ON NULL INPUT
|
||||
| RETURN text
|
||||
| LANGUAGE java
|
||||
| AS $$
|
||||
| return input.toLowerCase("abc");
|
||||
| $$;""".stripMargin
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, query)
|
||||
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(SimpleStm(query)), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "parse CREATE FUNCTION multiline with SELECT" in {
|
||||
|
||||
val udf =
|
||||
"""CREATE FUNCTION IF NOT EXISTS keyspace.udf(input text) xxx
|
||||
| CALLED ON NULL INPUT
|
||||
| RETURN text
|
||||
| LANGUAGE java
|
||||
| AS '
|
||||
| return input.toLowerCase("abc");
|
||||
| ';""".stripMargin
|
||||
|
||||
val select = "SELECT udf(val) from table WHERe id=1;"
|
||||
val queries =
|
||||
s"""$udf
|
||||
|$select
|
||||
""".stripMargin
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, queries)
|
||||
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(SimpleStm(udf), SimpleStm(select)), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "parse CREATE multiple FUNCTIONS" in {
|
||||
|
||||
val udf1 =
|
||||
"""CREATE FUNCTION IF NOT EXISTS keyspace.udf(input text) xxx
|
||||
| CALLED ON NULL INPUT
|
||||
| RETURN text
|
||||
| LANGUAGE java
|
||||
| AS '
|
||||
| return input.toLowerCase("abc");
|
||||
| ';""".stripMargin
|
||||
|
||||
val select = "SELECT * FROM keyspace.table;"
|
||||
|
||||
val udf2 = """CREATE FUNCTION IF NOT EXISTS keyspace.maxOf(val1 int, val2 int)
|
||||
| CALLED ON NULL INPUT
|
||||
| RETURN text
|
||||
| LANGUAGE java
|
||||
| AS '
|
||||
| return Math.max(val1,val2);
|
||||
| ';""".stripMargin
|
||||
|
||||
val queries =
|
||||
s"""$udf1
|
||||
|
|
||||
|$select
|
||||
|
|
||||
|$udf2
|
||||
""".stripMargin
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, queries)
|
||||
|
||||
parsed.get.size should be(3)
|
||||
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(SimpleStm(udf1), SimpleStm(select), SimpleStm(udf2)), _) =>
|
||||
}
|
||||
}
|
||||
|
||||
"Parser" should "parse CREATE Materialized View" in {
|
||||
val query =
|
||||
"""CREATE MATERIALIZED VIEW xxx
|
||||
| AS SELECT * FROM myTable
|
||||
| WHERE partition IS NOT NULL
|
||||
| PRIMARY KEY(col, partition);""".stripMargin
|
||||
|
||||
val parsed = parser.parseAll(parser.queries, query)
|
||||
|
||||
parsed should matchPattern {
|
||||
case parser.Success(List(SimpleStm(query)), _) =>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,9 @@
|
|||
# List of users with their password allowed to access Zeppelin.
|
||||
# To use a different strategy (LDAP / Database / ...) check the shiro doc at http://shiro.apache.org/configuration.html#Configuration-INISections
|
||||
admin = password1
|
||||
user1 = password2
|
||||
user2 = password3
|
||||
user1 = password2, role1, role2
|
||||
user2 = password3, role3
|
||||
user3 = password4, role2
|
||||
|
||||
# Sample LDAP configuration, for user Authentication, currently tested for single Realm
|
||||
[main]
|
||||
|
|
|
|||
|
|
@ -29,10 +29,11 @@
|
|||
# export ZEPPELIN_NOTEBOOK_DIR # Where notebook saved
|
||||
# export ZEPPELIN_NOTEBOOK_HOMESCREEN # Id of notebook to be displayed in homescreen. ex) 2A94M5J1Z
|
||||
# export ZEPPELIN_NOTEBOOK_HOMESCREEN_HIDE # hide homescreen notebook from list when this value set to "true". default "false"
|
||||
# export ZEPPELIN_NOTEBOOK_S3_BUCKET # Bucket where notebook saved
|
||||
# export ZEPPELIN_NOTEBOOK_S3_USER # User in bucket where notebook saved. For example bucket/user/notebook/2A94M5J1Z/note.json
|
||||
# export ZEPPELIN_NOTEBOOK_S3_BUCKET # Bucket where notebook saved
|
||||
# export ZEPPELIN_NOTEBOOK_S3_USER # User in bucket where notebook saved. For example bucket/user/notebook/2A94M5J1Z/note.json
|
||||
# export ZEPPELIN_IDENT_STRING # A string representing this instance of zeppelin. $USER by default.
|
||||
# export ZEPPELIN_NICENESS # The scheduling priority for daemons. Defaults to 0.
|
||||
# export ZEPPELIN_INTERPRETER_LOCALREPO # Local repository for interpreter's additional dependency loading
|
||||
|
||||
|
||||
#### Spark interpreter configuration ####
|
||||
|
|
@ -42,6 +43,7 @@
|
|||
##
|
||||
# export SPARK_HOME # (required) When it is defined, load it instead of Zeppelin embedded Spark libraries
|
||||
# export SPARK_SUBMIT_OPTIONS # (optional) extra options to pass to spark submit. eg) "--driver-memory 512M --executor-memory 1G".
|
||||
# export SPARK_APP_NAME # (optional) The name of spark application.
|
||||
|
||||
## Use embedded spark binaries ##
|
||||
## without SPARK_HOME defined, Zeppelin still able to run spark interpreter process using embedded spark binaries.
|
||||
|
|
|
|||
|
|
@ -83,6 +83,33 @@
|
|||
</property>
|
||||
-->
|
||||
|
||||
<!-- If using Azure for storage use the following settings -->
|
||||
<!--
|
||||
<property>
|
||||
<name>zeppelin.notebook.azure.user</name>
|
||||
<value>user</value>
|
||||
<description>optional user name for Azure folder structure</description>
|
||||
</property>
|
||||
|
||||
<property>
|
||||
<name>zeppelin.notebook.azure.share</name>
|
||||
<value>zeppelin</value>
|
||||
<description>share name for notebook storage</description>
|
||||
</property>
|
||||
|
||||
<property>
|
||||
<name>zeppelin.notebook.azure.connectionString</name>
|
||||
<value>DefaultEndpointsProtocol=https;AccountName=<accountName>;AccountKey=<accountKey></value>
|
||||
<description>share name for notebook storage</description>
|
||||
</property>
|
||||
|
||||
<property>
|
||||
<name>zeppelin.notebook.storage</name>
|
||||
<value>org.apache.zeppelin.notebook.repo.AzureNotebookRepo</value>
|
||||
<description>notebook persistence layer implementation</description>
|
||||
</property>
|
||||
-->
|
||||
|
||||
<!-- For versioning your local norebook storage using Git repository
|
||||
<property>
|
||||
<name>zeppelin.notebook.storage</name>
|
||||
|
|
@ -103,9 +130,15 @@
|
|||
<description>Interpreter implementation base directory</description>
|
||||
</property>
|
||||
|
||||
<property>
|
||||
<name>zeppelin.interpreter.localRepo</name>
|
||||
<value>local-repo</value>
|
||||
<description>Local repository for interpreter's additional dependency loading</description>
|
||||
</property>
|
||||
|
||||
<property>
|
||||
<name>zeppelin.interpreters</name>
|
||||
<value>org.apache.zeppelin.spark.SparkInterpreter,org.apache.zeppelin.spark.PySparkInterpreter,org.apache.zeppelin.spark.SparkSqlInterpreter,org.apache.zeppelin.spark.DepInterpreter,org.apache.zeppelin.markdown.Markdown,org.apache.zeppelin.angular.AngularInterpreter,org.apache.zeppelin.shell.ShellInterpreter,org.apache.zeppelin.hive.HiveInterpreter,org.apache.zeppelin.tajo.TajoInterpreter,org.apache.zeppelin.flink.FlinkInterpreter,org.apache.zeppelin.lens.LensInterpreter,org.apache.zeppelin.ignite.IgniteInterpreter,org.apache.zeppelin.ignite.IgniteSqlInterpreter,org.apache.zeppelin.cassandra.CassandraInterpreter,org.apache.zeppelin.geode.GeodeOqlInterpreter,org.apache.zeppelin.postgresql.PostgreSqlInterpreter,org.apache.zeppelin.jdbc.JDBCInterpreter,org.apache.zeppelin.phoenix.PhoenixInterpreter,org.apache.zeppelin.kylin.KylinInterpreter,org.apache.zeppelin.elasticsearch.ElasticsearchInterpreter,org.apache.zeppelin.scalding.ScaldingInterpreter</value>
|
||||
<value>org.apache.zeppelin.spark.SparkInterpreter,org.apache.zeppelin.spark.PySparkInterpreter,org.apache.zeppelin.spark.SparkSqlInterpreter,org.apache.zeppelin.spark.DepInterpreter,org.apache.zeppelin.markdown.Markdown,org.apache.zeppelin.angular.AngularInterpreter,org.apache.zeppelin.shell.ShellInterpreter,org.apache.zeppelin.hive.HiveInterpreter,org.apache.zeppelin.tajo.TajoInterpreter,org.apache.zeppelin.flink.FlinkInterpreter,org.apache.zeppelin.lens.LensInterpreter,org.apache.zeppelin.ignite.IgniteInterpreter,org.apache.zeppelin.ignite.IgniteSqlInterpreter,org.apache.zeppelin.cassandra.CassandraInterpreter,org.apache.zeppelin.geode.GeodeOqlInterpreter,org.apache.zeppelin.postgresql.PostgreSqlInterpreter,org.apache.zeppelin.jdbc.JDBCInterpreter,org.apache.zeppelin.phoenix.PhoenixInterpreter,org.apache.zeppelin.kylin.KylinInterpreter,org.apache.zeppelin.elasticsearch.ElasticsearchInterpreter,org.apache.zeppelin.scalding.ScaldingInterpreter,org.apache.zeppelin.alluxio.AlluxioInterpreter,org.apache.zeppelin.hbase.HbaseInterpreter</value>
|
||||
<description>Comma separated interpreter configurations. First interpreter become a default</description>
|
||||
</property>
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ except ImportError:
|
|||
|
||||
# Location of your Zeppelin git development area
|
||||
ZEPPELIN_HOME = os.environ.get("ZEPPELIN_HOME", os.getcwd())
|
||||
# Remote name which points to the Gihub site
|
||||
# Remote name which points to the Github site
|
||||
PR_REMOTE_NAME = os.environ.get("PR_REMOTE_NAME", "apache-github")
|
||||
# Remote name which points to Apache git
|
||||
PUSH_REMOTE_NAME = os.environ.get("PUSH_REMOTE_NAME", "apache")
|
||||
|
|
@ -352,5 +352,5 @@ if JIRA_IMPORTED:
|
|||
print "JIRA_USERNAME and JIRA_PASSWORD not set"
|
||||
print "Exiting without trying to close the associated JIRA."
|
||||
else:
|
||||
print "Could not find jira-python library. Run 'sudo pip install jira-python' to install."
|
||||
print "Could not find jira library. Run 'sudo pip install jira' to install."
|
||||
print "Exiting without trying to close the associated JIRA."
|
||||
|
|
|
|||
|
|
@ -8,10 +8,13 @@ See https://help.github.com/articles/using-jekyll-with-pages#installing-jekyll
|
|||
|
||||
**tl;dr version:**
|
||||
|
||||
```
|
||||
ruby --version >= 1.9.3
|
||||
gem install bundler
|
||||
# go to /docs under your Zeppelin source
|
||||
bundle install
|
||||
|
||||
```
|
||||
|
||||
*On OS X 10.9 you may need to do "xcode-select --install"*
|
||||
|
||||
|
||||
|
|
@ -20,28 +23,28 @@ See https://help.github.com/articles/using-jekyll-with-pages#installing-jekyll
|
|||
bundle exec jekyll serve --watch
|
||||
|
||||
|
||||
## Deploy to ASF svnpubsub infra (commiters only)
|
||||
1. generate static website in `./_site`
|
||||
```
|
||||
bundle exec jekyll build --safe
|
||||
```
|
||||
|
||||
2. checkout ASF repo
|
||||
```
|
||||
svn co https://svn.apache.org/repos/asf/incubator/zeppelin asf-zepplelin
|
||||
```
|
||||
3. copy zeppelin/_site to asf-zepplelin/site/docs/[VERSION]
|
||||
4. ```svn commit```
|
||||
|
||||
## Adding a new page
|
||||
|
||||
rake page name="new-page.md"
|
||||
|
||||
|
||||
## Bumping up version in a new release
|
||||
|
||||
## Bumping up version
|
||||
* `ZEPPELIN_VERSION` and `BASE_PATH` property in _config.yml
|
||||
* `Zeppelin <small>([VERSION])</small>` in _includes/themes/zeppelin/_navigation.html
|
||||
should be updated
|
||||
|
||||
* `BASE_PATH` property in _config.yml
|
||||
* `ZEPPELIN <small>([VERSION])</small>` in _includes/themes/zeppelin/_navigation.html
|
||||
|
||||
need to be updated
|
||||
## Deploy to ASF svnpubsub infra (for committers only)
|
||||
1. generate static website in `./_site`
|
||||
```
|
||||
# go to /docs under Zeppelin source
|
||||
bundle exec jekyll build --safe
|
||||
```
|
||||
|
||||
2. checkout ASF repo
|
||||
```
|
||||
svn co https://svn.apache.org/repos/asf/incubator/zeppelin asf-zeppelin
|
||||
```
|
||||
3. copy `zeppelin/docs/_site` to `asf-zeppelin/site/docs/[VERSION]`
|
||||
4. ```svn commit```
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ author :
|
|||
twitter : ASF
|
||||
feedburner : feedname
|
||||
|
||||
ZEPPELIN_VERSION : 0.6.0-incubating-SNAPSHOT
|
||||
|
||||
# The production_url is only used when full-domain names are needed
|
||||
# such as sitemap.txt
|
||||
# Most places will/should use BASE_PATH to make the urls
|
||||
|
|
|
|||
|
|
@ -26,32 +26,40 @@
|
|||
<li><a href="{{BASE_PATH}}/install/yarn_install.html">YARN Install</a></li>
|
||||
<li><a href="{{BASE_PATH}}/install/virtual_machine.html">Virtual Machine Install</a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a href="{{BASE_PATH}}/install/upgrade.html">Upgrade Version</a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<!-- li><span><b>Tutorial</b><span></li -->
|
||||
<li><a href="{{BASE_PATH}}/tutorial/tutorial.html">Tutorial</a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<!-- li><span><b>Guide</b><span></li -->
|
||||
<li><a href="{{BASE_PATH}}/manual/dynamicform.html">Dynamic Form</a></li>
|
||||
<li><a href="{{BASE_PATH}}/manual/publish.html">Publish your Paragraph</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" data-toggle="dropdown" class="dropdown-toggle">Interpreter <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{{BASE_PATH}}/manual/interpreters.html">Overview</a></li>
|
||||
<li><a href="{{BASE_PATH}}/manual/dynamicinterpreterload.html">Dynamic Interpreter Loading</a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a href="{{BASE_PATH}}/interpreter/cassandra.html">Cassandra</a></li>
|
||||
<li><a href="{{BASE_PATH}}/interpreter/elasticsearch.html">Elasticsearch</a></li>
|
||||
<li><a href="{{BASE_PATH}}/interpreter/flink.html">Flink</a></li>
|
||||
<li><a href="{{BASE_PATH}}/interpreter/geode.html">Geode</a></li>
|
||||
<li><a href="{{BASE_PATH}}/interpreter/hbase.html">HBase</a></li>
|
||||
<li><a href="{{BASE_PATH}}/interpreter/hive.html">Hive</a></li>
|
||||
<li><a href="{{BASE_PATH}}/interpreter/ignite.html">Ignite</a></li>
|
||||
<li><a href="{{BASE_PATH}}/interpreter/jdbc.html">JDBC</a></li>
|
||||
<li><a href="{{BASE_PATH}}/interpreter/lens.html">Lens</a></li>
|
||||
<li><a href="{{BASE_PATH}}/interpreter/markdown.html">Markdown</a></li>
|
||||
<li><a href="{{BASE_PATH}}/interpreter/postgresql.html">Postgresql, hawq</a></li>
|
||||
<li><a href="{{BASE_PATH}}/interpreter/scalding.html">Scalding</a></li>
|
||||
<li><a href="{{BASE_PATH}}/pleasecontribute.html">Shell</a></li>
|
||||
<li><a href="{{BASE_PATH}}/interpreter/spark.html">Spark</a></li>
|
||||
<li><a href="{{BASE_PATH}}/interpreter/alluxio.html">Alluxio</a></li>
|
||||
<li><a href="{{BASE_PATH}}/pleasecontribute.html">Tajo</a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a href="{{BASE_PATH}}/manual/dynamicinterpreterload.html">Dynamic Interpreter Loading</a></li>
|
||||
<li><a href="{{BASE_PATH}}/manual/dependencymanagement.html">Interpreter Dependency Management</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
|
|
@ -76,14 +84,24 @@
|
|||
<!-- li><span><b>REST API</b><span></li -->
|
||||
<li><a href="{{BASE_PATH}}/rest-api/rest-interpreter.html">Interpreter API</a></li>
|
||||
<li><a href="{{BASE_PATH}}/rest-api/rest-notebook.html">Notebook API</a></li>
|
||||
<li><a href="{{BASE_PATH}}/rest-api/rest-configuration.html">Configuration API</a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<!-- li><span><b>Security</b><span></li -->
|
||||
<li><a href="{{BASE_PATH}}/security/overview.html">Security Overview</a></li>
|
||||
<li><a href="{{BASE_PATH}}/security/authentication.html">Authentication</a></li>
|
||||
<li><a href="{{BASE_PATH}}/security/notebook_authorization.html">Notebook Authorization</a></li>
|
||||
<li><a href="{{BASE_PATH}}/security/interpreter_authorization.html">Interpreter Authorization</a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<!-- li><span><b>Development</b><span></li -->
|
||||
<li><a href="{{BASE_PATH}}/development/writingzeppelininterpreter.html">Writing Zeppelin Interpreter</a></li>
|
||||
<li><a href="{{BASE_PATH}}/development/howtocontribute.html">How to contribute (code)</a></li>
|
||||
<li><a href="{{BASE_PATH}}/development/howtocontributewebsite.html">How to contribute (website)</a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<!-- li><span><b>Shiro Security</b><span></li -->
|
||||
<li><a href="{{BASE_PATH}}/manual/shiroauthentication.html">Shiro Authentication</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav><!--/.navbar-collapse -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -11,9 +11,11 @@
|
|||
|
||||
<!-- Le HTML5 shim, for IE6-8 support of HTML elements -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Le styles -->
|
||||
<link href="{{ ASSET_PATH }}/bootstrap/css/bootstrap.css" rel="stylesheet">
|
||||
<link href="{{ ASSET_PATH }}/css/style.css?body=1" rel="stylesheet" type="text/css">
|
||||
|
|
|
|||
BIN
docs/assets/themes/zeppelin/img/docs-img/alluxio-example.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
docs/assets/themes/zeppelin/img/docs-img/basic-usage-angular.png
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
docs/assets/themes/zeppelin/img/docs-img/copy-the-link.png
Normal file
|
After Width: | Height: | Size: 185 KiB |
|
After Width: | Height: | Size: 298 KiB |
|
After Width: | Height: | Size: 328 KiB |
|
After Width: | Height: | Size: 369 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 80 KiB |
BIN
docs/assets/themes/zeppelin/img/docs-img/kdc_zeppelin.png
Normal file
|
After Width: | Height: | Size: 7 KiB |
BIN
docs/assets/themes/zeppelin/img/docs-img/link-the-paragraph.png
Normal file
|
After Width: | Height: | Size: 311 KiB |
|
After Width: | Height: | Size: 133 KiB |
BIN
docs/assets/themes/zeppelin/img/docs-img/your-website.png
Normal file
|
After Width: | Height: | Size: 425 KiB |
BIN
docs/assets/themes/zeppelin/img/docs-img/zeppelin-login.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
docs/assets/themes/zeppelin/img/screenshots/form_checkbox.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 227 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
|
@ -5,52 +5,49 @@ description: "How to contribute"
|
|||
group: development
|
||||
---
|
||||
|
||||
# Contributing to Apache Zeppelin ( Code )
|
||||
|
||||
## IMPORTANT
|
||||
|
||||
Apache Zeppelin (incubating) is an [Apache2 License](http://www.apache.org/licenses/LICENSE-2.0.html) Software.
|
||||
Any contribution to Zeppelin (Source code, Documents, Image, Website) means you agree license all your contributions as Apache2 License.
|
||||
Any contributions to Zeppelin (Source code, Documents, Image, Website) means you agree with license all your contributions as Apache2 License.
|
||||
|
||||
## Setting up
|
||||
Here are some tools you will need to build and test Zeppelin.
|
||||
|
||||
#### Software Configuration Management ( SCM )
|
||||
|
||||
### Setting up
|
||||
Here are some things you will need to do to build and test Zeppelin.
|
||||
Since Zeppelin uses Git for it's SCM system, you need git client installed in your development machine.
|
||||
|
||||
#### Software Configuration Management(SCM)
|
||||
|
||||
Zeppelin uses Git for it's SCM system. Hosted by github.com. `https://github.com/apache/incubator-zeppelin` You'll need git client installed in your development machine.
|
||||
|
||||
#### Integrated Development Environment(IDE)
|
||||
#### Integrated Development Environment ( IDE )
|
||||
|
||||
You are free to use whatever IDE you prefer, or your favorite command line editor.
|
||||
|
||||
#### Build Tools
|
||||
### Build Tools
|
||||
|
||||
To build the code, install
|
||||
Oracle Java 7
|
||||
Apache Maven
|
||||
|
||||
* Oracle Java 7
|
||||
* Apache Maven
|
||||
|
||||
### Getting the source code
|
||||
First of all, you need the Zeppelin source code. The official location for Zeppelin is [https://github.com/apache/incubator-zeppelin](https://github.com/apache/incubator-zeppelin)
|
||||
## Getting the source code
|
||||
First of all, you need Zeppelin source code. The official location of Zeppelin is [http://git.apache.org/incubator-zeppelin.git](http://git.apache.org/incubator-zeppelin.git).
|
||||
|
||||
#### git access
|
||||
### git access
|
||||
|
||||
Get the source code on your development machine using git.
|
||||
|
||||
```
|
||||
git clone https://github.com/apache/incubator-zeppelin.git zeppelin
|
||||
git clone git://git.apache.org/incubator-zeppelin.git zeppelin
|
||||
```
|
||||
|
||||
You may also want to develop against a specific release. For example, for branch-0.1
|
||||
You may also want to develop against a specific branch. For example, for branch-0.5.6
|
||||
|
||||
```
|
||||
git clone -b branch-0.1 https://github.com/apache/incubator-zeppelin.git zeppelin
|
||||
git clone -b branch-0.5.6 git://git.apache.org/incubator-zeppelin.git zeppelin
|
||||
```
|
||||
|
||||
|
||||
#### Fork repository
|
||||
|
||||
If you want not only build Zeppelin but also make changes, then you need to fork Zeppelin repository and make pull request.
|
||||
|
||||
If you want not only build Zeppelin but also make any changes, then you need fork [Zeppelin github mirror repository](https://github.com/apache/incubator-zeppelin) and make a pull request.
|
||||
|
||||
###Build
|
||||
|
||||
|
|
@ -67,7 +64,7 @@ mvn install -DskipTests
|
|||
To build with specific spark / hadoop version
|
||||
|
||||
```
|
||||
mvn install -Dspark.version=1.0.1 -Dhadoop.version=2.2.0
|
||||
mvn install -Dspark.version=x.x.x -Dhadoop.version=x.x.x
|
||||
```
|
||||
|
||||
### Run Zeppelin server in development mode
|
||||
|
|
@ -76,7 +73,8 @@ mvn install -Dspark.version=1.0.1 -Dhadoop.version=2.2.0
|
|||
cd zeppelin-server
|
||||
HADOOP_HOME=YOUR_HADOOP_HOME JAVA_HOME=YOUR_JAVA_HOME mvn exec:java -Dexec.mainClass="org.apache.zeppelin.server.ZeppelinServer" -Dexec.args=""
|
||||
```
|
||||
NOTE: make sure you first run ```mvn clean install -DskipTests``` on your zeppelin root directory otherwise your server build will fail to find the required dependencies in the local repro
|
||||
|
||||
> **Note:** Make sure you first run ```mvn clean install -DskipTests``` on your zeppelin root directory, otherwise your server build will fail to find the required dependencies in the local repro.
|
||||
|
||||
or use daemon script
|
||||
|
||||
|
|
@ -84,15 +82,13 @@ or use daemon script
|
|||
bin/zeppelin-daemon start
|
||||
```
|
||||
|
||||
|
||||
Server will be run on http://localhost:8080
|
||||
|
||||
Server will be run on [http://localhost:8080](http://localhost:8080).
|
||||
|
||||
### Generating Thrift Code
|
||||
|
||||
Some portions of the Zeppelin code are generated by [Thrift](http://thrift.apache.org). For most Zeppelin changes, you don't need to worry about this, but if you modify any of the Thrift IDL files (e.g. zeppelin-interpreter/src/main/thrift/*.thrift), then you also need to regenerate these files and submit their updated version as part of your patch.
|
||||
Some portions of the Zeppelin code are generated by [Thrift](http://thrift.apache.org). For most Zeppelin changes, you don't need to worry about this. But if you modify any of the Thrift IDL files (e.g. zeppelin-interpreter/src/main/thrift/*.thrift), then you also need to regenerate these files and submit their updated version as part of your patch.
|
||||
|
||||
To regenerate the code, install thrift-0.9.0 and change directory into Zeppelin source directory. and then run following command
|
||||
To regenerate the code, install **thrift-0.9.0** and change directory into Zeppelin source directory. and then run following command
|
||||
|
||||
|
||||
```
|
||||
|
|
@ -100,10 +96,10 @@ thrift -out zeppelin-interpreter/src/main/java/ --gen java zeppelin-interpreter/
|
|||
```
|
||||
|
||||
|
||||
### JIRA
|
||||
## JIRA
|
||||
Zeppelin manages its issues in Jira. [https://issues.apache.org/jira/browse/ZEPPELIN](https://issues.apache.org/jira/browse/ZEPPELIN)
|
||||
|
||||
### Stay involved
|
||||
## Stay involved
|
||||
Contributors should join the Zeppelin mailing lists.
|
||||
|
||||
* [dev@zeppelin.incubator.apache.org](http://mail-archives.apache.org/mod_mbox/incubator-zeppelin-dev/) is for people who want to contribute code to Zeppelin. [subscribe](mailto:dev-subscribe@zeppelin.incubator.apache.org?subject=send this email to subscribe), [unsubscribe](mailto:dev-unsubscribe@zeppelin.incubator.apache.org?subject=send this email to unsubscribe), [archives](http://mail-archives.apache.org/mod_mbox/incubator-zeppelin-dev/)
|
||||
|
|
|
|||
|
|
@ -5,62 +5,51 @@ description: "How to contribute (website)"
|
|||
group: development
|
||||
---
|
||||
|
||||
## IMPORTANT
|
||||
# Contributing to Apache Zeppelin ( Website )
|
||||
|
||||
## IMPORTANT
|
||||
Apache Zeppelin (incubating) is an [Apache2 License](http://www.apache.org/licenses/LICENSE-2.0.html) Software.
|
||||
Any contribution to Zeppelin (Source code, Documents, Image, Website) means you agree license all your contributions as Apache2 License.
|
||||
|
||||
|
||||
## Modifying the website
|
||||
|
||||
### Modifying the website
|
||||
|
||||
|
||||
<br />
|
||||
#### Getting the source code
|
||||
Website is hosted in 'master' branch under `/docs/` dir.
|
||||
|
||||
First of all, you need the website source code. The official location of mirror for Zeppelin is [https://github.com/apache/incubator-zeppelin](https://github.com/apache/incubator-zeppelin).
|
||||
First of all, you need the website source code. The official location of mirror for Zeppelin is [http://git.apache.org/incubator-zeppelin.git](http://git.apache.org/incubator-zeppelin.git).
|
||||
|
||||
Get the source code on your development machine using git.
|
||||
|
||||
```
|
||||
git clone https://github.com/apache/incubator-zeppelin.git
|
||||
git clone git://git.apache.org/incubator-zeppelin.git
|
||||
cd docs
|
||||
```
|
||||
|
||||
<br />
|
||||
#### Build
|
||||
|
||||
To build, you'll need to install some prerequisites.
|
||||
To build, you'll need to install some prerequisites. Please check 'Build documentation' section in [docs/README.md](https://github.com/apache/incubator-zeppelin/blob/master/docs/README.md#build-documentation).
|
||||
|
||||
Please check 'Build' section on [docs/README.md](https://github.com/apache/incubator-zeppelin/blob/master/docs/README.md#build)
|
||||
|
||||
<br />
|
||||
#### Run website in development mode
|
||||
|
||||
While you're modifying website, you'll want to see preview of it.
|
||||
While you're modifying website, you'll want to see preview of it. Please check 'Run website' section in [docs/README.md](https://github.com/apache/incubator-zeppelin/blob/master/docs/README.md#run-website).
|
||||
|
||||
Please check 'Run' section on [docs/README.md](https://github.com/apache/incubator-zeppelin/blob/master/docs/README.md#run)
|
||||
You'll be able to access it on [http://localhost:4000](http://localhost:4000) with your web browser.
|
||||
|
||||
You'll be able to access it on localhost:4000 with your webbrowser.
|
||||
#### Making a Pull Request
|
||||
|
||||
<br />
|
||||
#### Pull request
|
||||
|
||||
When you're ready, just make a pull-request.
|
||||
When you are ready, just make a pull-request.
|
||||
|
||||
|
||||
<br />
|
||||
### Alternative way
|
||||
## Alternative way
|
||||
|
||||
You can directly edit .md files in `/docs/` dir at github's web interface and make pull-request immediatly.
|
||||
|
||||
|
||||
<br />
|
||||
### JIRA
|
||||
## JIRA
|
||||
Zeppelin manages its issues in Jira. [https://issues.apache.org/jira/browse/ZEPPELIN](https://issues.apache.org/jira/browse/ZEPPELIN)
|
||||
|
||||
### Stay involved
|
||||
## Stay involved
|
||||
Contributors should join the Zeppelin mailing lists.
|
||||
|
||||
* [dev@zeppelin.incubator.apache.org](http://mail-archives.apache.org/mod_mbox/incubator-zeppelin-dev/) is for people who want to contribute code to Zeppelin. [subscribe](mailto:dev-subscribe@zeppelin.incubator.apache.org?subject=send this email to subscribe), [unsubscribe](mailto:dev-unsubscribe@zeppelin.incubator.apache.org?subject=send this email to unsubscribe), [archives](http://mail-archives.apache.org/mod_mbox/incubator-zeppelin-dev/)
|
||||
|
|
|
|||
|
|
@ -22,12 +22,16 @@ limitations under the License.
|
|||
### What is Zeppelin Interpreter
|
||||
|
||||
Zeppelin Interpreter is a language backend. For example to use scala code in Zeppelin, you need scala interpreter.
|
||||
Every Interpreter belongs to an InterpreterGroup. InterpreterGroup is a unit of start/stop interpreter.
|
||||
Interpreters in the same InterpreterGroup can reference each other. For example, SparkSqlInterpreter can reference SparkInterpreter to get SparkContext from it while they're in the same group.
|
||||
Every Interpreter belongs to an InterpreterGroup.
|
||||
Interpreters in the same InterpreterGroup can reference each other. For example, SparkSqlInterpreter can reference SparkInterpreter to get SparkContext from it while they're in the same group.
|
||||
|
||||
<img class="img-responsive" style="width:50%; border: 1px solid #ecf0f1;" height="auto" src="/assets/themes/zeppelin/img/interpreter.png" />
|
||||
|
||||
All Interpreters in the same interpreter group are launched in a single, separate JVM process. The Interpreter communicates with Zeppelin engine via thrift.
|
||||
InterpreterSetting is configuration of a given InterpreterGroup and a unit of start/stop interpreter.
|
||||
All Interpreters in the same InterpreterSetting are launched in a single, separate JVM process. The Interpreter communicates with Zeppelin engine via thrift.
|
||||
|
||||
In 'Separate Interpreter for each note' mode, new Interpreter instance will be created per notebook. But it still runs on the same JVM while they're in the same InterpreterSettings.
|
||||
|
||||
|
||||
### Make your own Interpreter
|
||||
|
||||
|
|
@ -48,6 +52,18 @@ The name of the interpreter is what you later write to identify a paragraph whic
|
|||
%MyInterpreterName
|
||||
some interpreter specific code...
|
||||
```
|
||||
|
||||
### Programming Languages for Interpreter
|
||||
If the interpreter uses a specific programming language ( like Scala, Python, SQL ), it is generally recommended to add a syntax highlighting supported for that to the notebook paragraph editor.
|
||||
|
||||
To check out the list of languages supported, see the `mode-*.js` files under `zeppelin-web/bower_components/ace-builds/src-noconflict` or from [github.com/ajaxorg/ace-builds](https://github.com/ajaxorg/ace-builds/tree/master/src-noconflict).
|
||||
|
||||
If you want to add a new set of syntax highlighting,
|
||||
|
||||
1. Add the `mode-*.js` file to `zeppelin-web/bower.json` ( when built, `zeppelin-web/src/index.html` will be changed automatically. ).
|
||||
2. Add to the list of `editorMode` in `zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js` - it follows the pattern 'ace/mode/x' where x is the name.
|
||||
3. Add to the code that checks for `%` prefix and calls `session.setMode(editorMode.x)` in `setParagraphMode` located in `zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js`.
|
||||
|
||||
### Install your interpreter binary
|
||||
|
||||
Once you have built your interpreter, you can place it under the interpreter directory with all its dependencies.
|
||||
|
|
@ -64,7 +80,7 @@ To configure your interpreter you need to follow these steps:
|
|||
|
||||
Property value is comma separated [INTERPRETER\_CLASS\_NAME].
|
||||
For example,
|
||||
|
||||
|
||||
```
|
||||
<property>
|
||||
<name>zeppelin.interpreters</name>
|
||||
|
|
@ -79,7 +95,7 @@ To configure your interpreter you need to follow these steps:
|
|||
4. In the interpreter page, click the `+Create` button and configure your interpreter properties.
|
||||
Now you are done and ready to use your interpreter.
|
||||
|
||||
Note that the interpreters shipped with zeppelin have a [default configuration](https://github.com/apache/incubator-zeppelin/blob/master/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java#L397) which is used when there is no `conf/zeppelin-site.xml`.
|
||||
Note that the interpreters released with zeppelin have a [default configuration](https://github.com/apache/incubator-zeppelin/blob/master/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java#L397) which is used when there is no `conf/zeppelin-site.xml`.
|
||||
|
||||
### Use your interpreter
|
||||
|
||||
|
|
@ -138,15 +154,24 @@ You can only omit your interpreter group when your interpreter group is selected
|
|||
codes for myintp2
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### Examples
|
||||
|
||||
Check some interpreters shipped by default.
|
||||
Checkout some interpreters released with Zeppelin by default.
|
||||
|
||||
- [spark](https://github.com/apache/incubator-zeppelin/tree/master/spark)
|
||||
- [markdown](https://github.com/apache/incubator-zeppelin/tree/master/markdown)
|
||||
- [shell](https://github.com/apache/incubator-zeppelin/tree/master/shell)
|
||||
- [hive](https://github.com/apache/incubator-zeppelin/tree/master/hive)
|
||||
|
||||
### Contributing a new Interpreter to Zeppelin releases
|
||||
|
||||
We welcome contribution to a new interpreter. Please follow these few steps:
|
||||
|
||||
- First, check out the general contribution guide [here](./howtocontributewebsite.html).
|
||||
- Follow the steps in "Make your own Interpreter" section above.
|
||||
- Add your interpreter as in the "Configure your interpreter" section above; also add it to the example template [zeppelin-site.xml.template](https://github.com/apache/incubator-zeppelin/blob/master/conf/zeppelin-site.xml.template).
|
||||
- Add tests! They are run by Travis for all changes and it is important that they are self-contained.
|
||||
- Include your interpreter as a module in [`pom.xml`](https://github.com/apache/incubator-zeppelin/blob/master/pom.xml).
|
||||
- Add documentation on how to use your interpreter under `docs/interpreter/`. Follow the Markdown style as this [example](https://github.com/apache/incubator-zeppelin/blob/master/docs/interpreter/elasticsearch.md). Make sure you list config settings and provide working examples on using your interpreter in code boxes in Markdown. Link to images as appropriate (images should go to `docs/assets/themes/zeppelin/img/docs-img/`). And add a link to your documentation in the navigation menu (`docs/_includes/themes/zeppelin/_navigation.html`).
|
||||
- Most importantly, ensure licenses of the transitive closure of all dependencies are list in [license file](https://github.com/apache/incubator-zeppelin/blob/master/zeppelin-distribution/src/bin_license/LICENSE).
|
||||
- Commit your changes and open a Pull Request on the project [Mirror on GitHub](https://github.com/apache/incubator-zeppelin); check to make sure Travis CI build is passing.
|
||||
|
|
|
|||
|
|
@ -20,32 +20,28 @@ limitations under the License.
|
|||
{% include JB/setup %}
|
||||
|
||||
|
||||
### Angular (Beta)
|
||||
## Angular Display System in Zeppelin
|
||||
|
||||
Angular display system treats output as an view template of [AngularJS](https://angularjs.org/).
|
||||
It compiles templates and display inside of Zeppelin.
|
||||
Angular display system treats output as a view template for [AngularJS](https://angularjs.org/).
|
||||
It compiles templates and displays them inside of Zeppelin.
|
||||
|
||||
Zeppelin provides gateway between your interpreter and your compiled AngularJS view teamplates.
|
||||
Therefore, you can not only update scope variable from your interpreter but also watch your scope variable in the interpreter, which is JVM process.
|
||||
Zeppelin provides a gateway between your interpreter and your compiled **AngularJS view** templates.
|
||||
Therefore, you can not only update scope variables from your interpreter but also watch them in the interpreter, which is JVM process.
|
||||
|
||||
### Print AngularJS view
|
||||
|
||||
To use angular display system, you should start with `%angular`.
|
||||
<img src="/assets/themes/zeppelin/img/screenshots/display_angular.png" width="60%" />
|
||||
|
||||
Since `name` is not defined, `Hello {{name}}` will display `Hello`.
|
||||
> **Please Note:** Display system is backend independent.
|
||||
|
||||
<br />
|
||||
#### Print AngularJS view
|
||||
### Bind / Unbind Variables
|
||||
|
||||
To use angular display system, your output should starts with "%angular".
|
||||
<img src="/assets/themes/zeppelin/img/screenshots/display_angular.png" width=600px />
|
||||
Through **ZeppelinContext**, you can bind / unbind variables to **AngularJS view**. Currently, it only works in **Spark Interpreter ( scala )**.
|
||||
|
||||
Note that display system is backend independent.
|
||||
|
||||
Because of variable 'name' is not defined, 'Hello \{\{name\}\}' display 'Hello '.
|
||||
|
||||
<br />
|
||||
#### Bind/Unbind variable
|
||||
|
||||
Through ZeppelinContext, you can bind/unbind variable to AngularJS view.
|
||||
|
||||
Currently it only works in Spark Interpreter (scala).
|
||||
|
||||
```
|
||||
```scala
|
||||
// bind my 'object' as angular scope variable 'name' in current notebook.
|
||||
z.angularBind(String name, Object object)
|
||||
|
||||
|
|
@ -60,19 +56,17 @@ z.angularUnbindGlobal(String name)
|
|||
|
||||
```
|
||||
|
||||
In the example, let's bind "world" variable 'name'. Then you can see AngularJs view are updated immediately.
|
||||
Using the above example, let's bind `world` variable to `name`. Then you can see **AngularJs view** is immediately updated.
|
||||
|
||||
<img src="/assets/themes/zeppelin/img/screenshots/display_angular1.png" width=600px />
|
||||
<img src="/assets/themes/zeppelin/img/screenshots/display_angular1.png" width="60%" />
|
||||
|
||||
|
||||
<br />
|
||||
#### Watch/Unwatch variable
|
||||
### Watch / Unwatch Variables
|
||||
|
||||
Through ZeppelinContext, you can watch/unwatch variable in AngularJs view.
|
||||
Through **ZeppelinContext**, you can watch / unwatch variables in **AngularJs view**. Currently, it only works in **Spark Interpreter ( scala )**.
|
||||
|
||||
Currently it only works in Spark Interpreter (scala).
|
||||
|
||||
```
|
||||
```scala
|
||||
// register for angular scope variable 'name' (notebook)
|
||||
z.angularWatch(String name, (before, after) => { ... })
|
||||
|
||||
|
|
@ -88,11 +82,138 @@ z.angularUnwatchGlobal(String name)
|
|||
|
||||
```
|
||||
|
||||
Let's make an button, that increment 'run' variable by 1 when it is clicked.
|
||||
z.angularBind("run", 0) will initialize 'run' to zero. And then register watcher of 'run'.
|
||||
Let's make a button. When it is clicked, the value of `run` will be increased 1 by 1.
|
||||
|
||||
<img src="/assets/themes/zeppelin/img/screenshots/display_angular2.png" width=600px />
|
||||
<img src="/assets/themes/zeppelin/img/screenshots/display_angular2.png" width="60%" />
|
||||
|
||||
After clicked button, you'll see both 'run' and numWatched are increased by 1
|
||||
`z.angularBind("run", 0)` will initialize `run` to zero. And then, it will be also applied to `run` in `z.angularWatch()`.
|
||||
When the button is clicked, you'll see both `run` and `numWatched` are incremented by 1.
|
||||
|
||||
<img src="/assets/themes/zeppelin/img/screenshots/display_angular3.png" width=600px />
|
||||
<img src="/assets/themes/zeppelin/img/screenshots/display_angular3.png" width="60%" />
|
||||
|
||||
## Let's make it Simpler and more Intuitive
|
||||
In this section, we will introduce a simpler and more intuitive way of using **Angular Display System** in Zeppelin.
|
||||
|
||||
### How can we use it?
|
||||
Here are some usages.
|
||||
|
||||
#### Import
|
||||
|
||||
##### - In notebook scope
|
||||
```scala
|
||||
import org.apache.zeppelin.display.angular.notebookscope._
|
||||
import AngularElem._
|
||||
```
|
||||
|
||||
##### - In paragraph scope
|
||||
```scala
|
||||
import org.apache.zeppelin.display.angular.paragraphscope._
|
||||
import AngularElem._
|
||||
```
|
||||
|
||||
|
||||
#### Display Element
|
||||
```scala
|
||||
// automatically convert to string and print with %angular display system directive in front.
|
||||
<div><div>.display
|
||||
```
|
||||
#### Event Handler
|
||||
```scala
|
||||
// on click
|
||||
<div></div>.onClick(() => {
|
||||
my callback routine
|
||||
}).display
|
||||
|
||||
// on change
|
||||
<div></div>.onChange(() => {
|
||||
my callback routine
|
||||
}).display
|
||||
|
||||
// arbitrary event
|
||||
<div></div>.onEvent("ng-click", () => {
|
||||
my callback routine
|
||||
}).display
|
||||
```
|
||||
|
||||
#### Bind Model
|
||||
```scala
|
||||
// bind model
|
||||
<div></div>.model("myModel").display
|
||||
|
||||
// bind model with initial value
|
||||
<div></div>.model("myModel", initialValue).display
|
||||
```
|
||||
|
||||
#### Interact with Model
|
||||
```scala
|
||||
// read model
|
||||
AngularModel("myModel")()
|
||||
|
||||
// update model
|
||||
AngularModel("myModel", "newValue")
|
||||
```
|
||||
|
||||
<br/>
|
||||
### Example: Basic Usage
|
||||
Using the above basic usages, you can apply them like below examples.
|
||||
|
||||
#### Display Elements
|
||||
|
||||
```scala
|
||||
<div style="color:blue">
|
||||
<h4>Hello Angular Display System</h4>
|
||||
</div>.display
|
||||
```
|
||||
|
||||
#### OnClick Event
|
||||
```scala
|
||||
<div class="btn btn-success">
|
||||
Click me
|
||||
</div>.onClick{() =>
|
||||
// callback for button click
|
||||
}.display
|
||||
```
|
||||
|
||||
#### Bind Model
|
||||
|
||||
{% raw %}
|
||||
```scala
|
||||
<div>{{{{myModel}}}}</div>.model("myModel", "Initial Value").display
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
#### Interact With Model
|
||||
```scala
|
||||
// read the value
|
||||
AngularModel("myModel")()
|
||||
|
||||
// update the value
|
||||
AngularModel("myModel", "New value")
|
||||
```
|
||||
|
||||
<img src="../assets/themes/zeppelin/img/docs-img/basic-usage-angular.png" width="70%">
|
||||
|
||||
### Example: String Converter
|
||||
Using below example, you can convert the lowercase string to uppercase.
|
||||
|
||||
{% raw %}
|
||||
```scala
|
||||
// clear previously created angular object.
|
||||
AngularElem.disassociate
|
||||
|
||||
val button = <div class="btn btn-success btn-sm">Convert</div>.onClick{() =>
|
||||
val inputString = AngularModel("input")().toString
|
||||
AngularModel("title", inputString.toUpperCase)
|
||||
}
|
||||
|
||||
<div>
|
||||
{ <h4> {{{{title}}}}</h4>.model("title", "Please type text to convert uppercase") }
|
||||
Your text { <input type="text"></input>.model("input", "") }
|
||||
{button}
|
||||
</div>.display
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
<img src="../assets/themes/zeppelin/img/docs-img/string-converter-angular.gif" width="70%">
|
||||
|
||||
|
||||
|
|
@ -37,7 +37,6 @@ limitations under the License.
|
|||
<div class="col-md-7"><img class="img-responsive" style="border: 1px solid #ecf0f1;" height="auto" src="/assets/themes/zeppelin/img/notebook.png" /></div>
|
||||
</div>
|
||||
|
||||
|
||||
<br />
|
||||
### Multiple language backend
|
||||
|
||||
|
|
@ -86,7 +85,6 @@ With simple drag and drop Zeppelin aggeregates the values and display them in pi
|
|||
</div>
|
||||
Learn more about Zeppelin's Display system. ( [text](./displaysystem/display.html), [html](./displaysystem/display.html#html), [table](./displaysystem/table.html), [angular](./displaysystem/angular.html) )
|
||||
|
||||
|
||||
<br />
|
||||
### Dynamic forms
|
||||
|
||||
|
|
@ -96,7 +94,6 @@ Zeppelin can dynamically create some input forms into your notebook.
|
|||
|
||||
Learn more about [Dynamic Forms](./manual/dynamicform.html).
|
||||
|
||||
|
||||
<br />
|
||||
### Collaboration
|
||||
|
||||
|
|
@ -114,6 +111,8 @@ This way, you can easily embed it as an iframe inside of your website.</p>
|
|||
<img class="img-responsive center-block" src="/assets/themes/zeppelin/img/screenshots/publish.png" />
|
||||
</div>
|
||||
|
||||
If you want to learn more about this feature, please visit [this page](./manual/publish.html).
|
||||
|
||||
<br />
|
||||
### 100% Opensource
|
||||
|
||||
|
|
@ -124,5 +123,4 @@ Join the [Mailing list](./community.html) and report issues on our [Issue tracke
|
|||
|
||||
<br />
|
||||
### Undergoing Incubation
|
||||
Apache Zeppelin is an effort undergoing [incubation](https://incubator.apache.org/index.html) at The Apache Software Foundation (ASF), sponsored by the Incubator. Incubation is required of all newly accepted projects until a further review indicates that the infrastructure, communications, and decision making process have stabilized in a manner consistent with other successful ASF projects. While incubation status is not necessarily a reflection of the completeness or stability of the code, it does indicate that the project has yet to be fully endorsed by the ASF.
|
||||
|
||||
Apache Zeppelin is an effort undergoing [incubation](https://incubator.apache.org/index.html) at The Apache Software Foundation (ASF), sponsored by the Incubator. Incubation is required of all newly accepted projects until a further review indicates that the infrastructure, communications, and decision making process have stabilized in a manner consistent with other successful ASF projects. While incubation status is not necessarily a reflection of the completeness or stability of the code, it does indicate that the project has yet to be fully endorsed by the ASF.
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
layout: page
|
||||
title: "Install Zeppelin"
|
||||
title: "Zeppelin Installation"
|
||||
description: ""
|
||||
group: install
|
||||
---
|
||||
|
|
@ -21,21 +21,31 @@ limitations under the License.
|
|||
|
||||
|
||||
|
||||
## From binary package
|
||||
## Zeppelin Installation
|
||||
Welcome to your first trial to explore Zeppelin!
|
||||
|
||||
Download latest binary package from [Download](../download.html).
|
||||
In this documentation, we will explain how you can install Zeppelin from **Binary Package** or build from **Source** by yourself. Plus, you can see all of Zeppelin's configurations in the [Zeppelin Configuration](install.html#zeppelin-configuration) section below.
|
||||
|
||||
### Install with Binary Package
|
||||
|
||||
## Build from source
|
||||
If you want to install Zeppelin with latest binary package, please visit [this page](http://zeppelin.incubator.apache.org/download.html).
|
||||
|
||||
Check instructions in [README](https://github.com/apache/incubator-zeppelin/blob/master/README.md) to build from source.
|
||||
### Build from Zeppelin Source
|
||||
|
||||
You can also build Zeppelin from the source.
|
||||
|
||||
#### Prerequisites for build
|
||||
* Java 1.7
|
||||
* Git
|
||||
* Maven(3.1.x or higher)
|
||||
* Node.js Package Manager
|
||||
|
||||
## Configure
|
||||
If you don't have requirements prepared, please check instructions in [README.md](https://github.com/apache/incubator-zeppelin/blob/master/README.md) for the details.
|
||||
|
||||
Configuration can be done by both environment variable(conf/zeppelin-env.sh) and java properties(conf/zeppelin-site.xml). If both defined, environment vaiable is used.
|
||||
<a name="zeppelin-configuration"> </a>
|
||||
## Zeppelin Configuration
|
||||
|
||||
You can configure Zeppelin with both **environment variables** in `conf/zeppelin-env.sh` and **java properties** in `conf/zeppelin-site.xml`. If both are defined, then the **environment variables** will be used priorly.
|
||||
|
||||
<table class="table-configuration">
|
||||
<tr>
|
||||
|
|
@ -48,7 +58,7 @@ Configuration can be done by both environment variable(conf/zeppelin-env.sh) and
|
|||
<td>ZEPPELIN_PORT</td>
|
||||
<td>zeppelin.server.port</td>
|
||||
<td>8080</td>
|
||||
<td>Zeppelin server port.</td>
|
||||
<td>Zeppelin server port</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEPPELIN_MEM</td>
|
||||
|
|
@ -66,19 +76,25 @@ Configuration can be done by both environment variable(conf/zeppelin-env.sh) and
|
|||
<td>ZEPPELIN_JAVA_OPTS</td>
|
||||
<td>N/A</td>
|
||||
<td></td>
|
||||
<td>JVM Options</td>
|
||||
<td>JVM options</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEPPELIN_ALLOWED_ORIGINS</td>
|
||||
<td>zeppelin.server.allowed.origins</td>
|
||||
<td>*</td>
|
||||
<td>Allows a way to specify a ',' separated list of allowed origins for rest and websockets. i.e. http://localhost:8080</td>
|
||||
<td>Enables a way to specify a ',' separated list of allowed origins for rest and websockets. <br /> i.e. http://localhost:8080 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>N/A</td>
|
||||
<td>zeppelin.anonymous.allowed</td>
|
||||
<td>true</td>
|
||||
<td>Anonymous user is allowed by default.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEPPELIN_SERVER_CONTEXT_PATH</td>
|
||||
<td>zeppelin.server.context.path</td>
|
||||
<td>/</td>
|
||||
<td>Context Path of the Web Application</td>
|
||||
<td>A context path of the web application</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEPPELIN_SSL</td>
|
||||
|
|
@ -138,37 +154,55 @@ Configuration can be done by both environment variable(conf/zeppelin-env.sh) and
|
|||
<td>ZEPPELIN_NOTEBOOK_HOMESCREEN</td>
|
||||
<td>zeppelin.notebook.homescreen</td>
|
||||
<td></td>
|
||||
<td>Id of notebook to be displayed in homescreen ex) 2A94M5J1Z</td>
|
||||
<td>A notebook id displayed in Zeppelin homescreen <br />i.e. 2A94M5J1Z</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEPPELIN_NOTEBOOK_HOMESCREEN_HIDE</td>
|
||||
<td>zeppelin.notebook.homescreen.hide</td>
|
||||
<td>false</td>
|
||||
<td>hide homescreen notebook from list when this value set to "true"</td>
|
||||
<td>This value can be "true" when to hide the notebook id set by <code>ZEPPELIN_NOTEBOOK_HOMESCREEN</code> on the Zeppelin homescreen. <br />For the further information, please read <a href="../manual/notebookashomepage.html">Customize your Zeppelin homepage</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEPPELIN_WAR_TEMPDIR</td>
|
||||
<td>zeppelin.war.tempdir</td>
|
||||
<td>webapps</td>
|
||||
<td>The location of jetty temporary directory.</td>
|
||||
<td>A location of jetty temporary directory</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEPPELIN_NOTEBOOK_DIR</td>
|
||||
<td>zeppelin.notebook.dir</td>
|
||||
<td>notebook</td>
|
||||
<td>Where notebook file is saved</td>
|
||||
<td>The root directory where Zeppelin notebook directories are saved</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEPPELIN_NOTEBOOK_S3_BUCKET</td>
|
||||
<td>zeppelin.notebook.s3.bucket</td>
|
||||
<td>zeppelin</td>
|
||||
<td>Bucket where notebook saved</td>
|
||||
<td>S3 Bucket where Zeppelin notebook files will be saved</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEPPELIN_NOTEBOOK_S3_USER</td>
|
||||
<td>zeppelin.notebook.s3.user</td>
|
||||
<td>user</td>
|
||||
<td>User in bucket where notebook saved. For example bucket/user/notebook/2A94M5J1Z/note.json</td>
|
||||
<td>A user name of S3 bucket<br />i.e. <code>bucket/user/notebook/2A94M5J1Z/note.json</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEPPELIN_NOTEBOOK_AZURE_CONNECTION_STRING</td>
|
||||
<td>zeppelin.notebook.azure.connectionString</td>
|
||||
<td></td>
|
||||
<td>The Azure storage account connection string<br />i.e. <code>DefaultEndpointsProtocol=https;AccountName=<accountName>;AccountKey=<accountKey></code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEPPELIN_NOTEBOOK_AZURE_SHARE</td>
|
||||
<td>zeppelin.notebook.azure.share</td>
|
||||
<td>zeppelin</td>
|
||||
<td>Share where the Zeppelin notebook files will be saved</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEPPELIN_NOTEBOOK_AZURE_USER</td>
|
||||
<td>zeppelin.notebook.azure.user</td>
|
||||
<td>user</td>
|
||||
<td>An optional user name of Azure file share<br />i.e. <code>share/user/notebook/2A94M5J1Z/note.json</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEPPELIN_NOTEBOOK_STORAGE</td>
|
||||
|
|
@ -183,7 +217,7 @@ Configuration can be done by both environment variable(conf/zeppelin-env.sh) and
|
|||
<td>org.apache.zeppelin.spark.SparkInterpreter,<br />org.apache.zeppelin.spark.PySparkInterpreter,<br />org.apache.zeppelin.spark.SparkSqlInterpreter,<br />org.apache.zeppelin.spark.DepInterpreter,<br />org.apache.zeppelin.markdown.Markdown,<br />org.apache.zeppelin.shell.ShellInterpreter,<br />org.apache.zeppelin.hive.HiveInterpreter<br />
|
||||
...
|
||||
</td>
|
||||
<td>Comma separated interpreter configurations [Class]. First interpreter become a default</td>
|
||||
<td>Comma separated interpreter configurations [Class] <br /> The first interpreter will be a default value. <br /> It means only the first interpreter in this list can be available without <code>%interpreter_name</code> annotation in Zeppelin notebook paragraph. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEPPELIN_INTERPRETER_DIR</td>
|
||||
|
|
@ -193,19 +227,16 @@ Configuration can be done by both environment variable(conf/zeppelin-env.sh) and
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
You'll also need to configure individual interpreter. Information can be found in 'Interpreter' section in this documentation.
|
||||
Maybe you need to configure individual interpreter. If so, please check **Interpreter** section in Zeppelin documentation.
|
||||
[Spark Interpreter for Apache Zeppelin](../interpreter/spark.html) will be a good example.
|
||||
|
||||
For example [Spark](../interpreter/spark.html).
|
||||
|
||||
<br />
|
||||
## Start/Stop
|
||||
## Zeppelin Start / Stop
|
||||
#### Start Zeppelin
|
||||
|
||||
```
|
||||
bin/zeppelin-daemon.sh start
|
||||
```
|
||||
After successful start, visit http://localhost:8080 with your web browser.
|
||||
After successful start, visit [http://localhost:8080](http://localhost:8080) with your web browser.
|
||||
|
||||
#### Stop Zeppelin
|
||||
|
||||
|
|
@ -213,4 +244,38 @@ After successful start, visit http://localhost:8080 with your web browser.
|
|||
bin/zeppelin-daemon.sh stop
|
||||
```
|
||||
|
||||
#### Start Zeppelin with a service manager such as upstart
|
||||
|
||||
Zeppelin can auto start as a service with an init script, such as services managed by upstart.
|
||||
|
||||
The following is an example upstart script to be saved as `/etc/init/zeppelin.conf`
|
||||
This example has been tested with Ubuntu Linux.
|
||||
This also allows the service to be managed with commands such as
|
||||
|
||||
`sudo service zeppelin start`
|
||||
`sudo service zeppelin stop`
|
||||
`sudo service zeppelin restart`
|
||||
|
||||
Other service managers could use a similar approach with the `upstart` argument passed to the zeppelin-daemon.sh script: `bin/zeppelin-daemon.sh upstart`
|
||||
|
||||
##### zeppelin.conf
|
||||
|
||||
```
|
||||
description "zeppelin"
|
||||
|
||||
start on (local-filesystems and net-device-up IFACE!=lo)
|
||||
stop on shutdown
|
||||
|
||||
# Respawn the process on unexpected termination
|
||||
respawn
|
||||
|
||||
# respawn the job up to 7 times within a 5 second period.
|
||||
# If the job exceeds these values, it will be stopped and marked as failed.
|
||||
respawn limit 7 5
|
||||
|
||||
# zeppelin was installed in /usr/share/zeppelin in this example
|
||||
chdir /usr/share/zeppelin
|
||||
exec bin/zeppelin-daemon.sh upstart
|
||||
```
|
||||
|
||||
|
||||
|
|
|
|||
44
docs/install/upgrade.md
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
layout: page
|
||||
title: "Manual upgrade procedure for Zeppelin"
|
||||
description: ""
|
||||
group: install
|
||||
---
|
||||
<!--
|
||||
Licensed 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.
|
||||
-->
|
||||
{% include JB/setup %}
|
||||
|
||||
## Manual upgrade procedure for Zeppelin
|
||||
|
||||
Basically, newer version of Zeppelin works with previous version notebook directory and configurations.
|
||||
So, copying `notebook` and `conf` directory should be enough.
|
||||
|
||||
### Instructions
|
||||
1. Stop Zeppelin
|
||||
|
||||
```
|
||||
bin/zeppelin-daemon.sh stop
|
||||
```
|
||||
|
||||
1. Copy your `notebook` and `conf` directory into a backup directory
|
||||
|
||||
1. Download newer version of Zeppelin and Install. See [Install page](./install.html)
|
||||
|
||||
1. Copy backup `notebook` and `conf` directory into newer version of Zeppelin `notebook` and `conf` directory
|
||||
|
||||
1. Start Zeppelin
|
||||
|
||||
```
|
||||
bin/zeppelin-daemon.sh start
|
||||
```
|
||||
|
|
@ -22,11 +22,11 @@ limitations under the License.
|
|||
|
||||
## Vagrant Virtual Machine for Apache Zeppelin
|
||||
|
||||
The Apache Zeppelin distribution includes a scripts directory
|
||||
Apache Zeppelin distribution includes a scripts directory
|
||||
|
||||
`scripts/vagrant/zeppelin-dev`
|
||||
|
||||
This script creates a virtual machine that launches a repeatable, known set of core dependencies required for developing Zeppelin. It can also be used to run an existing Zeppelin build if you don't plan to build from source. For pyspark users, this script also includes several helpful [Python Libraries](#pythonextras)
|
||||
This script creates a virtual machine that launches a repeatable, known set of core dependencies required for developing Zeppelin. It can also be used to run an existing Zeppelin build if you don't plan to build from source. For pyspark users, this script also includes several helpful [Python Libraries](#pythonextras).
|
||||
|
||||
####Installing the required components to launch a virtual machine.
|
||||
|
||||
|
|
@ -34,19 +34,22 @@ This script requires three applications, [Ansible](http://docs.ansible.com/ansib
|
|||
|
||||
### Create a Zeppelin Ready VM in 4 Steps (5 on Windows)
|
||||
|
||||
*If you are running Windows and don't yet have python installed, install Python 2.7.x* [Python Windows Installer](https://www.python.org/downloads/release/python-2710/)
|
||||
If you are running Windows and don't yet have python installed, [install Python 2.7.x](https://www.python.org/downloads/release/python-2710/) first.
|
||||
|
||||
1. Download and Install Vagrant: [Vagrant Downloads](http://www.vagrantup.com/downloads)
|
||||
2. Install Ansible: [Ansible Python pip install](http://docs.ansible.com/ansible/intro_installation.html#latest-releases-via-pip)
|
||||
`sudo easy_install pip` then
|
||||
`sudo pip install ansible`
|
||||
`ansible --version` should now report version 1.9.2 or higher
|
||||
2. Install Ansible: [Ansible Python pip install](http://docs.ansible.com/ansible/intro_installation.html#latest-releases-via-pip)
|
||||
|
||||
```
|
||||
sudo easy_install pip
|
||||
sudo pip install ansible
|
||||
ansible --version
|
||||
```
|
||||
After then, please check whether it reports **ansible version 1.9.2 or higher**.
|
||||
|
||||
3. Install Virtual Box: [Virtual Box Downloads](https://www.virtualbox.org/ "Virtual Box")
|
||||
4. Type `vagrant up` from within the `/scripts/vagrant/zeppelin-dev` directory
|
||||
|
||||
Thats it!
|
||||
|
||||
You can now run `vagrant ssh` and this will place you into the guest machines terminal prompt.
|
||||
Thats it ! You can now run `vagrant ssh` and this will place you into the guest machines terminal prompt.
|
||||
|
||||
If you don't wish to build Zeppelin from scratch, run the z-manager installer script while running in the guest VM:
|
||||
|
||||
|
|
@ -55,18 +58,17 @@ curl -fsSL https://raw.githubusercontent.com/NFLabs/z-manager/master/zeppelin-in
|
|||
```
|
||||
|
||||
|
||||
|
||||
### Building Zeppelin
|
||||
|
||||
You can now `git clone https://github.com/apache/incubator-zeppelin.git` into a directory on your host machine, or directly in your virtual machine.
|
||||
You can now `git clone git://git.apache.org/incubator-zeppelin.git` into a directory on your host machine, or directly in your virtual machine.
|
||||
|
||||
Cloning zeppelin into the `/scripts/vagrant/zeppelin-dev` directory from the host, will allow the directory to be shared between your host and the guest machine.
|
||||
Cloning Zeppelin into the `/scripts/vagrant/zeppelin-dev` directory from the host, will allow the directory to be shared between your host and the guest machine.
|
||||
|
||||
Cloning the project again may seem counter intuitive, since this script likley originated from the project repository. Consider copying just the vagrant/zeppelin-dev script from the zeppelin project as a stand alone directory, then once again clone the specific branch you wish to build.
|
||||
Cloning the project again may seem counter intuitive, since this script likley originated from the project repository. Consider copying just the vagrant/zeppelin-dev script from the Zeppelin project as a stand alone directory, then once again clone the specific branch you wish to build.
|
||||
|
||||
Synced folders enable Vagrant to sync a folder on the host machine to the guest machine, allowing you to continue working on your project's files on your host machine, but use the resources in the guest machine to compile or run your project. _[(1) Synced Folder Description from Vagrant Up](https://docs.vagrantup.com/v2/synced-folders/index.html)_
|
||||
|
||||
By default, Vagrant will share your project directory (the directory with the Vagrantfile) to `/vagrant`. Which means you should be able to build within the guest machine after you
|
||||
By default, Vagrant will share your project directory (the directory with the Vagrantfile) to `/vagrant`. Which means you should be able to build within the guest machine after you
|
||||
`cd /vagrant/incubator-zeppelin`
|
||||
|
||||
|
||||
|
|
@ -74,7 +76,7 @@ By default, Vagrant will share your project directory (the directory with the Va
|
|||
|
||||
Running the following commands in the guest machine should display these expected versions:
|
||||
|
||||
`node --version` should report *v0.12.7*
|
||||
`node --version` should report *v0.12.7*
|
||||
`mvn --version` should report *Apache Maven 3.3.3* and *Java version: 1.7.0_85*
|
||||
|
||||
|
||||
|
|
@ -117,15 +119,15 @@ Comment out the `forward_port` line, and uncomment the `private_network` line in
|
|||
config.vm.network "private_network", ip: "192.168.51.52"
|
||||
```
|
||||
|
||||
`vagrant halt` followed by `vagrant up` will restart the guest machine bound to the IP address of `192.168.51.52`.
|
||||
This approach usually is typically required if running other virtual machines that discover each other directly by IP address, such as Spark Masters and Slaves as well as Cassandra Nodes, Elasticsearch Nodes, and other Spark data sources. You may wish to launch nodes in virtual machines with IP Addresses in a subnet that works for your local network, such as: 192.168.51.53, 192.168.51.54, 192.168.51.53, etc..
|
||||
`vagrant halt` followed by `vagrant up` will restart the guest machine bound to the IP address of `192.168.51.52`.
|
||||
This approach usually is typically required if running other virtual machines that discover each other directly by IP address, such as Spark Masters and Slaves as well as Cassandra Nodes, Elasticsearch Nodes, and other Spark data sources. You may wish to launch nodes in virtual machines with IP addresses in a subnet that works for your local network, such as: 192.168.51.53, 192.168.51.54, 192.168.51.53, etc..
|
||||
|
||||
|
||||
### [Python Extras](id:pythonextras)
|
||||
|
||||
With zeppelin running, Numpy, SciPy, Pandas and Matplotlib will be available. Create a pyspark notebook, and try
|
||||
With Zeppelin running, **Numpy**, **SciPy**, **Pandas** and **Matplotlib** will be available. Create a pyspark notebook, and try the below code.
|
||||
|
||||
```
|
||||
```python
|
||||
%pyspark
|
||||
|
||||
import numpy
|
||||
|
|
@ -139,9 +141,9 @@ print "pandas " + pandas.__version__
|
|||
print "matplotlib " + matplotlib.__version__
|
||||
```
|
||||
|
||||
To Test plotting using matplotlib into a rendered %html SVG image, try
|
||||
To Test plotting using Matplotlib into a rendered `%html` SVG image, try
|
||||
|
||||
```
|
||||
```python
|
||||
%pyspark
|
||||
|
||||
import matplotlib
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ limitations under the License.
|
|||
{% include JB/setup %}
|
||||
|
||||
## Introduction
|
||||
This page describes how to pre-configure a bare metal node, build & configure Zeppelin on it, configure Zeppelin and connect it to existing YARN cluster running Hortonworks flavour of Hadoop. It also describes steps to configure Spark & Hive interpreter of Zeppelin.
|
||||
This page describes how to pre-configure a bare metal node, configure Zeppelin and connect it to existing YARN cluster running Hortonworks flavour of Hadoop. It also describes steps to configure Spark & Hive interpreter of Zeppelin.
|
||||
|
||||
## Prepare Node
|
||||
|
||||
|
|
@ -44,84 +44,16 @@ Its assumed in the rest of the document that zeppelin user is indeed created and
|
|||
|
||||
### List of Prerequisites
|
||||
|
||||
* CentOS 6.x
|
||||
* Git
|
||||
* Java 1.7
|
||||
* Apache Maven
|
||||
* Hadoop client.
|
||||
* Spark.
|
||||
* CentOS 6.x, Mac OSX, Ubuntu 14.X
|
||||
* Java 1.7
|
||||
* Hadoop client
|
||||
* Spark
|
||||
* Internet connection is required.
|
||||
|
||||
Its assumed that the node has CentOS 6.x installed on it. Although any version of Linux distribution should work fine. The working directory of all prerequisite pacakges is /home/zeppelin/prerequisites, although any location could be used.
|
||||
|
||||
#### Git
|
||||
Intall latest stable version of Git. This document describes installation of version 2.4.8
|
||||
|
||||
```bash
|
||||
yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel
|
||||
yum install gcc perl-ExtUtils-MakeMaker
|
||||
yum remove git
|
||||
cd /home/zeppelin/prerequisites
|
||||
wget https://github.com/git/git/archive/v2.4.8.tar.gz
|
||||
tar xzf git-2.0.4.tar.gz
|
||||
cd git-2.0.4
|
||||
make prefix=/home/zeppelin/prerequisites/git all
|
||||
make prefix=/home/zeppelin/prerequisites/git install
|
||||
echo "export PATH=$PATH:/home/zeppelin/prerequisites/bin" >> /home/zeppelin/.bashrc
|
||||
source /home/zeppelin/.bashrc
|
||||
git --version
|
||||
```
|
||||
|
||||
Assuming all the packages are successfully installed, running the version option with git command should display
|
||||
|
||||
```bash
|
||||
git version 2.4.8
|
||||
```
|
||||
|
||||
#### Java
|
||||
Zeppelin works well with 1.7.x version of Java runtime. Download JDK version 7 and a stable update and follow below instructions to install it.
|
||||
|
||||
```bash
|
||||
cd /home/zeppelin/prerequisites/
|
||||
#Download JDK 1.7, Assume JDK 7 update 79 is downloaded.
|
||||
tar -xf jdk-7u79-linux-x64.tar.gz
|
||||
echo "export JAVA_HOME=/home/zeppelin/prerequisites/jdk1.7.0_79" >> /home/zeppelin/.bashrc
|
||||
source /home/zeppelin/.bashrc
|
||||
echo $JAVA_HOME
|
||||
```
|
||||
Assuming all the packages are successfully installed, echoing JAVA_HOME environment variable should display
|
||||
|
||||
```bash
|
||||
/home/zeppelin/prerequisites/jdk1.7.0_79
|
||||
```
|
||||
|
||||
#### Apache Maven
|
||||
Download and install a stable version of Maven.
|
||||
|
||||
```bash
|
||||
cd /home/zeppelin/prerequisites/
|
||||
wget ftp://mirror.reverse.net/pub/apache/maven/maven-3/3.3.3/binaries/apache-maven-3.3.3-bin.tar.gz
|
||||
tar -xf apache-maven-3.3.3-bin.tar.gz
|
||||
cd apache-maven-3.3.3
|
||||
export MAVEN_HOME=/home/zeppelin/prerequisites/apache-maven-3.3.3
|
||||
echo "export PATH=$PATH:/home/zeppelin/prerequisites/apache-maven-3.3.3/bin" >> /home/zeppelin/.bashrc
|
||||
source /home/zeppelin/.bashrc
|
||||
mvn -version
|
||||
```
|
||||
|
||||
Assuming all the packages are successfully installed, running the version option with mvn command should display
|
||||
|
||||
```bash
|
||||
Apache Maven 3.3.3 (7994120775791599e205a5524ec3e0dfe41d4a06; 2015-04-22T04:57:37-07:00)
|
||||
Maven home: /home/zeppelin/prerequisites/apache-maven-3.3.3
|
||||
Java version: 1.7.0_79, vendor: Oracle Corporation
|
||||
Java home: /home/zeppelin/prerequisites/jdk1.7.0_79/jre
|
||||
Default locale: en_US, platform encoding: UTF-8
|
||||
OS name: "linux", version: "2.6.32-358.el6.x86_64", arch: "amd64", family: "unix"
|
||||
```
|
||||
It's assumed that the node has CentOS 6.x installed on it. Although any version of Linux distribution should work fine.
|
||||
|
||||
#### Hadoop client
|
||||
Zeppelin can work with multiple versions & distributions of Hadoop. A complete list [is available here.](https://github.com/apache/incubator-zeppelin#build) This document assumes Hadoop 2.7.x client libraries including configuration files are installed on Zeppelin node. It also assumes /etc/hadoop/conf contains various Hadoop configuration files. The location of Hadoop configuration files may vary, hence use appropriate location.
|
||||
Zeppelin can work with multiple versions & distributions of Hadoop. A complete list is available [here](https://github.com/apache/incubator-zeppelin#build). This document assumes Hadoop 2.7.x client libraries including configuration files are installed on Zeppelin node. It also assumes /etc/hadoop/conf contains various Hadoop configuration files. The location of Hadoop configuration files may vary, hence use appropriate location.
|
||||
|
||||
```bash
|
||||
hadoop version
|
||||
|
|
@ -134,32 +66,21 @@ This command was run using /usr/hdp/2.3.1.0-2574/hadoop/lib/hadoop-common-2.7.1.
|
|||
```
|
||||
|
||||
#### Spark
|
||||
Zeppelin can work with multiple versions Spark. A complete list [is available here.](https://github.com/apache/incubator-zeppelin#build) This document assumes Spark 1.3.1 is installed on Zeppelin node at /home/zeppelin/prerequisites/spark.
|
||||
Spark is supported out of the box and to take advantage of this, you need to Download appropriate version of Spark binary packages from [Spark Download page](http://spark.apache.org/downloads.html) and unzip it.
|
||||
Zeppelin can work with multiple versions of Spark. A complete list is available [here](https://github.com/apache/incubator-zeppelin#build).
|
||||
This document assumes Spark 1.6.0 is installed at /usr/lib/spark.
|
||||
> Note: Spark should be installed on the same node as Zeppelin.
|
||||
|
||||
## Build
|
||||
> Note: Spark's pre-built package for CDH 4 doesn't support yarn.
|
||||
|
||||
Checkout source code from [https://github.com/apache/incubator-zeppelin](https://github.com/apache/incubator-zeppelin)
|
||||
#### Zeppelin
|
||||
|
||||
```bash
|
||||
cd /home/zeppelin/
|
||||
git clone https://github.com/apache/incubator-zeppelin.git
|
||||
```
|
||||
Zeppelin package is available at /home/zeppelin/incubator-zeppelin after the checkout completes.
|
||||
|
||||
### Cluster mode
|
||||
|
||||
As its assumed Hadoop 2.7.x is installed on the YARN cluster & Spark 1.3.1 is installed on Zeppelin node. Hence appropriate options are chosen to build Zeppelin. This is very important as Zeppelin will bundle corresponding Hadoop & Spark libraries and they must match the ones present on YARN cluster & Zeppelin Spark installation.
|
||||
|
||||
Zeppelin is a maven project and hence must be built with Apache Maven.
|
||||
|
||||
```bash
|
||||
cd /home/zeppelin/incubator-zeppelin
|
||||
mvn clean package -Pspark-1.3 -Dspark.version=1.3.1 -Dhadoop.version=2.7.0 -Phadoop-2.6 -Pyarn -DskipTests
|
||||
```
|
||||
Building Zeppelin for first time downloads various dependencies and hence takes few minutes to complete.
|
||||
Checkout source code from [git://git.apache.org/incubator-zeppelin.git](https://github.com/apache/incubator-zeppelin.git) or download binary package from [Download page](https://zeppelin.incubator.apache.org/download.html).
|
||||
You can refer [Install](install.html) page for the details.
|
||||
This document assumes that Zeppelin is located under `/home/zeppelin/incubator-zeppelin`.
|
||||
|
||||
## Zeppelin Configuration
|
||||
Zeppelin configurations needs to be modified to connect to YARN cluster. Create a copy of zeppelin environment XML
|
||||
Zeppelin configuration needs to be modified to connect to YARN cluster. Create a copy of zeppelin environment shell script.
|
||||
|
||||
```bash
|
||||
cp /home/zeppelin/incubator-zeppelin/conf/zeppelin-env.sh.template /home/zeppelin/incubator-zeppelin/conf/zeppelin-env.sh
|
||||
|
|
@ -168,9 +89,10 @@ cp /home/zeppelin/incubator-zeppelin/conf/zeppelin-env.sh.template /home/zeppeli
|
|||
Set the following properties
|
||||
|
||||
```bash
|
||||
export JAVA_HOME=/home/zeppelin/prerequisites/jdk1.7.0_79
|
||||
export HADOOP_CONF_DIR=/etc/hadoop/conf
|
||||
export JAVA_HOME="/usr/java/jdk1.7.0_79"
|
||||
export HADOOP_CONF_DIR="/etc/hadoop/conf"
|
||||
export ZEPPELIN_JAVA_OPTS="-Dhdp.version=2.3.1.0-2574"
|
||||
export SPARK_HOME="/usr/lib/spark"
|
||||
```
|
||||
|
||||
As /etc/hadoop/conf contains various configurations of YARN cluster, Zeppelin can now submit Spark/Hive jobs on YARN cluster form its web interface. The value of hdp.version is set to 2.3.1.0-2574. This can be obtained by running the following command
|
||||
|
|
@ -196,7 +118,7 @@ bin/zeppelin-daemon.sh stop
|
|||
```
|
||||
|
||||
## Interpreter
|
||||
Zeppelin provides to various distributed processing frameworks to process data that ranges from Spark, Hive, Tajo, Ignite and Lens to name a few. This document describes to configure Hive & Spark interpreters.
|
||||
Zeppelin provides various distributed processing frameworks to process data that ranges from Spark, Hive, Tajo, Ignite and Lens to name a few. This document describes to configure Hive & Spark interpreters.
|
||||
|
||||
### Hive
|
||||
Zeppelin supports Hive interpreter and hence copy hive-site.xml that should be present at /etc/hive/conf to the configuration folder of Zeppelin. Once Zeppelin is built it will have conf folder under /home/zeppelin/incubator-zeppelin.
|
||||
|
|
@ -209,7 +131,7 @@ Once Zeppelin server has started successfully, visit http://[zeppelin-server-hos
|
|||
Click on Save button. Once these configurations are updated, Zeppelin will prompt you to restart the interpreter. Accept the prompt and the interpreter will reload the configurations.
|
||||
|
||||
### Spark
|
||||
Zeppelin was built with Spark 1.3.1 and it was assumed that 1.3.1 version of Spark is installed at /home/zeppelin/prerequisites/spark. Look for Spark configrations and click edit button to add the following properties
|
||||
It was assumed that 1.6.0 version of Spark is installed at /usr/lib/spark. Look for Spark configurations and click edit button to add the following properties
|
||||
|
||||
<table class="table-configuration">
|
||||
<tr>
|
||||
|
|
@ -222,11 +144,6 @@ Zeppelin was built with Spark 1.3.1 and it was assumed that 1.3.1 version of Spa
|
|||
<td>yarn-client</td>
|
||||
<td>In yarn-client mode, the driver runs in the client process, and the application master is only used for requesting resources from YARN.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>spark.home</td>
|
||||
<td>/home/zeppelin/prerequisites/spark</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>spark.driver.extraJavaOptions</td>
|
||||
<td>-Dhdp.version=2.3.1.0-2574</td>
|
||||
|
|
@ -237,11 +154,6 @@ Zeppelin was built with Spark 1.3.1 and it was assumed that 1.3.1 version of Spa
|
|||
<td>-Dhdp.version=2.3.1.0-2574</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>spark.yarn.jar</td>
|
||||
<td>/home/zeppelin/incubator-zeppelin/interpreter/spark/zeppelin-spark-0.6.0-incubating-SNAPSHOT.jar</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Click on Save button. Once these configurations are updated, Zeppelin will prompt you to restart the interpreter. Accept the prompt and the interpreter will reload the configurations.
|
||||
|
|
|
|||
233
docs/interpreter/alluxio.md
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
---
|
||||
layout: page
|
||||
title: "Alluxio Interpreter"
|
||||
description: "Alluxio Interpreter"
|
||||
group: manual
|
||||
---
|
||||
{% include JB/setup %}
|
||||
|
||||
## Alluxio Interpreter for Apache Zeppelin
|
||||
[Alluxio](http://alluxio.org/) is a memory-centric distributed storage system enabling reliable data sharing at memory-speed across cluster frameworks.
|
||||
|
||||
## Configuration
|
||||
<table class="table-configuration">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Class</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>alluxio.master.hostname</td>
|
||||
<td>localhost</td>
|
||||
<td>Alluxio master hostname</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>alluxio.master.port</td>
|
||||
<td>19998</td>
|
||||
<td>Alluxio master port</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Enabling Alluxio Interpreter
|
||||
In a notebook, to enable the **Alluxio** interpreter, click on the **Gear** icon and select **Alluxio**.
|
||||
|
||||
## Using the Alluxio Interpreter
|
||||
In a paragraph, use `%alluxio` to select the **Alluxio** interpreter and then input all commands.
|
||||
|
||||
```bash
|
||||
%alluxio
|
||||
help
|
||||
```
|
||||
|
||||
> **Tip :** Use ( Ctrl + . ) for autocompletion.
|
||||
|
||||
## Interpreter Commands
|
||||
The **Alluxio** interpreter accepts the following commands.
|
||||
|
||||
<center>
|
||||
<table class="table-configuration">
|
||||
<tr>
|
||||
<th>Operation</th>
|
||||
<th>Syntax</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>cat</td>
|
||||
<td>cat "path"</td>
|
||||
<td>Print the content of the file to the console.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>chgrp</td>
|
||||
<td>chgrp "group" "path"</td>
|
||||
<td>Change the group of the directory or file.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>chmod</td>
|
||||
<td>chmod "permission" "path"</td>
|
||||
<td>Change the permission of the directory or file.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>chown</td>
|
||||
<td>chown "owner" "path"</td>
|
||||
<td>Change the owner of the directory or file.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>copyFromLocal</td>
|
||||
<td>copyFromLocal "source path" "remote path"</td>
|
||||
<td>Copy the specified file specified by "source path" to the path specified by "remote path".
|
||||
This command will fail if "remote path" already exists.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>copyToLocal</td>
|
||||
<td>copyToLocal "remote path" "local path"</td>
|
||||
<td>Copy the specified file from the path specified by "remote path" to a local destination.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>count</td>
|
||||
<td>count "path"</td>
|
||||
<td>Display the number of folders and files matching the specified prefix in "path".</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>du</td>
|
||||
<td>du "path"</td>
|
||||
<td>Display the size of a file or a directory specified by the input path.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>fileInfo</td>
|
||||
<td>fileInfo "path"</td>
|
||||
<td>Print the information of the blocks of a specified file.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>free</td>
|
||||
<td>free "path"</td>
|
||||
<td>Free a file or all files under a directory from Alluxio. If the file/directory is also
|
||||
in under storage, it will still be available there.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>getCapacityBytes</td>
|
||||
<td>getCapacityBytes</td>
|
||||
<td>Get the capacity of the AlluxioFS.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>getUsedBytes</td>
|
||||
<td>getUsedBytes</td>
|
||||
<td>Get number of bytes used in the AlluxioFS.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>load</td>
|
||||
<td>load "path"</td>
|
||||
<td>Load the data of a file or a directory from under storage into Alluxio.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>loadMetadata</td>
|
||||
<td>loadMetadata "path"</td>
|
||||
<td>Load the metadata of a file or a directory from under storage into Alluxio.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>location</td>
|
||||
<td>location "path"</td>
|
||||
<td>Display a list of hosts that have the file data.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ls</td>
|
||||
<td>ls "path"</td>
|
||||
<td>List all the files and directories directly under the given path with information such as
|
||||
size.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>mkdir</td>
|
||||
<td>mkdir "path1" ... "pathn"</td>
|
||||
<td>Create directory(ies) under the given paths, along with any necessary parent directories.
|
||||
Multiple paths separated by spaces or tabs. This command will fail if any of the given paths
|
||||
already exist.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>mount</td>
|
||||
<td>mount "path" "uri"</td>
|
||||
<td>Mount the underlying file system path "uri" into the Alluxio namespace as "path". The "path"
|
||||
is assumed not to exist and is created by the operation. No data or metadata is loaded from under
|
||||
storage into Alluxio. After a path is mounted, operations on objects under the mounted path are
|
||||
mirror to the mounted under storage.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>mv</td>
|
||||
<td>mv "source" "destination"</td>
|
||||
<td>Move a file or directory specified by "source" to a new location "destination". This command
|
||||
will fail if "destination" already exists.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>persist</td>
|
||||
<td>persist "path"</td>
|
||||
<td>Persist a file or directory currently stored only in Alluxio to the underlying file system.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>pin</td>
|
||||
<td>pin "path"</td>
|
||||
<td>Pin the given file to avoid evicting it from memory. If the given path is a directory, it
|
||||
recursively pins all the files contained and any new files created within this directory.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>report</td>
|
||||
<td>report "path"</td>
|
||||
<td>Report to the master that a file is lost.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>rm</td>
|
||||
<td>rm "path"</td>
|
||||
<td>Remove a file. This command will fail if the given path is a directory rather than a file.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>setTtl</td>
|
||||
<td>setTtl "time"</td>
|
||||
<td>Set the TTL (time to live) in milliseconds to a file.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>tail</td>
|
||||
<td>tail "path"</td>
|
||||
<td>Print the last 1KB of the specified file to the console.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>touch</td>
|
||||
<td>touch "path"</td>
|
||||
<td>Create a 0-byte file at the specified location.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>unmount</td>
|
||||
<td>unmount "path"</td>
|
||||
<td>Unmount the underlying file system path mounted in the Alluxio namespace as "path". Alluxio
|
||||
objects under "path" are removed from Alluxio, but they still exist in the previously mounted
|
||||
under storage.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>unpin</td>
|
||||
<td>unpin "path"</td>
|
||||
<td>Unpin the given file to allow Alluxio to evict this file again. If the given path is a
|
||||
directory, it recursively unpins all files contained and any new files created within this
|
||||
directory.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>unsetTtl</td>
|
||||
<td>unsetTtl</td>
|
||||
<td>Remove the TTL (time to live) setting from a file.</td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
|
||||
## How to test it's working
|
||||
Be sure to have configured correctly the Alluxio interpreter, then open a new paragraph and type one of the above commands.
|
||||
|
||||
Below a simple example to show how to interact with Alluxio interpreter.
|
||||
Following steps are performed:
|
||||
|
||||
* using sh interpreter a new text file is created on local machine
|
||||
* using Alluxio interpreter:
|
||||
* is listed the content of the afs (Alluxio File System) root
|
||||
* the file previously created is copied to afs
|
||||
* is listed again the content of the afs root to check the existence of the new copied file
|
||||
* is showed the content of the copied file (using the tail command)
|
||||
* the file previously copied to afs is copied to local machine
|
||||
* using sh interpreter it's checked the existence of the new file copied from Alluxio and its content is showed
|
||||
|
||||
<center>
|
||||

|
||||
</center>
|
||||
67
docs/interpreter/hbase.md
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
---
|
||||
layout: page
|
||||
title: "HBase Shell Interpreter"
|
||||
description: ""
|
||||
group: manual
|
||||
---
|
||||
{% include JB/setup %}
|
||||
|
||||
## HBase Shell Interpreter for Apache Zeppelin
|
||||
[HBase Shell](http://hbase.apache.org/book.html#shell) is a JRuby IRB client for Apache HBase. This interpreter provides all capabilities of Apache HBase shell within Apache Zeppelin. The interpreter assumes that Apache HBase client software has been installed and it can connect to the Apache HBase cluster from the machine on where Apache Zeppelin is installed.
|
||||
To get start with HBase, please see [HBase Quickstart](https://hbase.apache.org/book.html#quickstart)
|
||||
|
||||
> Note: currently only HBase 1.0.x releases are supported.
|
||||
|
||||
## Configuration
|
||||
|
||||
<table class="table-configuration">
|
||||
<tr>
|
||||
<th>Property</th>
|
||||
<th>Default</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>hbase.home</td>
|
||||
<td>/usr/lib/hbase</td>
|
||||
<td>Installation directory of Hbase</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>hbase.ruby.sources</td>
|
||||
<td>lib/ruby</td>
|
||||
<td>Path to Ruby scripts relative to 'hbase.home'</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>hbase.test.mode</td>
|
||||
<td>false</td>
|
||||
<td>Disable checks for unit and manual tests</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Enabling the HBase Shell Interpreter
|
||||
|
||||
In a notebook, to enable the **HBase Shell** interpreter, click the **Gear** icon and select **HBase Shell**.
|
||||
|
||||
## Using the HBase Shell Interpreter
|
||||
|
||||
In a paragraph, use `%hbase` to select the **HBase Shell** interpreter and then input all commands. To get the list of available commands, use `help`.
|
||||
|
||||
```bash
|
||||
%hbase
|
||||
help
|
||||
```
|
||||
|
||||
For example, to create a table
|
||||
|
||||
```bash
|
||||
%hbase
|
||||
create 'test', 'cf'
|
||||
```
|
||||
|
||||
And then to put data into that table
|
||||
|
||||
```bash
|
||||
%hbase
|
||||
put 'test', 'row1', 'cf:a', 'value1'
|
||||
```
|
||||
|
||||
For more information on all commands available, refer to [HBase shell commands](https://learnhbase.wordpress.com/2013/03/02/hbase-shell-commands/)
|
||||
228
docs/interpreter/jdbc.md
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
---
|
||||
layout: page
|
||||
title: "Generic JDBC Interpreter"
|
||||
description: "JDBC user guide"
|
||||
group: manual
|
||||
---
|
||||
{% include JB/setup %}
|
||||
|
||||
|
||||
## Generic JDBC Interpreter for Apache Zeppelin
|
||||
|
||||
This interpreter lets you create a JDBC connection to any data source, by now it has been tested with:
|
||||
|
||||
* Postgres
|
||||
* MySql
|
||||
* MariaDB
|
||||
* Redshift
|
||||
* Hive
|
||||
* Apache Drill
|
||||
* Details on using [Drill JDBC Driver](https://drill.apache.org/docs/using-the-jdbc-driver)
|
||||
|
||||
If someone else used another database please report how it works to improve functionality.
|
||||
|
||||
### Create Interpreter
|
||||
|
||||
When create a interpreter by default use PostgreSQL with the next properties:
|
||||
|
||||
<table class="table-configuration">
|
||||
<tr>
|
||||
<th>name</th>
|
||||
<th>value</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>common.max_count</td>
|
||||
<td>1000</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>default.driver</td>
|
||||
<td>org.postgresql.Driver</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>default.password</td>
|
||||
<td>********</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>default.url</td>
|
||||
<td>jdbc:postgresql://localhost:5432/</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>default.user</td>
|
||||
<td>gpadmin</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
It is not necessary to add driver jar to the classpath for PostgreSQL as it is included in Zeppelin.
|
||||
|
||||
#### Simple connection
|
||||
|
||||
Prior to creating the interpreter it is necessary to add maven coordinate or path of the JDBC driver to the Zeppelin classpath. To do this you must edit dependencies artifact(ex. `mysql:mysql-connector-java:5.1.38`) in interpreter menu as shown:
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-11">
|
||||
<img src="../assets/themes/zeppelin/img/docs-img/jdbc-simple-connection-setting.png" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
To create the interpreter you need to specify connection parameters as shown in the table.
|
||||
|
||||
<table class="table-configuration">
|
||||
<tr>
|
||||
<th>name</th>
|
||||
<th>value</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>common.max_count</td>
|
||||
<td>1000</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>default.driver</td>
|
||||
<td>driver name</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>default.password</td>
|
||||
<td>********</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>default.url</td>
|
||||
<td>jdbc url</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>default.user</td>
|
||||
<td>user name</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
#### Multiple connections
|
||||
|
||||
JDBC interpreter also allows connections to multiple data sources. It is necessary to set a prefix for each connection to reference it in the paragraph in the form of `%jdbc(prefix)`. Before you create the interpreter it is necessary to add each driver's maven coordinates or JDBC driver's jar file path to the Zeppelin classpath. To do this you must edit the dependencies of JDBC interpreter in interpreter menu as following:
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-11">
|
||||
<img src="../assets/themes/zeppelin/img/docs-img/jdbc-multi-connection-setting.png" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
You can add all the jars you need to make multiple connections into the same JDBC interpreter. To create the interpreter you must specify the parameters. For example we will create two connections to MySQL and Redshift, the respective prefixes are `default` and `redshift`:
|
||||
|
||||
<table class="table-configuration">
|
||||
<tr>
|
||||
<th>name</th>
|
||||
<th>value</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>common.max_count</td>
|
||||
<td>1000</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>default.driver</td>
|
||||
<td>com.mysql.jdbc.Driver</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>default.password</td>
|
||||
<td>********</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>default.url</td>
|
||||
<td>jdbc:mysql://localhost:3306/</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>default.user</td>
|
||||
<td>mysql-user</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>redshift.driver</td>
|
||||
<td>com.amazon.redshift.jdbc4.Driver</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>redshift.password</td>
|
||||
<td>********</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>redshift.url</td>
|
||||
<td>jdbc:redshift://examplecluster.abc123xyz789.us-west-2.redshift.amazonaws.com:5439</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>redshift.user</td>
|
||||
<td>redshift-user</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
### Bind to Notebook
|
||||
In the `Notebook` click on the `settings` icon at the top-right corner. Use select/deselect to specify the interpreters to be used in the `Notebook`.
|
||||
|
||||
### More Properties
|
||||
You can modify the interpreter configuration in the `Interpreter` section. The most common properties are as follows, but you can specify other properties that need to be connected.
|
||||
|
||||
<table class="table-configuration">
|
||||
<tr>
|
||||
<th>Property Name</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{prefix}.url</td>
|
||||
<td>JDBC URL to connect, the URL must include the name of the database </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{prefix}.user</td>
|
||||
<td>JDBC user name</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{prefix}.password</td>
|
||||
<td>JDBC password</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{prefix}.driver</td>
|
||||
<td>JDBC driver name.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>common.max_result</td>
|
||||
<td>Max number of SQL result to display to prevent the browser overload. This is common properties for all connections</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
To develop this functionality use this [method](http://docs.oracle.com/javase/7/docs/api/java/sql/DriverManager.html#getConnection%28java.lang.String,%20java.util.Properties%29). For example if a connection needs a schema parameter, it would have to add the property as follows:
|
||||
|
||||
<table class="table-configuration">
|
||||
<tr>
|
||||
<th>name</th>
|
||||
<th>value</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{prefix}.schema</td>
|
||||
<td>schema_name</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### How to use
|
||||
|
||||
#### Reference in paragraph
|
||||
|
||||
Start the paragraphs with the `%jdbc`, this will use the `default` prefix for connection. If you want to use other connection you should specify the prefix of it as follows `%jdbc(prefix)`:
|
||||
|
||||
```sql
|
||||
%jdbc
|
||||
SELECT * FROM db_name;
|
||||
|
||||
```
|
||||
or
|
||||
```sql
|
||||
%jdbc(prefix)
|
||||
SELECT * FROM db_name;
|
||||
|
||||
```
|
||||
|
||||
#### Apply Zeppelin Dynamic Forms
|
||||
|
||||
You can leverage [Zeppelin Dynamic Form](../manual/dynamicform.html) inside your queries. You can use both the `text input` and `select form` parametrization features
|
||||
|
||||
```sql
|
||||
%jdbc(prefix)
|
||||
SELECT name, country, performer
|
||||
FROM demo.performers
|
||||
WHERE name='{{performer=Sheryl Crow|Doof|Fanfarlo|Los Paranoia}}'
|
||||
```
|
||||
|
||||
### Bugs & Contacts
|
||||
If you find a bug for this interpreter, please create a [JIRA]( https://issues.apache.org/jira/browse/ZEPPELIN-382?jql=project%20%3D%20ZEPPELIN) ticket.
|
||||
|
|
@ -40,6 +40,74 @@ Spark Interpreter group, which consisted of 4 interpreters.
|
|||
</table>
|
||||
|
||||
## Configuration
|
||||
Zeppelin provides the below properties for Spark interpreter.
|
||||
You can also set other Spark properties which are not listed in the table. If so, please refer to [Spark Available Properties](http://spark.apache.org/docs/latest/configuration.html#available-properties).
|
||||
<table class="table-configuration">
|
||||
<tr>
|
||||
<th>Property</th>
|
||||
<th>Default</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>args</td>
|
||||
<td></td>
|
||||
<td>Spark commandline args</td>
|
||||
</tr>
|
||||
<td>master</td>
|
||||
<td>local[*]</td>
|
||||
<td>Spark master uri. <br/> ex) spark://masterhost:7077</td>
|
||||
<tr>
|
||||
<td>spark.app.name</td>
|
||||
<td>Zeppelin</td>
|
||||
<td>The name of spark application.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>spark.cores.max</td>
|
||||
<td></td>
|
||||
<td>Total number of cores to use. <br/> Empty value uses all available core.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>spark.executor.memory </td>
|
||||
<td>512m</td>
|
||||
<td>Executor memory per worker instance. <br/> ex) 512m, 32g</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>zeppelin.dep.additionalRemoteRepository</td>
|
||||
<td>spark-packages, <br/> http://dl.bintray.com/spark-packages/maven, <br/> false;</td>
|
||||
<td>A list of `id,remote-repository-URL,is-snapshot;` <br/> for each remote repository.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>zeppelin.dep.localrepo</td>
|
||||
<td>local-repo</td>
|
||||
<td>Local repository for dependency loader</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>zeppelin.pyspark.python</td>
|
||||
<td>python</td>
|
||||
<td>Python command to run pyspark with</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>zeppelin.spark.concurrentSQL</td>
|
||||
<td>false</td>
|
||||
<td>Execute multiple SQL concurrently if set true.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>zeppelin.spark.maxResult</td>
|
||||
<td>1000</td>
|
||||
<td>Max number of SparkSQL result to display.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>zeppelin.spark.printREPLOutput</td>
|
||||
<td>true</td>
|
||||
<td>Print REPL output</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>zeppelin.spark.useHiveContext</td>
|
||||
<td>true</td>
|
||||
<td>Use HiveContext instead of SQLContext if it is true.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Without any configuration, Spark interpreter works out of box in local mode. But if you want to connect to your Spark cluster, you'll need to follow below two simple steps.
|
||||
|
||||
### 1. Export SPARK_HOME
|
||||
|
|
@ -80,9 +148,58 @@ SparkContext, SQLContext, ZeppelinContext are automatically created and exposed
|
|||
<a name="dependencyloading"> </a>
|
||||
|
||||
## Dependency Management
|
||||
There are two ways to load external library in spark interpreter. First is using Zeppelin's `%dep` interpreter and second is loading Spark properties.
|
||||
There are two ways to load external library in spark interpreter. First is using Interpreter setting menu and second is loading Spark properties.
|
||||
|
||||
### 1. Setting Dependencies via Interpreter Setting
|
||||
Please see [Dependency Management](../manual/dependencymanagement.html) for the details.
|
||||
|
||||
### 2. Loading Spark Properties
|
||||
Once `SPARK_HOME` is set in `conf/zeppelin-env.sh`, Zeppelin uses `spark-submit` as spark interpreter runner. `spark-submit` supports two ways to load configurations. The first is command line options such as --master and Zeppelin can pass these options to `spark-submit` by exporting `SPARK_SUBMIT_OPTIONS` in conf/zeppelin-env.sh. Second is reading configuration options from `SPARK_HOME/conf/spark-defaults.conf`. Spark properites that user can set to distribute libraries are:
|
||||
|
||||
<table class="table-configuration">
|
||||
<tr>
|
||||
<th>spark-defaults.conf</th>
|
||||
<th>SPARK_SUBMIT_OPTIONS</th>
|
||||
<th>Applicable Interpreter</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>spark.jars</td>
|
||||
<td>--jars</td>
|
||||
<td>%spark</td>
|
||||
<td>Comma-separated list of local jars to include on the driver and executor classpaths.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>spark.jars.packages</td>
|
||||
<td>--packages</td>
|
||||
<td>%spark</td>
|
||||
<td>Comma-separated list of maven coordinates of jars to include on the driver and executor classpaths. Will search the local maven repo, then maven central and any additional remote repositories given by --repositories. The format for the coordinates should be groupId:artifactId:version.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>spark.files</td>
|
||||
<td>--files</td>
|
||||
<td>%pyspark</td>
|
||||
<td>Comma-separated list of files to be placed in the working directory of each executor.</td>
|
||||
</tr>
|
||||
</table>
|
||||
> Note that adding jar to pyspark is only availabe via `%dep` interpreter at the moment.
|
||||
|
||||
Here are few examples:
|
||||
|
||||
* SPARK\_SUBMIT\_OPTIONS in conf/zeppelin-env.sh
|
||||
|
||||
export SPARK_SUBMIT_OPTIONS="--packages com.databricks:spark-csv_2.10:1.2.0 --jars /path/mylib1.jar,/path/mylib2.jar --files /path/mylib1.py,/path/mylib2.zip,/path/mylib3.egg"
|
||||
|
||||
* SPARK_HOME/conf/spark-defaults.conf
|
||||
|
||||
spark.jars /path/mylib1.jar,/path/mylib2.jar
|
||||
spark.jars.packages com.databricks:spark-csv_2.10:1.2.0
|
||||
spark.files /path/mylib1.py,/path/mylib2.egg,/path/mylib3.zip
|
||||
|
||||
### 3. Dynamic Dependency Loading via %dep interpreter
|
||||
> Note: `%dep` interpreter is deprecated since v0.6.0-incubating.
|
||||
`%dep` interpreter load libraries to `%spark` and `%pyspark` but not to `%spark.sql` interpreter so we recommend you to use first option instead.
|
||||
|
||||
### 1. Dynamic Dependency Loading via %dep interpreter
|
||||
When your code requires external library, instead of doing download/copy/restart Zeppelin, you can easily do following jobs using `%dep` interpreter.
|
||||
|
||||
* Load libraries recursively from Maven repository
|
||||
|
|
@ -129,49 +246,6 @@ z.load("groupId:artifactId:version").exclude("groupId:*")
|
|||
z.load("groupId:artifactId:version").local()
|
||||
```
|
||||
|
||||
### 2. Loading Spark Properties
|
||||
Once `SPARK_HOME` is set in `conf/zeppelin-env.sh`, Zeppelin uses `spark-submit` as spark interpreter runner. `spark-submit` supports two ways to load configurations. The first is command line options such as --master and Zeppelin can pass these options to `spark-submit` by exporting `SPARK_SUBMIT_OPTIONS` in conf/zeppelin-env.sh. Second is reading configuration options from `SPARK_HOME/conf/spark-defaults.conf`. Spark properites that user can set to distribute libraries are:
|
||||
|
||||
<table class="table-configuration">
|
||||
<tr>
|
||||
<th>spark-defaults.conf</th>
|
||||
<th>SPARK_SUBMIT_OPTIONS</th>
|
||||
<th>Applicable Interpreter</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>spark.jars</td>
|
||||
<td>--jars</td>
|
||||
<td>%spark</td>
|
||||
<td>Comma-separated list of local jars to include on the driver and executor classpaths.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>spark.jars.packages</td>
|
||||
<td>--packages</td>
|
||||
<td>%spark</td>
|
||||
<td>Comma-separated list of maven coordinates of jars to include on the driver and executor classpaths. Will search the local maven repo, then maven central and any additional remote repositories given by --repositories. The format for the coordinates should be groupId:artifactId:version.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>spark.files</td>
|
||||
<td>--files</td>
|
||||
<td>%pyspark</td>
|
||||
<td>Comma-separated list of files to be placed in the working directory of each executor.</td>
|
||||
</tr>
|
||||
</table>
|
||||
> Note that adding jar to pyspark is only availabe via `%dep` interpreter at the moment.
|
||||
|
||||
Here are few examples:
|
||||
|
||||
* SPARK\_SUBMIT\_OPTIONS in conf/zeppelin-env.sh
|
||||
|
||||
export SPARK_SUBMIT_OPTIONS="--packages com.databricks:spark-csv_2.10:1.2.0 --jars /path/mylib1.jar,/path/mylib2.jar --files /path/mylib1.py,/path/mylib2.zip,/path/mylib3.egg"
|
||||
|
||||
* SPARK_HOME/conf/spark-defaults.conf
|
||||
|
||||
spark.jars /path/mylib1.jar,/path/mylib2.jar
|
||||
spark.jars.packages com.databricks:spark-csv_2.10:1.2.0
|
||||
spark.files /path/mylib1.py,/path/mylib2.egg,/path/mylib3.zip
|
||||
|
||||
## ZeppelinContext
|
||||
Zeppelin automatically injects ZeppelinContext as variable 'z' in your scala/python environment. ZeppelinContext provides some additional functions and utility.
|
||||
|
||||
|
|
@ -256,3 +330,32 @@ select * from ${table=defaultTableName} where text like '%${search}%'
|
|||
```
|
||||
|
||||
To learn more about dynamic form, checkout [Dynamic Form](../manual/dynamicform.html).
|
||||
|
||||
|
||||
### Separate Interpreter for each note
|
||||
|
||||
In 'Separate Interpreter for each note' mode, SparkInterpreter creates scala compiler per each notebook. However it still shares the single SparkContext.
|
||||
|
||||
## Setting up Zeppelin with Kerberos
|
||||
Logical setup with Zeppelin, Kerberos Key Distribution Center (KDC), and Spark on YARN:
|
||||
|
||||
<img src="../assets/themes/zeppelin/img/docs-img/kdc_zeppelin.png">
|
||||
|
||||
####Configuration Setup
|
||||
|
||||
1. On the server that Zeppelin is installed, install Kerberos client modules and configuration, krb5.conf.
|
||||
This is to make the server communicate with KDC.
|
||||
|
||||
2. Set SPARK\_HOME in [ZEPPELIN\_HOME]/conf/zeppelin-env.sh to use spark-submit
|
||||
( Additionally, you might have to set “export HADOOP\_CONF\_DIR=/etc/hadoop/conf” )
|
||||
|
||||
3. Add the two properties below to spark configuration ( [SPARK_HOME]/conf/spark-defaults.conf ):
|
||||
|
||||
spark.yarn.principal
|
||||
spark.yarn.keytab
|
||||
|
||||
> **NOTE:** If you do not have access to the above spark-defaults.conf file, optionally, you may add the lines to the Spark Interpreter through the Interpreter tab in the Zeppelin UI.
|
||||
|
||||
4. That's it. Play with Zeppelin !
|
||||
|
||||
|
||||
|
|
|
|||
74
docs/manual/dependencymanagement.md
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
---
|
||||
layout: page
|
||||
title: "Dependnecy Management"
|
||||
description: ""
|
||||
group: manual
|
||||
---
|
||||
<!--
|
||||
Licensed 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.
|
||||
-->
|
||||
{% include JB/setup %}
|
||||
|
||||
## Dependency Management for Interpreter
|
||||
|
||||
You can include external libraries to interpreter by setting dependencies in interpreter menu.
|
||||
|
||||
When your code requires external library, instead of doing download/copy/restart Zeppelin, you can easily do following jobs in this menu.
|
||||
|
||||
* Load libraries recursively from Maven repository
|
||||
* Load libraries from local filesystem
|
||||
* Add additional maven repository
|
||||
* Automatically add libraries to SparkCluster
|
||||
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<a data-lightbox="compiler" href="{{BASE_PATH}}/assets/themes/zeppelin/img/docs-img/interpreter-dependency-loading.png">
|
||||
<img class="img-responsive" src="{{BASE_PATH}}/assets/themes/zeppelin/img/docs-img/interpreter-dependency-loading.png" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-6" style="padding-top:30px">
|
||||
<b> Load Dependencies to Interpreter </b>
|
||||
<br /><br />
|
||||
<ol>
|
||||
<li> Click 'Interpreter' menu in navigation bar. </li>
|
||||
<li> Click 'edit' button of the interpreter which you want to load dependencies to. </li>
|
||||
<li> Fill artifact and exclude field to your needs.
|
||||
You can enter not only groupId:artifactId:version but also local file in artifact field. </li>
|
||||
<li> Press 'Save' to restart the interpreter with loaded libraries. </li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<a data-lightbox="compiler" href="{{BASE_PATH}}/assets/themes/zeppelin/img/docs-img/interpreter-add-repo1.png">
|
||||
<img class="img-responsive" src="{{BASE_PATH}}/assets/themes/zeppelin/img/docs-img/interpreter-add-repo1.png" />
|
||||
</a>
|
||||
<a data-lightbox="compiler" href="{{BASE_PATH}}/assets/themes/zeppelin/img/docs-img/interpreter-add-repo2.png">
|
||||
<img class="img-responsive" src="{{BASE_PATH}}/assets/themes/zeppelin/img/docs-img/interpreter-add-repo2.png" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-6" style="padding-top:30px">
|
||||
<b> Add repository for dependency resolving </b>
|
||||
<br /><br />
|
||||
<ol>
|
||||
<li> Press <i class="fa fa-cog"></i> icon in 'Interpreter' menu on the top right side.
|
||||
It will show you available repository lists.</li>
|
||||
<li> If you need to resolve dependencies from other than central maven repository or
|
||||
local ~/.m2 repository, hit <i class="fa fa-plus"></i> icon next to repository lists. </li>
|
||||
<li> Fill out the form and click 'Add' button, then you will be able to see that new repository is added. </li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -54,6 +54,16 @@ Also you can separate option's display name and value, using _${formName=default
|
|||
|
||||
<img src="/assets/themes/zeppelin/img/screenshots/form_select_displayname.png" />
|
||||
|
||||
#### Checkbox form
|
||||
|
||||
For multi-selection, you can create a checkbox form using _${checkbox:formName=defaultValue1|defaultValue2...,option1|option2...}_. The variable will be substituted by a comma-separated string based on the selected items. For example:
|
||||
|
||||
<img src="/assets/themes/zeppelin/img/screenshots/form_checkbox.png">
|
||||
|
||||
Besides, you can specify the delimiter using _${checkbox(delimiter):formName=...}_:
|
||||
|
||||
<img src="/assets/themes/zeppelin/img/screenshots/form_checkbox_delimiter.png">
|
||||
|
||||
### Creates Programmatically
|
||||
|
||||
Some language backend uses programmatic way to create form. For example [ZeppelinContext](../interpreter/spark.html#zeppelincontext) provides form creation API
|
||||
|
|
@ -134,3 +144,26 @@ print("Hello "+z.select("day", [("1","mon"),
|
|||
</div>
|
||||
</div>
|
||||
<img src="/assets/themes/zeppelin/img/screenshots/form_select_prog.png" />
|
||||
|
||||
#### Checkbox form
|
||||
<div class="codetabs">
|
||||
<div data-lang="scala" markdown="1">
|
||||
|
||||
{% highlight scala %}
|
||||
%spark
|
||||
val options = Seq(("apple","Apple"), ("banana","Banana"), ("orange","Orange"))
|
||||
println("Hello "+z.checkbox("fruit", options).mkString(" and "))
|
||||
{% endhighlight %}
|
||||
|
||||
</div>
|
||||
<div data-lang="python" markdown="1">
|
||||
|
||||
{% highlight python %}
|
||||
%pyspark
|
||||
options = [("apple","Apple"), ("banana","Banana"), ("orange","Orange")]
|
||||
print("Hello "+ " and ".join(z.checkbox("fruit", options, ["apple"])))
|
||||
{% endhighlight %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<img src="/assets/themes/zeppelin/img/screenshots/form_checkbox_prog.png" />
|
||||
|
|
|
|||
|
|
@ -25,34 +25,37 @@ The concept of Zeppelin interpreter allows any language/data-processing-backend
|
|||
Currently, Zeppelin supports many interpreters such as Scala ( with Apache Spark ), Python ( with Apache Spark ), SparkSQL, Hive, Markdown, Shell and so on.
|
||||
|
||||
## What is Zeppelin interpreter?
|
||||
Zeppelin Interpreter is a plug-in which enables Zeppelin users to use a specific language/data-processing-backend. For example, to use scala code in Zeppelin, you need `%spark` interpreter.
|
||||
Zeppelin Interpreter is a plug-in which enables Zeppelin users to use a specific language/data-processing-backend. For example, to use Scala code in Zeppelin, you need `%spark` interpreter.
|
||||
|
||||
When you click the ```+Create``` button in the interpreter page, the interpreter drop-down list box will show all the available interpreters on your server.
|
||||
|
||||
<img src="/assets/themes/zeppelin/img/screenshots/interpreter_create.png">
|
||||
|
||||
## What is Zeppelin Interpreter Setting?
|
||||
Zeppelin interpreter setting is the configuration of a given interpreter on Zeppelin server. For example, the properties are required for hive JDBC interpreter to connect to the Hive server.
|
||||
Zeppelin interpreter setting is the configuration of a given interpreter on Zeppelin server. For example, the properties are required for hive JDBC interpreter to connect to the Hive server.
|
||||
|
||||
<img src="/assets/themes/zeppelin/img/screenshots/interpreter_setting.png">
|
||||
|
||||
Each notebook can be binded to multiple Interpreter Settings using setting icon on upper right corner of the notebook.
|
||||
|
||||
<img src="/assets/themes/zeppelin/img/screenshots/interpreter_binding.png" width="800px">
|
||||
|
||||
|
||||
|
||||
## What is Zeppelin Interpreter Group?
|
||||
Every Interpreter is belonged to an **Interpreter Group**. Interpreter Group is a unit of start/stop interpreter.
|
||||
By default, every interpreter is belonged to a single group, but the group might contain more interpreters. For example, spark interpreter group is including Spark support, pySpark,
|
||||
SparkSQL and the dependency loader.
|
||||
By default, every interpreter is belonged to a single group, but the group might contain more interpreters. For example, Spark interpreter group is including Spark support, pySpark, SparkSQL and the dependency loader.
|
||||
|
||||
Technically, Zeppelin interpreters from the same group are running in the same JVM. For more information about this, please checkout [here](../development/writingzeppelininterpreter.html).
|
||||
|
||||
Each interpreters is belonged to a single group and registered together. All of their properties are listed in the interpreter setting like below image.
|
||||
<img src="/assets/themes/zeppelin/img/screenshots/interpreter_setting_spark.png">
|
||||
|
||||
## Programming Languages for Interpreter
|
||||
If the interpreter uses a specific programming language ( like Scala, Python, SQL ), it is generally recommended to add a syntax highlighting supported for that to the notebook paragraph editor.
|
||||
|
||||
To check out the list of languages supported, see the `mode-*.js` files under `zeppelin-web/bower_components/ace-builds/src-noconflict` or from [github.com/ajaxorg/ace-builds](https://github.com/ajaxorg/ace-builds/tree/master/src-noconflict).
|
||||
|
||||
If you want to add a new set of syntax highlighting,
|
||||
|
||||
1. Add the `mode-*.js` file to `zeppelin-web/bower.json` ( when built, `zeppelin-web/src/index.html` will be changed automatically. ).
|
||||
2. Add to the list of `editorMode` in `zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js` - it follows the pattern 'ace/mode/x' where x is the name.
|
||||
3. Add to the code that checks for `%` prefix and calls `session.setMode(editorMode.x)` in `setParagraphMode` located in `zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js`.
|
||||
## Interpreter binding mode
|
||||
|
||||
Each Interpreter Setting can choose one of two different interpreter binding mode.
|
||||
Shared mode (default) and 'Separate Interpreter for each note' mode. In shared mode, every notebook binded to the Interpreter Setting will share the single Interpreter instance. In 'Separate Interpreter for each note' mode, each notebook will create new Interpreter instance. Therefore each notebook will have fresh new Interpreter environment.
|
||||
|
||||
<img src="/assets/themes/zeppelin/img/screenshots/interpreter_persession.png" width="400px">
|
||||
|
||||
|
|
|
|||
48
docs/manual/publish.md
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
layout: page
|
||||
title: "Publish your Paragraph"
|
||||
description: ""
|
||||
group: manual
|
||||
---
|
||||
<!--
|
||||
Licensed 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.
|
||||
-->
|
||||
{% include JB/setup %}
|
||||
|
||||
## How can you publish your paragraph ?
|
||||
Zeppelin provides a feature for publishing your notebook paragraph results. Using this feature, you can show Zeppelin notebook paragraph results in your own website.
|
||||
It's very straightforward. Just use `<iframe>` tag in your page.
|
||||
|
||||
> **Warning**: Please use this feature with caution and in a trusted environment only, as Zeppelin entire Webapp could be accessible for whoever visits your website.
|
||||
|
||||
### Copy a Paragraph Link
|
||||
A first step to publish your paragraph result is **Copy a Paragraph Link**.
|
||||
|
||||
* After running a paragraph in your Zeppelin notebook, click a gear button located on the right side. Then, click **Link this Paragraph** menu like below image.
|
||||
<center><img src="../assets/themes/zeppelin/img/docs-img/link-the-paragraph.png" height="100%" width="100%"></center>
|
||||
|
||||
* Just copy the provided link.
|
||||
<center><img src="../assets/themes/zeppelin/img/docs-img/copy-the-link.png" height="100%" width="100%"></center>
|
||||
|
||||
### Embed the Paragraph to Your Website
|
||||
For publishing the copied paragraph, you may use `<iframe>` tag in your website page.
|
||||
For example,
|
||||
|
||||
```
|
||||
<iframe src="http://< ip-address >:< port >/#/notebook/2B3QSZTKR/paragraph/...?asIframe" height="" width="" ></iframe>
|
||||
```
|
||||
|
||||
Finally, you can show off your beautiful visualization results in your website.
|
||||
<center><img src="../assets/themes/zeppelin/img/docs-img/your-website.png" height="90%" width="90%"></center>
|
||||
|
||||
> **Note**: To embed the paragraph in a website, Zeppelin needs to be reachable by that website.
|
||||
72
docs/manual/shiroauthentication.md
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
---
|
||||
layout: page
|
||||
title: "Shiro Security for Apache Zeppelin"
|
||||
description: ""
|
||||
group: manual
|
||||
---
|
||||
<!--
|
||||
Licensed 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.
|
||||
-->
|
||||
{% include JB/setup %}
|
||||
|
||||
# Shiro authentication for Apache Zeppelin
|
||||
[Apache Shiro](http://shiro.apache.org/) is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. In this documentation, we will explain step by step how Shiro works for Zeppelin notebook authentication.
|
||||
|
||||
When you connect to Apache Zeppelin, you will be asked to enter your credentials. Once you logged in, then you have access to all notes including other user's notes.
|
||||
|
||||
## Security Setup
|
||||
You can setup **Zeppelin notebook authentication** in some simple steps.
|
||||
|
||||
####1. Secure the HTTP channel
|
||||
To secure the HTTP channel, you have to change both **anon** and **authcBasic** settings in `conf/shiro.ini`. In here, **anon** means "the access is anonymous" and **authcBasic** means "basic auth security".
|
||||
|
||||
The default status of them is
|
||||
|
||||
```
|
||||
/** = anon
|
||||
#/** = authcBasic
|
||||
```
|
||||
Deactivate the line "/** = anon" and activate the line "/** = authcBasic" in `conf/shiro.ini` file.
|
||||
|
||||
```
|
||||
#/** = anon
|
||||
/** = authcBasic
|
||||
```
|
||||
|
||||
For the further information about `shiro.ini` file format, please refer to [Shiro Configuration](http://shiro.apache.org/configuration.html#Configuration-INISections).
|
||||
|
||||
####2. Secure the Websocket channel
|
||||
Set to property **zeppelin.anonymous.allowed** to **false** in `conf/zeppelin-site.xml`. If you don't have this file yet, just copy `conf/zeppelin-site.xml.template` to `conf/zeppelin-site.xml`.
|
||||
|
||||
####3. Start Zeppelin
|
||||
|
||||
```
|
||||
bin/zeppelin-daemon.sh start (or restart)
|
||||
```
|
||||
|
||||
Then you can browse Zeppelin at [http://localhost:8080](http://localhost:8080).
|
||||
|
||||
####4. Login
|
||||
Finally, you can login using one of the below **username/password** combinations.
|
||||
|
||||
<center><img src="../assets/themes/zeppelin/img/docs-img/zeppelin-login.png" width="40%" height="40%"></center>
|
||||
|
||||
```
|
||||
admin = password1
|
||||
user1 = password2
|
||||
user2 = password3
|
||||
```
|
||||
|
||||
Those combinations are defined in the `conf/shiro.ini` file.
|
||||
|
||||
> **NOTE :** This documentation is originally from [SECURITY-README.md](https://github.com/apache/incubator-zeppelin/blob/master/SECURITY-README.md).
|
||||
143
docs/rest-api/rest-configuration.md
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
---
|
||||
layout: page
|
||||
title: "Configuration REST API"
|
||||
description: ""
|
||||
group: rest-api
|
||||
---
|
||||
<!--
|
||||
Licensed 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.
|
||||
-->
|
||||
{% include JB/setup %}
|
||||
|
||||
## Zeppelin REST API
|
||||
Zeppelin provides several REST API's for interaction and remote activation of zeppelin functionality.
|
||||
|
||||
All REST API are available starting with the following endpoint ```http://[zeppelin-server]:[zeppelin-port]/api```
|
||||
|
||||
Note that zeppein REST API receive or return JSON objects, it it recommended you install some JSON viewers such as
|
||||
[JSONView](https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc)
|
||||
|
||||
|
||||
If you work with zeppelin and find a need for an additional REST API please [file an issue or send us mail](../../community.html)
|
||||
|
||||
<br />
|
||||
### Configuration REST API list
|
||||
|
||||
<table class="table-configuration">
|
||||
<col width="200">
|
||||
<tr>
|
||||
<th>List configurations</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```GET``` method return all key/value pair of configurations on the server.<br/>
|
||||
Note: For security reason, some pairs would not be shown.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/configurations/all```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
<td>200</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> Fail code</td>
|
||||
<td> 500 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> sample JSON response
|
||||
</td>
|
||||
<td>
|
||||
<pre>
|
||||
{
|
||||
"status":"OK",
|
||||
"message":"",
|
||||
"body":{
|
||||
"zeppelin.war.tempdir":"webapps",
|
||||
"zeppelin.notebook.homescreen.hide":"false",
|
||||
"zeppelin.interpreter.remoterunner":"bin/interpreter.sh",
|
||||
"zeppelin.notebook.s3.user":"user",
|
||||
"zeppelin.server.port":"8089",
|
||||
"zeppelin.dep.localrepo":"local-repo",
|
||||
"zeppelin.ssl.truststore.type":"JKS",
|
||||
"zeppelin.ssl.keystore.path":"keystore",
|
||||
"zeppelin.notebook.s3.bucket":"zeppelin",
|
||||
"zeppelin.server.addr":"0.0.0.0",
|
||||
"zeppelin.ssl.client.auth":"false",
|
||||
"zeppelin.server.context.path":"/",
|
||||
"zeppelin.ssl.keystore.type":"JKS",
|
||||
"zeppelin.ssl.truststore.path":"truststore",
|
||||
"zeppelin.interpreters":"org.apache.zeppelin.spark.SparkInterpreter,org.apache.zeppelin.spark.PySparkInterpreter,org.apache.zeppelin.spark.SparkSqlInterpreter,org.apache.zeppelin.spark.DepInterpreter,org.apache.zeppelin.markdown.Markdown,org.apache.zeppelin.angular.AngularInterpreter,org.apache.zeppelin.shell.ShellInterpreter,org.apache.zeppelin.hive.HiveInterpreter,org.apache.zeppelin.tajo.TajoInterpreter,org.apache.zeppelin.flink.FlinkInterpreter,org.apache.zeppelin.lens.LensInterpreter,org.apache.zeppelin.ignite.IgniteInterpreter,org.apache.zeppelin.ignite.IgniteSqlInterpreter,org.apache.zeppelin.cassandra.CassandraInterpreter,org.apache.zeppelin.geode.GeodeOqlInterpreter,org.apache.zeppelin.postgresql.PostgreSqlInterpreter,org.apache.zeppelin.phoenix.PhoenixInterpreter,org.apache.zeppelin.kylin.KylinInterpreter,org.apache.zeppelin.elasticsearch.ElasticsearchInterpreter,org.apache.zeppelin.scalding.ScaldingInterpreter",
|
||||
"zeppelin.ssl":"false",
|
||||
"zeppelin.notebook.autoInterpreterBinding":"true",
|
||||
"zeppelin.notebook.homescreen":"",
|
||||
"zeppelin.notebook.storage":"org.apache.zeppelin.notebook.repo.VFSNotebookRepo",
|
||||
"zeppelin.interpreter.connect.timeout":"30000",
|
||||
"zeppelin.anonymous.allowed":"true",
|
||||
"zeppelin.server.allowed.origins":"*",
|
||||
"zeppelin.encoding":"UTF-8"
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br/>
|
||||
|
||||
<table class="table-configuration">
|
||||
<col width="200">
|
||||
<tr>
|
||||
<th>List configurations (prefix match)</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```GET``` method return all prefix matched key/value pair of configurations on the server.<br/>
|
||||
Note: For security reason, some pairs would not be shown.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/configurations/prefix/[prefix]```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
<td>200</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> Fail code</td>
|
||||
<td> 500 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> sample JSON response
|
||||
</td>
|
||||
<td>
|
||||
<pre>
|
||||
{
|
||||
"status":"OK",
|
||||
"message":"",
|
||||
"body":{
|
||||
"zeppelin.ssl.keystore.type":"JKS",
|
||||
"zeppelin.ssl.truststore.path":"truststore",
|
||||
"zeppelin.ssl.truststore.type":"JKS",
|
||||
"zeppelin.ssl.keystore.path":"keystore",
|
||||
"zeppelin.ssl":"false",
|
||||
"zeppelin.ssl.client.auth":"false"
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
@ -23,10 +23,9 @@ limitations under the License.
|
|||
Zeppelin provides several REST API's for interaction and remote activation of zeppelin functionality.
|
||||
|
||||
All REST API are available starting with the following endpoint `http://[zeppelin-server]:[zeppelin-port]/api`.
|
||||
Note that zeppein REST API receive or return JSON objects, it it recommended you install some JSON view such as
|
||||
Note that zeppein REST API receive or return JSON objects, it it recommended you install some JSON viewers such as
|
||||
[JSON View](https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc).
|
||||
|
||||
|
||||
If you work with zeppelin and find a need for an additional REST API, please [file an issue or send us mail](http://zeppelin.incubator.apache.org/community.html).
|
||||
|
||||
<br />
|
||||
|
|
@ -152,7 +151,8 @@ limitations under the License.
|
|||
"class": "org.apache.zeppelin.markdown.Markdown",
|
||||
"name": "md"
|
||||
}
|
||||
]
|
||||
],
|
||||
"dependencies": []
|
||||
},
|
||||
{
|
||||
"id": "2AY6GV7Q3",
|
||||
|
|
@ -171,6 +171,11 @@ limitations under the License.
|
|||
"class": "org.apache.zeppelin.spark.SparkSqlInterpreter",
|
||||
"name": "sql"
|
||||
}
|
||||
],
|
||||
"dependencies": [
|
||||
{
|
||||
"groupArtifactVersion": "com.databricks:spark-csv_2.10:1.3.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
@ -220,6 +225,12 @@ limitations under the License.
|
|||
"class": "org.apache.zeppelin.markdown.Markdown",
|
||||
"name": "md"
|
||||
}
|
||||
],
|
||||
"dependencies": [
|
||||
{
|
||||
"groupArtifactVersion": "groupId:artifactId:version",
|
||||
"exclusions": "groupId:artifactId"
|
||||
}
|
||||
]
|
||||
}
|
||||
</pre>
|
||||
|
|
@ -244,6 +255,12 @@ limitations under the License.
|
|||
"class": "org.apache.zeppelin.markdown.Markdown",
|
||||
"name": "md"
|
||||
}
|
||||
],
|
||||
"dependencies": [
|
||||
{
|
||||
"groupArtifactVersion": "groupId:artifactId:version",
|
||||
"exclusions": "groupId:artifactId"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -293,6 +310,12 @@ limitations under the License.
|
|||
"class": "org.apache.zeppelin.markdown.Markdown",
|
||||
"name": "md"
|
||||
}
|
||||
],
|
||||
"dependencies": [
|
||||
{
|
||||
"groupArtifactVersion": "groupId:artifactId:version",
|
||||
"exclusions": "groupId:artifactId"
|
||||
}
|
||||
]
|
||||
}
|
||||
</pre>
|
||||
|
|
@ -317,6 +340,12 @@ limitations under the License.
|
|||
"class": "org.apache.zeppelin.markdown.Markdown",
|
||||
"name": "md"
|
||||
}
|
||||
],
|
||||
"dependencies": [
|
||||
{
|
||||
"groupArtifactVersion": "groupId:artifactId:version",
|
||||
"exclusions": "groupId:artifactId"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -392,3 +421,75 @@ limitations under the License.
|
|||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br/>
|
||||
### 6. Add repository for dependency resolving
|
||||
|
||||
<table class="table-configuration">
|
||||
<col width="200">
|
||||
<tr>
|
||||
<th>Add new repository for dependency loader</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```POST``` method adds new repository.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/interpreter/repository```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
<td>201</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Fail code</td>
|
||||
<td> 500 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sample JSON input</td>
|
||||
<td>
|
||||
<pre>
|
||||
{
|
||||
"id": "securecentral",
|
||||
"url": "https://repo1.maven.org/maven2",
|
||||
"snapshot": false
|
||||
}
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sample JSON response</td>
|
||||
<td>
|
||||
<code>{"status":"OK"}</code>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br/>
|
||||
### 7. Delete repository for dependency resolving
|
||||
|
||||
<table class="table-configuration">
|
||||
<col width="200">
|
||||
<tr>
|
||||
<th>Delete repository for dependency loader</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```DELETE``` method delete repository with given id.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/interpreter/repository/[repository ID]```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
<td>200</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Fail code</td>
|
||||
<td> 500 </td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ limitations under the License.
|
|||
|
||||
All REST APIs are available starting with the following endpoint ```http://[zeppelin-server]:[zeppelin-port]/api```
|
||||
|
||||
Note that zeppelin REST APIs receive or return JSON objects, it is recommended for you to install some JSON viewer
|
||||
Note that zeppelin REST APIs receive or return JSON objects, it is recommended for you to install some JSON viewers
|
||||
such as [JSONView](https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc)
|
||||
|
||||
|
||||
|
|
@ -33,7 +33,7 @@ limitations under the License.
|
|||
<br />
|
||||
### Notebook REST API list
|
||||
|
||||
Notebooks REST API supports the following operations: List, Create, Get, Delete, Clone, Run as detailed in the following table
|
||||
Notebooks REST API supports the following operations: List, Create, Get, Delete, Clone, Run, Export, Import as detailed in the following table
|
||||
|
||||
<table class="table-configuration">
|
||||
<col width="200">
|
||||
|
|
@ -773,3 +773,112 @@ limitations under the License.
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
<table class="table-configuration">
|
||||
<col width="200">
|
||||
<tr>
|
||||
<th>Export notebook</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```GET``` method exports a notebook by the given id and gernerates a JSON
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/export/[notebookId]```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
<td>201</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> Fail code</td>
|
||||
<td> 500 </td>
|
||||
</tr>
|
||||
<td> sample JSON response </td>
|
||||
<td><pre>{
|
||||
"paragraphs": [
|
||||
{
|
||||
"text": "%md This is my new paragraph in my new note",
|
||||
"dateUpdated": "Jan 8, 2016 4:49:38 PM",
|
||||
"config": {
|
||||
"enabled": true
|
||||
},
|
||||
"settings": {
|
||||
"params": {},
|
||||
"forms": {}
|
||||
},
|
||||
"jobName": "paragraph_1452300578795_1196072540",
|
||||
"id": "20160108-164938_1685162144",
|
||||
"dateCreated": "Jan 8, 2016 4:49:38 PM",
|
||||
"status": "READY",
|
||||
"progressUpdateIntervalMs": 500
|
||||
}
|
||||
],
|
||||
"name": "source note for export",
|
||||
"id": "2B82H3RR1",
|
||||
"angularObjects": {},
|
||||
"config": {},
|
||||
"info": {}
|
||||
}</pre></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="table-configuration">
|
||||
<col width="200">
|
||||
<tr>
|
||||
<th>Export notebook</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>This ```POST``` method imports a notebook from the notebook JSON input
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/import```</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Success code</td>
|
||||
<td>201</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> Fail code</td>
|
||||
<td> 500 </td>
|
||||
</tr>
|
||||
<td> sample JSON input </td>
|
||||
<td><pre>{
|
||||
"paragraphs": [
|
||||
{
|
||||
"text": "%md This is my new paragraph in my new note",
|
||||
"dateUpdated": "Jan 8, 2016 4:49:38 PM",
|
||||
"config": {
|
||||
"enabled": true
|
||||
},
|
||||
"settings": {
|
||||
"params": {},
|
||||
"forms": {}
|
||||
},
|
||||
"jobName": "paragraph_1452300578795_1196072540",
|
||||
"id": "20160108-164938_1685162144",
|
||||
"dateCreated": "Jan 8, 2016 4:49:38 PM",
|
||||
"status": "READY",
|
||||
"progressUpdateIntervalMs": 500
|
||||
}
|
||||
],
|
||||
"name": "source note for export",
|
||||
"id": "2B82H3RR1",
|
||||
"angularObjects": {},
|
||||
"config": {},
|
||||
"info": {}
|
||||
}</pre></td>
|
||||
<tr>
|
||||
<td> sample JSON response </td>
|
||||
<td><pre>"status": "CREATED","message": "","body": "2AZPHY918"}</pre></td>
|
||||
</tr>
|
||||
</tr>
|
||||
</table>
|
||||
31
docs/security/authentication.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
layout: page
|
||||
title: "Authentication"
|
||||
description: "Authentication"
|
||||
group: security
|
||||
---
|
||||
<!--
|
||||
Licensed 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.
|
||||
-->
|
||||
# Authentication
|
||||
|
||||
Authentication is company-specific.
|
||||
|
||||
One option is to use [Basic Access Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)
|
||||
|
||||
Another option is to have an authentication server that can verify user credentials in an LDAP server.
|
||||
If an incoming request to the Zeppelin server does not have a cookie with user information encrypted with the authentication server public key, the user
|
||||
is redirected to the authentication server. Once the user is verified, the authentication server redirects the browser to a specific
|
||||
URL in the Zeppelin server which sets the authentication cookie in the browser.
|
||||
The end result is that all requests to the Zeppelin
|
||||
web server have the authentication cookie which contains user and groups information.
|
||||
34
docs/security/interpreter_authorization.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
layout: page
|
||||
title: "Notebook Authorization"
|
||||
description: "Notebook Authorization"
|
||||
group: security
|
||||
---
|
||||
<!--
|
||||
Licensed 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.
|
||||
-->
|
||||
# Interpreter and Data Source Authorization
|
||||
|
||||
## Interpreter Authorization
|
||||
|
||||
Interpreter authorization involves permissions like creating an interpreter and execution queries using it.
|
||||
|
||||
## Data Source Authorization
|
||||
|
||||
Data source authorization involves authenticating to the data source like a Mysql database and letting it determine user permissions.
|
||||
|
||||
For the Hive interpreter, we need to maintain per-user connection pools.
|
||||
The interpret method takes the user string as parameter and executes the jdbc call using a connection in the user's connection pool.
|
||||
|
||||
In case of Presto, we don't need password if the Presto DB server runs backend code using HDFS authorization for the user.
|
||||
For databases like Vertica and Mysql we have to store password information for users.
|
||||
37
docs/security/notebook_authorization.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
---
|
||||
layout: page
|
||||
title: "Notebook Authorization"
|
||||
description: "Notebook Authorization"
|
||||
group: security
|
||||
---
|
||||
<!--
|
||||
Licensed 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.
|
||||
-->
|
||||
# Notebook Authorization
|
||||
|
||||
We assume that there is an authentication component that associates a user string and a set of group strings with every NotebookSocket.
|
||||
|
||||
Each note has the following:
|
||||
* set of owner entities (users or groups)
|
||||
* set of reader entities (users or groups)
|
||||
* set of writer entities (users or groups)
|
||||
|
||||
If a set is empty, it means that any user can perform that operation.
|
||||
|
||||
The NotebookServer classifies every Note operation into three categories: read, write, manage.
|
||||
Before executing a Note operation, it checks if the user and the groups associated with the NotebookSocket have permissions. For example, before executing an read
|
||||
operation, it checks if the user and the groups have at least one entity that belongs to the reader entities.
|
||||
|
||||
To initialize and modify note permissions, we provide UI like "Interpreter binding". The user inputs comma separated entities for owners, readers and writers.
|
||||
We execute a rest api call with this information. In the backend we get the user information for the connection and allow the operation if the user and groups
|
||||
associated with the current user have at least one entity that belongs to owner entities for the note.
|
||||
28
docs/security/overview.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
layout: page
|
||||
title: "Security Overview"
|
||||
description: "Security Overview"
|
||||
group: security
|
||||
---
|
||||
<!--
|
||||
Licensed 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.
|
||||
-->
|
||||
{% include JB/setup %}
|
||||
|
||||
# Security Overview
|
||||
|
||||
There are three aspects to Zeppelin security:
|
||||
|
||||
* Authentication: is the user who they say they are? [More](authentication.html)
|
||||
* Notebook authorization: does the user have permissions to read or write to a note? [More](notebook_authorization.html)
|
||||
* Interpreter and data source authorization: does the user have permissions to perform interpreter operations or access data source objects? [More](interpreter_authorization.html)
|
||||