azkaban-aplcache
Changes
build.gradle 154(+113 -41)
src/main/java/azkaban/flow/Edge.java 0(+0 -0)
src/main/java/azkaban/flow/Flow.java 0(+0 -0)
src/main/java/azkaban/flow/Node.java 0(+0 -0)
src/main/java/azkaban/user/Role.java 0(+0 -0)
src/main/java/azkaban/user/User.java 0(+0 -0)
src/main/less/.gitignore 1(+1 -0)
src/main/less/azkaban.less 0(+0 -0)
src/main/less/azkaban-graph.less 165(+165 -0)
src/main/less/base.less 160(+160 -0)
src/main/less/callout.less 0(+0 -0)
src/main/less/context-menu.less 0(+0 -0)
src/main/less/flow.less 342(+342 -0)
src/main/less/header.less 0(+0 -0)
src/main/less/log.less 0(+0 -0)
src/main/less/login.less 0(+0 -0)
src/main/less/Makefile 0(+0 -0)
src/main/less/navbar.less 121(+121 -0)
src/main/less/non-responsive.less 88(+88 -0)
src/main/less/off-canvas.less 0(+0 -0)
src/main/less/project.less 142(+142 -0)
src/main/less/tables.less 149(+149 -0)
src/main/less/variables.less 0(+0 -0)
src/main/resources/log4j.properties 0(+0 -0)
src/main/tl/.gitignore 1(+1 -0)
src/main/tl/flowstats.tl 155(+155 -0)
src/main/tl/flowstats-no-data.tl 0(+0 -0)
src/main/tl/flowsummary.tl 69(+69 -0)
src/main/tl/Makefile 0(+0 -0)
Details
build.gradle 154(+113 -41)
diff --git a/build.gradle b/build.gradle
index 22ef51b..0ea523c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -22,6 +22,7 @@ def getVersionName = { ->
return cmdCaller(['git', 'describe', '--tags', '--abbrev=0'])
}
+
version = getVersionName()
archivesBaseName = 'azkaban'
check.dependsOn.remove(test)
@@ -109,30 +110,11 @@ eclipse.classpath.file {
}
}
-sourceSets {
- main {
- java {
- srcDir 'src/java'
- }
- resources {
- // Used to include all vm's and property files in the src code
- srcDir 'src/java'
- include '**/*.vm'
- include '**/*.properties'
- }
- }
- test {
- java {
- srcDir 'unit/java'
- }
- }
-}
-
/**
* Invokes a makefile target that will compile less files
*/
task compileLess(type:Exec) {
- workingDir 'src/less'
+ workingDir 'src/main/less'
commandLine 'make', '-e'
environment (
OBJ_DIR : file(new File(buildDir,'/less'))
@@ -143,7 +125,7 @@ task compileLess(type:Exec) {
* Invokes a makefile target that will compile dust files
*/
task compileDust(type:Exec) {
- workingDir 'src/tl'
+ workingDir 'src/main/tl'
commandLine 'make', '-e'
environment (
OBJ_DIR : file(new File(buildDir,'/dust'))
@@ -185,13 +167,14 @@ task createVersionFile() << {
formattedDate + '\n'
File versionFile = file('build/package/version.file')
+ versionFile.parentFile.mkdirs()
versionFile.write(versionStr)
-
}
-task packageSolo(dependsOn: [jar, 'web', 'createVersionFile']) << {
- String packageDir = 'build/package/azkaban-solo-server'
-
+task packageSolo(type: Tar, dependsOn: [jar, 'web', 'createVersionFile']) {
+ appendix = 'solo-server'
+ packageDir = 'build/package/' + baseName + '-' + appendix
+
println 'Creating Azkaban Solo Server Package into ' + packageDir
mkdir packageDir
mkdir packageDir + '/extlib'
@@ -205,13 +188,13 @@ task packageSolo(dependsOn: [jar, 'web', 'createVersionFile']) << {
println 'Copying Azkaban lib'
copy {
- from('$buildDir/libs')
+ from('build/libs')
into(packageDir + '/lib')
}
println 'Copying web'
copy {
- from('$buildDir/web')
+ from('build/web')
into(packageDir + '/web')
}
@@ -231,10 +214,63 @@ task packageSolo(dependsOn: [jar, 'web', 'createVersionFile']) << {
into packageDir
from 'build/package/version.file'
}
+
+ println 'Tarballing Solo Package'
+ extension = 'tar.gz'
+ compression = Compression.GZIP
+
+ basedir = baseName + '-' + appendix + '-' + version
+ println 'Source is in ' + packageDir
+ into(basedir) {
+ from packageDir
+ exclude 'bin'
+ }
+
+ dst_bin = basedir + '/bin'
+ src_bin = packageDir + '/bin'
+ from(src_bin) {
+ into dst_bin
+ fileMode = 0755
+ }
}
-task packageExec(dependsOn: [jar, 'web', 'createVersionFile']) << {
- String packageDir = 'build/package/azkaban-exec-server'
+task packageSql(type: Tar) {
+ String packageDir = 'build/package/sql'
+
+ println 'Creating Azkaban Solo Server Package into ' + packageDir
+ mkdir packageDir
+
+ println 'Copying SQL files'
+ copy {
+ from('src/sql')
+ into(packageDir)
+ }
+
+ String destFile = packageDir + '/create-all-sql-' + version + '.sql';
+ println('Concating create scripts to ' + destFile)
+ ant.concat(destfile:destFile, fixlastline:'yes') {
+ fileset(dir: 'src/sql') {
+ exclude(name: 'update.*.sql')
+ exclude(name: 'database.properties')
+ }
+ }
+
+ println 'Tarballing SQL Package'
+ extension = 'tar.gz'
+ compression = Compression.GZIP
+ appendix = 'sql'
+
+ basedir = baseName + '-' + appendix + '-' + version
+ packageDir = 'build/package/sql'
+ println 'Source is in ' + packageDir
+ into(basedir) {
+ from packageDir
+ }
+}
+
+task packageExec(type: Tar, dependsOn: [jar, 'createVersionFile']) {
+ appendix = 'exec-server'
+ String packageDir = 'build/package/' + baseName + '-' + appendix
println 'Creating Azkaban Executor Server Package into ' + packageDir
mkdir packageDir
@@ -249,15 +285,9 @@ task packageExec(dependsOn: [jar, 'web', 'createVersionFile']) << {
println 'Copying Azkaban lib'
copy {
- from('$buildDir/libs')
+ from('build/libs')
into(packageDir + '/lib')
}
-
- println 'Copying web'
- copy {
- from('$buildDir/web')
- into(packageDir + '/web')
- }
println 'Copying dependency jars'
copy {
@@ -269,10 +299,31 @@ task packageExec(dependsOn: [jar, 'web', 'createVersionFile']) << {
into packageDir
from 'build/package/version.file'
}
+
+ println 'Tarballing Web Package'
+ extension = 'tar.gz'
+ compression = Compression.GZIP
+
+ basedir = baseName + '-' + appendix + '-' + version
+ packageDir = 'build/package/' + baseName + '-' + appendix
+ println 'Source is in ' + packageDir
+
+ into(basedir) {
+ from packageDir
+ exclude 'bin'
+ }
+
+ dst_bin = basedir + '/bin'
+ src_bin = packageDir + '/bin'
+ from(src_bin) {
+ into dst_bin
+ fileMode = 0755
+ }
}
-task packageWeb(dependsOn: [jar, 'web', 'createVersionFile']) << {
- String packageDir = 'build/package/azkaban-web-server'
+task packageWeb(type: Tar, dependsOn: [jar, 'web', 'createVersionFile']) {
+ appendix = 'web-server'
+ String packageDir = 'build/package/' + baseName + '-' + appendix
println 'Creating Azkaban Web Server Package into ' + packageDir
mkdir packageDir
@@ -287,13 +338,13 @@ task packageWeb(dependsOn: [jar, 'web', 'createVersionFile']) << {
println 'Copying Azkaban lib'
copy {
- from('$buildDir/libs')
+ from('build/libs')
into(packageDir + '/lib')
}
println 'Copying web'
copy {
- from('$buildDir/web')
+ from('build/web')
into(packageDir + '/web')
}
@@ -307,4 +358,25 @@ task packageWeb(dependsOn: [jar, 'web', 'createVersionFile']) << {
into packageDir
from 'build/package/version.file'
}
-}
+
+ println 'Tarballing Web Package'
+ extension = 'tar.gz'
+ compression = Compression.GZIP
+
+ basedir = baseName + '-' + appendix + '-' + version
+ println 'Source is in ' + packageDir
+ into(basedir) {
+ from packageDir
+ exclude 'bin'
+ }
+
+ dst_bin = basedir + '/bin'
+ src_bin = packageDir + '/bin'
+ from(src_bin) {
+ into dst_bin
+ fileMode = 0755
+ }
+}
+
+task packageAll(dependsOn : ['packageWeb', 'packageExec', 'packageSolo', 'packageSql']) {
+}
\ No newline at end of file
src/main/less/.gitignore 1(+1 -0)
diff --git a/src/main/less/.gitignore b/src/main/less/.gitignore
new file mode 100644
index 0000000..2416a67
--- /dev/null
+++ b/src/main/less/.gitignore
@@ -0,0 +1 @@
+obj/
src/main/less/azkaban-graph.less 165(+165 -0)
diff --git a/src/main/less/azkaban-graph.less b/src/main/less/azkaban-graph.less
new file mode 100644
index 0000000..50d818c
--- /dev/null
+++ b/src/main/less/azkaban-graph.less
@@ -0,0 +1,165 @@
+.nodebox {
+ text {
+ pointer-events: none;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ }
+
+ image {
+ pointer-events: none;
+ }
+
+ > .border:hover {
+ fill-opacity: 0.7;
+ }
+
+ > .flowborder:hover {
+ stroke-opacity: 0.7;
+ }
+}
+
+/* Nodes */
+.node {
+ &:hover {
+ cursor: pointer;
+ }
+
+ &.selected > .nodebox .border {
+ stroke-width: 3;
+ stroke: #39b3d7;
+ }
+
+ &.selected > .nodebox .flowborder {
+ stroke-width: 3;
+ fill: #D9EDFF;
+ }
+}
+
+.border {
+ stroke-width: 1;
+}
+
+.flownode .nodebox .flowborder {
+ stroke-width: 1.25;
+ fill: #FFF;
+ fill-opacity: 0.8;
+}
+
+.READY > g > rect {
+ fill: #DDD;
+ stroke: #CCC;
+}
+
+.READY > g > text {
+ fill: #000;
+}
+
+.RUNNING > g > rect {
+ fill: #39b3d7;
+ stroke: #39b3d7;
+}
+
+.RUNNING > g > text {
+ fill: #FFF;
+}
+
+.SUCCEEDED > g > rect {
+ fill: #5cb85c;
+ stroke: #4cae4c;
+}
+
+.SUCCEEDED > g > text {
+ fill: #FFF;
+}
+
+.FAILED > g > rect {
+ fill: #d2322d;
+ stroke: #d2322d;
+}
+
+.FAILED > g > text {
+ fill: #FFF;
+}
+
+.KILLED > g > rect {
+ fill: #d2322d;
+ stroke: #d2322d;
+}
+
+.KILLED > g > text {
+ fill: #FFF;
+}
+
+.CANCELLED > g > rect {
+ fill: #FF9999;
+ stroke: #FF9999;
+}
+
+.CANCELLED > g > text {
+ fill: #FFF;
+}
+
+.FAILED_FINISHING > g > rect {
+ fill: #ed9c28;
+ stroke: #ed9c28;
+}
+
+.FAILED_FINISHING > g > text {
+ fill: #FFF;
+}
+
+.DISABLED > g > rect {
+ fill: #DDD;
+ stroke: #CCC;
+}
+
+.DISABLED > g > rect {
+ fill: #DDD;
+ stroke: #CCC;
+}
+
+.nodeDisabled {
+ opacity: 0.25;
+}
+
+.SKIPPED > g > rect {
+ fill: #DDD;
+ stroke: #CCC;
+}
+
+.DISABLED {
+ opacity: 0.25;
+}
+
+.SKIPPED {
+ opacity: 0.25;
+}
+
+.QUEUED > g > rect {
+ fill: #39b3d7;
+ stroke: #39b3d7;
+}
+
+.QUEUED > g > text {
+ fill: #FFF;
+}
+
+.QUEUED {
+ opacity: 0.5;
+}
+
+/* Edges */
+.edge {
+ stroke: #CCC;
+ stroke-width: 1.5;
+
+ &:hover {
+ stroke: #009FC9;
+ stroke-width: 1.5;
+ }
+}
+
src/main/less/base.less 160(+160 -0)
diff --git a/src/main/less/base.less b/src/main/less/base.less
new file mode 100644
index 0000000..62861ca
--- /dev/null
+++ b/src/main/less/base.less
@@ -0,0 +1,160 @@
+.container-full {
+ padding: 0 105px;
+ margin: 0 auto;
+ width: 100%;
+ max-width: none;
+ min-width: 1075px;
+}
+
+.container-fill {
+ position: absolute;
+ top: 230px;
+ bottom: 50px;
+
+ .row {
+ height: 100%;
+ }
+
+ .col-sidebar {
+ height: 100%;
+ }
+
+ .col-content {
+ height: 100%;
+ }
+}
+
+.alert-default {
+ color: #a0a0a0;
+ background-color: #f5f5f5;
+ border-color: #dddddd;
+
+ hr {
+ border-top-color: #cccccc;
+ }
+
+ .alert-link {
+ color: #a0a0a0;
+ }
+}
+
+// Wide modal used for certain panels such as executing flow panel.
+.modal-wide .modal-dialog {
+ width: 80%;
+}
+
+// Hide messaging alert by default.
+.alert-messaging {
+ display: none;
+}
+
+// Add additional space under navs.
+.nav-tabs, .nav-pills {
+ margin-bottom: 15px;
+}
+
+.panel-list {
+ border: 0;
+}
+
+.list-group-collapse {
+ margin: 0;
+ .list-group-item {
+ border-radius: 0;
+ border-left: 0;
+ border-right: 0;
+
+ &:first-child {
+ border-top: 0;
+ }
+
+ &:last-child {
+ border-bottom: 0;
+ }
+
+ button {
+ margin-left: 3px;
+ }
+ }
+}
+
+@media (min-width: 768px) {
+ .form-horizontal .control-label-center {
+ text-align: center;
+ }
+}
+
+.well-clear {
+ background-color: transparent;
+}
+
+.nav {
+ .nav-button {
+ margin-left: 5px;
+ }
+}
+
+.state-icon {
+ background-image: url("../css/images/ui-icons_cccccc_256x240.png");
+ cursor: pointer;
+ display: block;
+ float: left;
+ height: 16px;
+ width: 16px;
+ margin-right: 5px;
+
+ &.state-icon-expand {
+ background-position: -32px -16px;
+ }
+
+ &.state-icon-collapse {
+ background-position: -64px -16px;
+ }
+
+ &.state-icon-wait {
+ background-position: -64px -80px;
+ }
+}
+
+.editable {
+ margin: 0px;
+ cursor: pointer;
+ &:hover {
+ background-color: #fcfcfc;
+ }
+ &.editable-placeholder {
+ color: #a0a0a0;
+ }
+}
+
+.editable-form {
+ display: none;
+}
+
+.nav.nav-sm > li > a {
+ padding: 8px 12px;
+ font-size: 13px;
+}
+
+.scrollable {
+ padding: 0;
+ overflow: auto;
+ margin-bottom: 20px;
+
+ table {
+ margin-bottom: 0;
+ }
+}
+
+.panel-scrollable {
+ padding: 0;
+ overflow: auto;
+
+ table {
+ margin-bottom: 0;
+ }
+}
+
+.form-control-auto {
+ width: auto;
+}
src/main/less/flow.less 342(+342 -0)
diff --git a/src/main/less/flow.less b/src/main/less/flow.less
new file mode 100644
index 0000000..8ee150a
--- /dev/null
+++ b/src/main/less/flow.less
@@ -0,0 +1,342 @@
+#svgDiv {
+ height: 100%;
+ padding: 0px;
+}
+
+#graphView {
+ -moz-user-select: none;
+ -khtml-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+}
+
+#flow-graph {
+ width: 100%;
+ height: 100%;
+}
+
+#headertabs {
+ -moz-user-select: none;
+ -khtml-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+}
+
+#flow-executing-graph {
+ width: 100%;
+ height: 500px;
+}
+
+.flow-progress {
+ width: 280px;
+ margin: 4px;
+ background-color: #f5f5f5;
+ height: 24px;
+ border-radius: 4px;
+ -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+}
+
+.flow-progress-bar {
+ height: 100%;
+ background-color: #ccc;
+ border-radius: 5px;
+ -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ -webkit-transition: width 0.6s ease;
+ transition: width 0.6s ease;
+
+ &.attempt {
+ opacity: 0.70;
+ &:hover {
+ opacity: 1;
+ }
+ }
+
+ &.SUCCEEDED {
+ background-color: @flow-succeeded-color;
+ }
+
+ &.FAILED {
+ background-color: @flow-failed-color;
+ }
+
+ &.KILLED {
+ background-color: @flow-killed-color;
+ }
+
+ &.RUNNING {
+ background-color: @flow-running-color;
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-size: 40px 40px;
+ -webkit-animation: progress-bar-stripes 2s linear infinite;
+ animation: progress-bar-stripes 2s linear infinite;
+ }
+
+ &.QUEUED {
+ background-color: @flow-queued-color;
+ }
+
+ &.CANCELLED {
+ background-color: @flow-cancelled-color;
+ }
+}
+
+td {
+ > .listExpand {
+ width: 16px;
+ height: 16px;
+ float:right;
+ margin-top: 5px;
+ font-size: 8pt;
+ }
+
+ .status {
+ -moz-border-radius: 2px;
+ border-radius: 2px;
+
+ padding: 2px 2px;
+ color: #FFF;
+ text-align: center;
+ margin-top: 2px;
+
+ &.SUCCEEDED {
+ background-color: @flow-succeeded-color;
+ }
+
+ &.FAILED {
+ background-color: @flow-failed-color;
+ }
+
+ &.KILLED {
+ background-color: @flow-killed-color;
+ }
+
+ &.PAUSED {
+ background-color: @flow-paused-color;
+ }
+
+ &.READY,
+ &.UNKNOWN,
+ &.PREPARING {
+ background-color: @flow-default-color;
+ }
+
+ &.RUNNING {
+ background-color: @flow-running-color;
+ }
+
+ &.FAILED_FINISHING {
+ background-color: @flow-failed-finishing-color;
+ }
+
+ &.DISABLED,
+ &.SKIPPED {
+ background-color: @flow-disabled-color;
+ }
+
+ &.CANCELLED {
+ background-color: @flow-cancelled-color;
+ }
+ }
+}
+
+#flowStatus {
+ &.SKIPPED {
+ color: @flow-disabled-color;
+ }
+
+ &.SUCCEEDED {
+ color: @flow-succeeded-color;
+ }
+
+ &.RUNNING {
+ color: @flow-running-color;
+ }
+
+ &.PAUSED {
+ color: @flow-paused-color;
+ }
+
+ &.FAILED {
+ color: @flow-failed-color;
+ }
+
+ &.KILLED {
+ color: @flow-killed-color;
+ }
+
+ &.CANCELLED {
+ color: @flow-cancelled-color;
+ }
+
+ &.FAILED_FINISHING {
+ color: @flow-failed-finishing-color;
+ }
+}
+
+.graph-sidebar {
+ height: 100%;
+ overflow-y: auto;
+
+ .graph-sidebar-list {
+ height: 100%;
+ }
+}
+
+.graph-sidebar-float {
+ position: absolute;
+ top: 0px;
+ bottom: 0px;
+
+ .graph-sidebar-list {
+ overflow-y: auto;
+ height: calc(~"100% - 102px");
+ }
+
+ .panel {
+ height: 100%;
+
+ .panel-heading {
+ padding-right: 10px;
+ }
+ }
+}
+
+.graph-container {
+ height: 100%;
+
+ svg {
+ width: 100%;
+ height: 100%;
+ }
+}
+
+.graph-sidebar-search {
+ width: 206px;
+ margin: 0px;
+}
+
+.graph-sidebar-close {
+ float: right;
+ color: #CCC;
+ padding: 5px 0px;
+ cursor: pointer;
+
+ &:hover {
+ color: #666;
+ }
+}
+
+.graph-sidebar-open {
+ position: absolute;
+ margin: 10px;
+ color: #CCC;
+ cursor: pointer;
+
+ &:hover {
+ color: #666;
+ }
+}
+
+ul.tree-list {
+ list-style-type: none;
+ padding-left: 0px;
+ margin: 0;
+}
+
+li.tree-list-item {
+ &.active > a {
+ background-color: #D9EDFF;
+ }
+
+ ul.tree-list {
+ padding-left: 20px;
+ }
+
+ &.subFilter > a > .expandarrow {
+ color : #f19153;
+ }
+
+ > a {
+ clear: both;
+ position: relative;
+ display: block;
+ border-bottom-width: 0;
+ padding: 5px 15px;
+ font-size: 10pt;
+
+ &:hover,
+ &:focus {
+ text-decoration: none;
+ background-color: #f5f5f5;
+ cursor: pointer;
+ }
+
+ &.nodedisabled,
+ &.DISABLED {
+ opacity: 0.3;
+ }
+
+ &.DISABLED .icon {
+ background-position: 16px 0px;
+ }
+
+ &.READY .icon {
+ background-position: 16px 0px;
+ }
+
+ &.QUEUED .icon {
+ opacity: 0.5;
+ background-position: 32px 0px;
+ }
+
+ &.RUNNING .icon {
+ background-position: 32px 0px;
+ }
+
+ &.SUCCEEDED .icon {
+ background-position: 48px 0px;
+ }
+
+ &.FAILED .icon {
+ background-position: 0px 0px;
+ }
+
+ &.KILLED .icon {
+ background-position: 0px 0px;
+ }
+
+ &.CANCELLED .icon {
+ background-position: 0px 0px;
+ opacity: 0.5;
+ }
+
+ &.FAILED_FINISHING .icon {
+ background-position: 0px 0px;
+ }
+
+ .icon {
+ float: left;
+ width: 16px;
+ height: 16px;
+ margin: 2px 4px 0px -5px;
+ background-image: url("./images/dot-icon.png");
+ background-position: 16px 0px;
+ }
+
+ .expandarrow {
+ float: right;
+ width: 16px;
+ height: 16px;
+ font-size: 8pt;
+ }
+
+ .filterHighlight {
+ background-color: #FFFF00;
+ }
+ }
+}
src/main/less/navbar.less 121(+121 -0)
diff --git a/src/main/less/navbar.less b/src/main/less/navbar.less
new file mode 100644
index 0000000..3826082
--- /dev/null
+++ b/src/main/less/navbar.less
@@ -0,0 +1,121 @@
+.navbar-logo {
+ float: left;
+ padding: 15px 15px;
+ font-size: 18px;
+ line-height: 20px;
+
+ a {
+ &:hover,
+ &:focus {
+ text-decoration: none;
+ }
+ }
+}
+
+@media (min-width: 768px) {
+ .navbar > .container .navbar-logo {
+ margin-left: -15px;
+ }
+}
+
+@media (min-width: 768px) {
+ .navbar-enviro {
+ width: auto;
+ }
+}
+
+.navbar-enviro {
+ margin: 30px 20px 0px 12px;
+
+ .navbar-enviro-name {
+ color: #ff3601;
+ font-family: Helvetica, Arial, Sans-Serif;
+ font-size: 118.75%;
+ font-weight: bold;
+ line-height: 100%;
+ }
+
+ .navbar-enviro-server {
+ color: #999;
+ font-family: Helvetica, Arial, Sans-Serif;
+ font-size: 75%;
+ }
+}
+
+// Restyle navbar-inverse for Azkaban navbar.
+.navbar-inverse {
+ background-color: #383838;
+ border-top: 5px solid #ff3601;
+ margin-bottom: 0;
+
+ .navbar-logo {
+ background: url('../../images/logo.png') top left no-repeat;
+ color: #ffffff;
+ font-size: 156.25%;
+ font-weight: bold;
+ margin: 15px 0.6% 15px 4.75%;
+ padding: 12px 0 11px 42px;
+
+ a {
+ color: #ffffff;
+ &:hover,
+ &:focus {
+ color: #ffffff;
+ background-color: transparent;
+ }
+ }
+ }
+
+ .navbar-nav {
+ font-family: Arial;
+ font-size: 81.25%;
+
+ > li {
+ padding: 25px 12px 25px 12px;
+ cursor: pointer;
+ &:hover {
+ background-color: rgba(0, 0, 0, 0.1);
+ }
+ }
+
+ > li > a {
+ padding: 0px;
+ color: #ccc;
+ &:focus,
+ &:hover {
+ color: #ccc;
+ background-color: transparent;
+ }
+ }
+
+ > .active {
+ background-color: rgba(0, 0, 0, 0.1);
+ }
+
+ > .active > a {
+ color: #fff;
+ font-weight: bold;
+ background-color: transparent;
+ border-bottom: 1px solid #ff3601;
+ &:hover {
+ color: #fff;
+ background-color: transparent;
+ }
+ }
+
+ > li.dropdown {
+ padding: 0;
+ }
+
+ > li.dropdown > a {
+ padding: 25px 12px 25px 12px;
+ }
+
+ > .open > a,
+ > .open > a:hover,
+ > .open > a:focus {
+ color: #ccc;
+ background-color: transparent;
+ }
+ }
+}
src/main/less/non-responsive.less 88(+88 -0)
diff --git a/src/main/less/non-responsive.less b/src/main/less/non-responsive.less
new file mode 100644
index 0000000..7fa282c
--- /dev/null
+++ b/src/main/less/non-responsive.less
@@ -0,0 +1,88 @@
+/* Non-responsive overrides
+ *
+ * Utilitze the following CSS to disable the responsive-ness of the container,
+ * grid system, and navbar.
+ */
+
+/* Reset the container */
+.container {
+ max-width: none !important;
+ width: 970px;
+}
+
+.container .navbar-header,
+.container .navbar-collapse {
+ margin-right: 0;
+ margin-left: 0;
+}
+
+/* Always float the navbar header */
+.navbar-header {
+ float: left;
+}
+
+/* Undo the collapsing navbar */
+.navbar-collapse {
+ display: block !important;
+ height: auto !important;
+ padding-bottom: 0;
+ overflow: visible !important;
+}
+
+.navbar-toggle {
+ display: none;
+}
+.navbar-collapse {
+ border-top: 0;
+}
+
+.navbar-brand {
+ margin-left: -15px;
+}
+
+/* Always apply the floated nav */
+.navbar-nav {
+ float: left;
+ margin: 0;
+}
+.navbar-nav > li {
+ float: left;
+}
+.navbar-nav > li > a {
+ padding: 15px;
+}
+
+/* Redeclare since we override the float above */
+.navbar-nav.navbar-right {
+ float: right;
+}
+
+/* Undo custom dropdowns */
+.navbar .navbar-nav .open .dropdown-menu {
+ position: absolute;
+ float: left;
+ background-color: #fff;
+ border: 1px solid #cccccc;
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ border-width: 0 1px 1px;
+ border-radius: 0 0 4px 4px;
+ -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
+ box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
+}
+.navbar-default .navbar-nav .open .dropdown-menu > li > a {
+ color: #333;
+}
+.navbar .navbar-nav .open .dropdown-menu > li > a:hover,
+.navbar .navbar-nav .open .dropdown-menu > li > a:focus,
+.navbar .navbar-nav .open .dropdown-menu > .active > a,
+.navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
+.navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
+ color: #fff !important;
+ background-color: #428bca !important;
+}
+.navbar .navbar-nav .open .dropdown-menu > .disabled > a,
+.navbar .navbar-nav .open .dropdown-menu > .disabled > a:hover,
+.navbar .navbar-nav .open .dropdown-menu > .disabled > a:focus {
+ color: #999 !important;
+ background-color: transparent !important;
+}
src/main/less/project.less 142(+142 -0)
diff --git a/src/main/less/project.less b/src/main/less/project.less
new file mode 100644
index 0000000..0fb63af
--- /dev/null
+++ b/src/main/less/project.less
@@ -0,0 +1,142 @@
+#project-list {
+ padding: 0;
+ margin: 0px 0px 40px 0px;
+
+ li {
+ list-style: none;
+ border-bottom: 1px solid #cccccc;
+ padding-top: 14px;
+ padding-bottom: 0px;
+ &:first-child {
+ border-top: 1px solid #cccccc;
+ }
+ }
+
+ .project-expander {
+ float: right;
+ cursor: pointer;
+ &:hover {
+ color: #2a6496;
+ }
+ }
+
+ .project-info {
+ float: left;
+ h4 {
+ margin-top: 0;
+ margin-bottom: 4px;
+ }
+
+ .project-description {
+ margin-bottom: 4px;
+ }
+
+ .project-last-modified {
+ color: #a0a0a0;
+ margin-bottom: 16px;
+ strong {
+ font-weight: normal;
+ color: #000000;
+ }
+ }
+ }
+}
+
+#project-sidebar {
+ h3 {
+ margin-bottom: 5px;
+ }
+}
+
+.project-flows {
+ display: none;
+ background-color: #f9f9f9;
+ padding: 10px 15px 10px 15px;
+
+ h5 {
+ margin-top: 5px;
+ }
+
+ .list-group {
+ margin-bottom: 10px;
+ }
+
+ .list-group-item {
+ background: transparent;
+ padding: 7px 12px 7px 12px;
+ }
+}
+
+// Flow panel heading.
+.flow-expander {
+ cursor: pointer;
+
+ .flow-expander-icon {
+ color: #9a9a9a;
+ margin-right: 5px;
+ }
+}
+
+.expanded-flow-job-list {
+ .list-group-item {
+ .job-buttons {
+ visibility: hidden;
+ }
+
+ &:hover {
+ background-color: #f5f5f5;
+
+ .job-buttons {
+ visibility: visible;
+ }
+ }
+ }
+
+ .dependency {
+ background-color: #f0f0f0;
+ }
+
+ .dependent {
+ background-color: #fafafa;
+ }
+}
+
+// Permissions page table.
+.permission-table {
+ .tb-perm {
+ width: 41px;
+ margin: 0px;
+ }
+
+ .tb-admin {
+ width: 41px;
+ margin: 0px;
+ }
+
+ .tb-read {
+ width: 33px;
+ margin: 0px;
+ }
+
+ .tb-write {
+ width: 34px;
+ margin: 0px;
+ }
+
+ .tb-execute {
+ width: 51px;
+ margin: 0px;
+ }
+
+ .tb-schedule {
+ margin: 0px;
+ width: 60px;
+ }
+
+ .tb-action {
+ margin: 0px;
+ width: 70px;
+ min-width: 70px;
+ max-width: 70px;
+ }
+}
src/main/less/tables.less 149(+149 -0)
diff --git a/src/main/less/tables.less b/src/main/less/tables.less
new file mode 100644
index 0000000..5d5aeea
--- /dev/null
+++ b/src/main/less/tables.less
@@ -0,0 +1,149 @@
+table.table-properties {
+ table-layout: fixed;
+ word-wrap: break-word;
+}
+
+// Flow summary.
+.property-key {
+ width: 25%;
+ font-weight: bold;
+}
+
+.property-value {
+
+}
+
+.property-value-half {
+ width: 25%;
+}
+
+.property-key,
+.property-value,
+.property-value-half {
+ pre {
+ background: transparent;
+ padding: 0;
+ border: 0;
+ }
+}
+
+.editable {
+ .remove-btn {
+ visibility: hidden;
+ }
+
+ &:hover .remove-btn {
+ visibility: visible;
+ }
+}
+
+// Job table.
+#all-jobs {
+ .tb-name {
+ width: 70%;
+ border-bottom-width: 0;
+ border-bottom-style: none;
+ }
+
+ .tb-up-date {
+ width: 140px;
+ min-width: 130px;
+ }
+
+ .tb-owner {
+ width: 10%;
+ min-width: 95px;
+ }
+}
+
+// Properties table.
+.properties-table {
+ .all-jobs .tb-pname {
+ }
+
+ .all-jobs .tb-pvalue {
+ }
+}
+
+// Table of executions.
+.executions-table {
+ tr {
+ &.expanded {
+ opacity: 0.6;
+ }
+ }
+
+ td {
+ &.subflowrow {
+ padding: 0px 0px;
+
+ table {
+ margin: 0px;
+ background-color: rgba(230, 230, 230, 0.75);
+
+ td {
+ background-color: none;
+ }
+ }
+ }
+
+ &.date {
+ width: 160px;
+ }
+
+ &.jobtype {
+ width: 90px;
+ }
+
+ &.execid {
+ width: 100px;
+ }
+
+ &.project {
+ width: 200px;
+ }
+
+ &.user {
+ width: 60px;
+ }
+
+ &.elapse {
+ width: 90px;
+ }
+
+ &.statustd {
+ width: 100px;
+ }
+
+ &.details {
+ width: 10px;
+ }
+
+ &.action {
+ width: 20px;
+ }
+
+ &.logs {
+ width: 30px;
+ }
+
+ &.timeline {
+ width: 280px;
+ padding: 0px 0px 0px 4px;
+ height: 100%;
+ vertical-align: bottom;
+ margin: 0px;
+ }
+
+ &.startTime {
+ width: 160px;
+ }
+
+ &.endTime {
+ width: 160px;
+ }
+ &.elapsedTime {
+ width: 90px;
+ }
+ }
+}
src/main/tl/.gitignore 1(+1 -0)
diff --git a/src/main/tl/.gitignore b/src/main/tl/.gitignore
new file mode 100644
index 0000000..2416a67
--- /dev/null
+++ b/src/main/tl/.gitignore
@@ -0,0 +1 @@
+obj/
src/main/tl/flowstats.tl 155(+155 -0)
diff --git a/src/main/tl/flowstats.tl b/src/main/tl/flowstats.tl
new file mode 100644
index 0000000..5276ce8
--- /dev/null
+++ b/src/main/tl/flowstats.tl
@@ -0,0 +1,155 @@
+ {?histogram}
+ <div class="row">
+ <div class="col-xs-12">
+ <div class="well well-clear well-sm">
+ <div id="job-histogram"></div>
+ </div>
+ </div>
+ </div>
+ {/histogram}
+
+ {?warnings}
+ <div class="alert alert-warning">
+ <h4>Warnings</h4>
+ <p>These stats may have reduced accuracy due to the following missing information:</p>
+ <ul>
+ {#warnings}
+ <li>{.}</li>
+ {/warnings}
+ </ul>
+ </div>
+ {/warnings}
+
+ <div class="row">
+ <div class="col-xs-12">
+ <h4>Resources</h4>
+ <table class="table table-bordered table-condensed table-striped">
+ <thead>
+ <tr>
+ <th class="property-key">Resource</th>
+ <th class="property-key">Value</th>
+ <th>Job Name</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="property-key">Max Map Slots</td>
+ <td>{stats.mapSlots.max}</td>
+ <td>{stats.mapSlots.job}</td>
+ </tr>
+ <tr>
+ <td class="property-key">Max Reduce Slots</td>
+ <td>{stats.reduceSlots.max}</td>
+ <td>{stats.reduceSlots.job}</td>
+ </tr>
+ <tr>
+ <td class="property-key">Total Map Slots</td>
+ <td colspan="2">{stats.totalMapSlots}</td>
+ </tr>
+ <tr>
+ <td class="property-key">Total Reduce Slots</td>
+ <td colspan="2">{stats.totalReduceSlots}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+
+ <div class="row">
+ <div class="col-xs-12">
+ <h4>Parameters</h4>
+ <table class="table table-bordered table-condensed table-striped">
+ <thead>
+ <tr>
+ <th class="property-key">Parameter</th>
+ <th class="property-key">Value</th>
+ <th>Job Name</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="property-key">Max <code>-Xmx</code></td>
+ <td>{stats.xmx.str}</td>
+ <td>{stats.xmx.job}</td>
+ </tr>
+ <tr>
+ <td class="property-key">Max <code>-Xms</code></td>
+ {?stats.xms.set}
+ <td>
+ {stats.xms.str}
+ </td>
+ <td>
+ {stats.xms.job}
+ </td>
+ {:else}
+ <td colspan="2">
+ Not set.
+ </td>
+ {/stats.xms.set}
+ </tr>
+ <tr>
+ <td class="property-key">Max <code>mapred.job.map.memory.mb</code></td>
+ <td>{stats.jobMapMemoryMb.max}</td>
+ <td>{stats.jobMapMemoryMb.job}</td>
+ </tr>
+ <tr>
+ <td class="property-key">Max <code>mapred.job.reduce.memory.mb</code></td>
+ <td>{stats.jobReduceMemoryMb.max}</td>
+ <td>{stats.jobReduceMemoryMb.job}</td>
+ </tr>
+ <tr>
+ <td class="property-key">Max Distributed Cache</td>
+ {?stats.distributedCache.using}
+ <td>
+ {stats.distributedCache.max}
+ </td>
+ <td>
+ {stats.distributedCache.job}
+ </td>
+ {:else}
+ <td colspan="2">
+ Not used.
+ </td>
+ {/stats.distributedCache.using}
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+
+ <div class="row">
+ <div class="col-xs-12">
+ <h4>Counters</h4>
+ <table class="table table-bordered table-condensed">
+ <thead>
+ <tr>
+ <th class="property-key">Parameter</th>
+ <th class="property-key">Value</th>
+ <th>Job Name</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="property-key">Max <code>FILE_BYTES_READ</code></td>
+ <td>{stats.fileBytesRead.max}</td>
+ <td>{stats.fileBytesRead.job}</td>
+ </tr>
+ <tr>
+ <td class="property-key">Max <code>HDFS_BYTES_READ</code></td>
+ <td>{stats.hdfsBytesRead.max}</td>
+ <td>{stats.hdfsBytesRead.job}</td>
+ </tr>
+ <tr>
+ <td class="property-key">Max <code>FILE_BYTES_WRITTEN</code></td>
+ <td>{stats.fileBytesWritten.max}</td>
+ <td>{stats.fileBytesWritten.job}</td>
+ </tr>
+ <tr>
+ <td class="property-key">Max <code>HDFS_BYTES_WRITTEN</code></td>
+ <td>{stats.hdfsBytesWritten.max}</td>
+ <td>{stats.hdfsBytesWritten.job}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
src/main/tl/flowsummary.tl 69(+69 -0)
diff --git a/src/main/tl/flowsummary.tl b/src/main/tl/flowsummary.tl
new file mode 100644
index 0000000..82627d6
--- /dev/null
+++ b/src/main/tl/flowsummary.tl
@@ -0,0 +1,69 @@
+ <div class="row">
+ <div class="col-xs-12">
+ <table class="table table-bordered table-condensed">
+ <tbody>
+ <tr>
+ <td class="property-key">Project name</td>
+ <td>{projectName}</td>
+ </tr>
+ <tr>
+ <td class="property-key">Job Types Used</td>
+ <td>{#jobTypes}{.} {/jobTypes}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+
+ <div class="row">
+ <div class="col-xs-12">
+ <h3>
+ Scheduling
+ {?schedule}
+ <div class="pull-right">
+ <button type="button" id="removeSchedBtn" class="btn btn-sm btn-danger" onclick="removeSched({schedule.scheduleId})" >Remove Schedule</button>
+ </div>
+ {/schedule}
+ </h3>
+ {?schedule}
+ <table class="table table-condensed table-bordered">
+ <tbody>
+ <tr>
+ <td class="property-key">Schedule ID</td>
+ <td class="property-value-half">{schedule.scheduleId}</td>
+ <td class="property-key">Submitted By</td>
+ <td class="property-value-half">{schedule.submitUser}</td>
+ </tr>
+ <tr>
+ <td class="property-key">First Scheduled to Run</td>
+ <td class="property-value-half">{schedule.firstSchedTime}</td>
+ <td class="property-key">Repeats Every</td>
+ <td class="property-value-half">{schedule.period}</td>
+ </tr>
+ <tr>
+ <td class="property-key">Next Execution Time</td>
+ <td class="property-value-half">{schedule.nextExecTime}</td>
+ <td class="property-key">SLA</td>
+ <td class="property-value-half">
+ {?schedule.slaOptions}
+ true
+ {:else}
+ false
+ {/schedule.slaOptions}
+ <div class="pull-right">
+ <button type="button" id="addSlaBtn" class="btn btn-xs btn-primary" onclick="slaView.initFromSched({schedule.scheduleId}, '{flowName}')" >View/Set SLA</button>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ {:else}
+ <div class="callout callout-default">
+ <h4>None</h4>
+ <p>This flow has not been scheduled.</p>
+ </div>
+ {/schedule}
+
+ <h3>Last Run Stats</h3>
+ </div>
+ </div>