Fixed licenses for tabula and tests. Fixed name of tabula project.
This commit is contained in:
189
conductor/tests/.metadata/.log
Normal file
189
conductor/tests/.metadata/.log
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
|
||||||
|
!ENTRY com.aptana.shared_core 4 4 2013-04-05 14:34:01.030
|
||||||
|
!MESSAGE Error when analyzing module forms
|
||||||
|
!STACK 0
|
||||||
|
java.lang.RuntimeException: java.io.IOException: No such file or directory
|
||||||
|
at org.python.pydev.core.DeltaSaver.addCommand(DeltaSaver.java:254)
|
||||||
|
at org.python.pydev.core.DeltaSaver.addInsertCommand(DeltaSaver.java:293)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.ModulesManagerWithBuild.doAddSingleModule(ModulesManagerWithBuild.java:112)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.ModulesManager.getModule(ModulesManager.java:895)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.ModulesManager.getModule(ModulesManager.java:718)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.SystemModulesManager.getModule(SystemModulesManager.java:342)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.AbstractASTManager.getModule(AbstractASTManager.java:448)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.AbstractASTManager.findModuleFromPath(AbstractASTManager.java:1430)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.AbstractASTManager.findOnImportedMods(AbstractASTManager.java:1321)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.AbstractASTManager.findOnImportedMods(AbstractASTManager.java:1200)
|
||||||
|
at com.python.pydev.analysis.visitors.ImportChecker.visitImportToken(ImportChecker.java:186)
|
||||||
|
at com.python.pydev.analysis.visitors.ImportChecker.visitImportToken(ImportChecker.java:167)
|
||||||
|
at com.python.pydev.analysis.visitors.Scope.addImportTokens(Scope.java:173)
|
||||||
|
at com.python.pydev.analysis.scopeanalysis.AbstractScopeAnalyzerVisitor.visitImportFrom(AbstractScopeAnalyzerVisitor.java:562)
|
||||||
|
at com.python.pydev.analysis.scopeanalysis.ScopeAnalyzerVisitor.visitImportFrom(ScopeAnalyzerVisitor.java:106)
|
||||||
|
at org.python.pydev.parser.jython.ast.ImportFrom.accept(ImportFrom.java:99)
|
||||||
|
at org.python.pydev.parser.jython.ast.Module.traverse(Module.java:86)
|
||||||
|
at com.python.pydev.analysis.scopeanalysis.AbstractScopeAnalyzerVisitor.traverse(AbstractScopeAnalyzerVisitor.java:189)
|
||||||
|
at org.python.pydev.parser.jython.ast.VisitorBase.visitModule(VisitorBase.java:10)
|
||||||
|
at org.python.pydev.parser.jython.ast.Module.accept(Module.java:79)
|
||||||
|
at com.python.pydev.refactoring.wizards.rename.AbstractRenameRefactorProcess.getOccurrencesWithScopeAnalyzer(AbstractRenameRefactorProcess.java:257)
|
||||||
|
at com.python.pydev.refactoring.wizards.rename.PyRenameAnyLocalProcess.findReferencesToRenameOnLocalScope(PyRenameAnyLocalProcess.java:38)
|
||||||
|
at com.python.pydev.refactoring.wizards.rename.AbstractRenameRefactorProcess.findReferencesToRename(AbstractRenameRefactorProcess.java:181)
|
||||||
|
at com.python.pydev.refactoring.wizards.rename.PyRenameEntryPoint.checkFinalConditions(PyRenameEntryPoint.java:258)
|
||||||
|
at com.python.pydev.refactoring.wizards.rename.PyRenameEntryPoint.checkFinalConditions(PyRenameEntryPoint.java:227)
|
||||||
|
at com.python.pydev.refactoring.markoccurrences.MarkOccurrencesJob.checkAnnotations(MarkOccurrencesJob.java:226)
|
||||||
|
at com.python.pydev.refactoring.markoccurrences.MarkOccurrencesJob.run(MarkOccurrencesJob.java:126)
|
||||||
|
at org.eclipse.core.internal.jobs.Worker.run(Worker.java:54)
|
||||||
|
Caused by: java.io.IOException: No such file or directory
|
||||||
|
at java.io.UnixFileSystem.createFileExclusively(Native Method)
|
||||||
|
at java.io.File.createNewFile(File.java:947)
|
||||||
|
at org.python.pydev.core.DeltaSaver.addCommand(DeltaSaver.java:252)
|
||||||
|
... 27 more
|
||||||
|
|
||||||
|
!ENTRY com.aptana.shared_core 4 4 2013-04-05 15:21:20.739
|
||||||
|
!MESSAGE Error when analyzing module urls
|
||||||
|
!STACK 0
|
||||||
|
java.lang.RuntimeException: java.io.IOException: No such file or directory
|
||||||
|
at org.python.pydev.core.DeltaSaver.addCommand(DeltaSaver.java:254)
|
||||||
|
at org.python.pydev.core.DeltaSaver.addInsertCommand(DeltaSaver.java:293)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.ModulesManagerWithBuild.doAddSingleModule(ModulesManagerWithBuild.java:112)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.ModulesManager.getModule(ModulesManager.java:895)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.ModulesManager.getModule(ModulesManager.java:718)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.SystemModulesManager.getRelativeModule(SystemModulesManager.java:139)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.AbstractASTManager.getModule(AbstractASTManager.java:446)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.AbstractASTManager.findModuleFromPath(AbstractASTManager.java:1437)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.AbstractASTManager.findOnImportedMods(AbstractASTManager.java:1289)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.AbstractASTManager.findOnImportedMods(AbstractASTManager.java:1200)
|
||||||
|
at com.python.pydev.analysis.visitors.ImportChecker.visitImportToken(ImportChecker.java:186)
|
||||||
|
at com.python.pydev.analysis.visitors.ImportChecker.visitImportToken(ImportChecker.java:167)
|
||||||
|
at com.python.pydev.analysis.visitors.Scope.addImportTokens(Scope.java:173)
|
||||||
|
at com.python.pydev.analysis.scopeanalysis.AbstractScopeAnalyzerVisitor.visitImportFrom(AbstractScopeAnalyzerVisitor.java:562)
|
||||||
|
at com.python.pydev.analysis.scopeanalysis.ScopeAnalyzerVisitor.visitImportFrom(ScopeAnalyzerVisitor.java:106)
|
||||||
|
at org.python.pydev.parser.jython.ast.ImportFrom.accept(ImportFrom.java:99)
|
||||||
|
at org.python.pydev.parser.jython.ast.Module.traverse(Module.java:86)
|
||||||
|
at com.python.pydev.analysis.scopeanalysis.AbstractScopeAnalyzerVisitor.traverse(AbstractScopeAnalyzerVisitor.java:189)
|
||||||
|
at org.python.pydev.parser.jython.ast.VisitorBase.visitModule(VisitorBase.java:10)
|
||||||
|
at org.python.pydev.parser.jython.ast.Module.accept(Module.java:79)
|
||||||
|
at com.python.pydev.refactoring.wizards.rename.AbstractRenameRefactorProcess.getOccurrencesWithScopeAnalyzer(AbstractRenameRefactorProcess.java:257)
|
||||||
|
at com.python.pydev.refactoring.wizards.rename.PyRenameImportProcess.findReferencesToRenameOnLocalScope(PyRenameImportProcess.java:66)
|
||||||
|
at com.python.pydev.refactoring.wizards.rename.AbstractRenameRefactorProcess.findReferencesToRename(AbstractRenameRefactorProcess.java:181)
|
||||||
|
at com.python.pydev.refactoring.wizards.rename.PyRenameEntryPoint.checkFinalConditions(PyRenameEntryPoint.java:258)
|
||||||
|
at com.python.pydev.refactoring.wizards.rename.PyRenameEntryPoint.checkFinalConditions(PyRenameEntryPoint.java:227)
|
||||||
|
at com.python.pydev.refactoring.markoccurrences.MarkOccurrencesJob.checkAnnotations(MarkOccurrencesJob.java:226)
|
||||||
|
at com.python.pydev.refactoring.markoccurrences.MarkOccurrencesJob.run(MarkOccurrencesJob.java:126)
|
||||||
|
at org.eclipse.core.internal.jobs.Worker.run(Worker.java:54)
|
||||||
|
Caused by: java.io.IOException: No such file or directory
|
||||||
|
at java.io.UnixFileSystem.createFileExclusively(Native Method)
|
||||||
|
at java.io.File.createNewFile(File.java:947)
|
||||||
|
at org.python.pydev.core.DeltaSaver.addCommand(DeltaSaver.java:252)
|
||||||
|
... 27 more
|
||||||
|
|
||||||
|
!ENTRY com.aptana.shared_core 4 4 2013-04-05 15:22:51.347
|
||||||
|
!MESSAGE Error when analyzing module views
|
||||||
|
!STACK 0
|
||||||
|
java.lang.RuntimeException: java.io.IOException: No such file or directory
|
||||||
|
at org.python.pydev.core.DeltaSaver.addCommand(DeltaSaver.java:254)
|
||||||
|
at org.python.pydev.core.DeltaSaver.addInsertCommand(DeltaSaver.java:293)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.ModulesManagerWithBuild.doAddSingleModule(ModulesManagerWithBuild.java:112)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.ModulesManager.getModule(ModulesManager.java:895)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.ModulesManager.getModule(ModulesManager.java:718)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.SystemModulesManager.getModule(SystemModulesManager.java:342)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.AbstractASTManager.getModule(AbstractASTManager.java:448)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.AbstractASTManager.findModuleFromPath(AbstractASTManager.java:1430)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.AbstractASTManager.findOnImportedMods(AbstractASTManager.java:1304)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.AbstractASTManager.findOnImportedMods(AbstractASTManager.java:1200)
|
||||||
|
at com.python.pydev.analysis.visitors.ImportChecker.visitImportToken(ImportChecker.java:186)
|
||||||
|
at com.python.pydev.analysis.visitors.ImportChecker.visitImportToken(ImportChecker.java:167)
|
||||||
|
at com.python.pydev.analysis.visitors.Scope.addImportTokens(Scope.java:173)
|
||||||
|
at com.python.pydev.analysis.scopeanalysis.AbstractScopeAnalyzerVisitor.visitImportFrom(AbstractScopeAnalyzerVisitor.java:562)
|
||||||
|
at com.python.pydev.analysis.scopeanalysis.ScopeAnalyzerVisitor.visitImportFrom(ScopeAnalyzerVisitor.java:106)
|
||||||
|
at org.python.pydev.parser.jython.ast.ImportFrom.accept(ImportFrom.java:99)
|
||||||
|
at org.python.pydev.parser.jython.ast.Module.traverse(Module.java:86)
|
||||||
|
at com.python.pydev.analysis.scopeanalysis.AbstractScopeAnalyzerVisitor.traverse(AbstractScopeAnalyzerVisitor.java:189)
|
||||||
|
at org.python.pydev.parser.jython.ast.VisitorBase.visitModule(VisitorBase.java:10)
|
||||||
|
at org.python.pydev.parser.jython.ast.Module.accept(Module.java:79)
|
||||||
|
at com.python.pydev.refactoring.wizards.rename.AbstractRenameRefactorProcess.getOccurrencesWithScopeAnalyzer(AbstractRenameRefactorProcess.java:257)
|
||||||
|
at com.python.pydev.refactoring.wizards.rename.PyRenameImportProcess.findReferencesToRenameOnLocalScope(PyRenameImportProcess.java:66)
|
||||||
|
at com.python.pydev.refactoring.wizards.rename.AbstractRenameRefactorProcess.findReferencesToRename(AbstractRenameRefactorProcess.java:181)
|
||||||
|
at com.python.pydev.refactoring.wizards.rename.PyRenameEntryPoint.checkFinalConditions(PyRenameEntryPoint.java:258)
|
||||||
|
at com.python.pydev.refactoring.wizards.rename.PyRenameEntryPoint.checkFinalConditions(PyRenameEntryPoint.java:227)
|
||||||
|
at com.python.pydev.refactoring.markoccurrences.MarkOccurrencesJob.checkAnnotations(MarkOccurrencesJob.java:226)
|
||||||
|
at com.python.pydev.refactoring.markoccurrences.MarkOccurrencesJob.run(MarkOccurrencesJob.java:126)
|
||||||
|
at org.eclipse.core.internal.jobs.Worker.run(Worker.java:54)
|
||||||
|
Caused by: java.io.IOException: No such file or directory
|
||||||
|
at java.io.UnixFileSystem.createFileExclusively(Native Method)
|
||||||
|
at java.io.File.createNewFile(File.java:947)
|
||||||
|
at org.python.pydev.core.DeltaSaver.addCommand(DeltaSaver.java:252)
|
||||||
|
... 27 more
|
||||||
|
|
||||||
|
!ENTRY com.aptana.shared_core 4 4 2013-04-05 15:25:25.732
|
||||||
|
!MESSAGE java.io.IOException: No such file or directory
|
||||||
|
!STACK 0
|
||||||
|
java.lang.RuntimeException: java.io.IOException: No such file or directory
|
||||||
|
at org.python.pydev.core.DeltaSaver.addCommand(DeltaSaver.java:254)
|
||||||
|
at org.python.pydev.core.DeltaSaver.addInsertCommand(DeltaSaver.java:293)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.ModulesManagerWithBuild.doAddSingleModule(ModulesManagerWithBuild.java:112)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.ModulesManager.getModule(ModulesManager.java:895)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.ModulesManager.getModule(ModulesManager.java:718)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.SystemModulesManager.getModule(SystemModulesManager.java:342)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.AbstractASTManager.getModule(AbstractASTManager.java:448)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.AbstractASTManager.findModuleFromPath(AbstractASTManager.java:1430)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.AbstractASTManager.findOnImportedMods(AbstractASTManager.java:1321)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.AbstractASTManager.findOnImportedMods(AbstractASTManager.java:1200)
|
||||||
|
at com.python.pydev.analysis.visitors.ImportChecker.visitImportToken(ImportChecker.java:186)
|
||||||
|
at com.python.pydev.analysis.visitors.ImportChecker.visitImportToken(ImportChecker.java:167)
|
||||||
|
at com.python.pydev.analysis.visitors.Scope.addImportTokens(Scope.java:173)
|
||||||
|
at com.python.pydev.analysis.scopeanalysis.AbstractScopeAnalyzerVisitor.visitImport(AbstractScopeAnalyzerVisitor.java:538)
|
||||||
|
at org.python.pydev.parser.jython.ast.Import.accept(Import.java:79)
|
||||||
|
at org.python.pydev.parser.jython.ast.Module.traverse(Module.java:86)
|
||||||
|
at com.python.pydev.analysis.scopeanalysis.AbstractScopeAnalyzerVisitor.traverse(AbstractScopeAnalyzerVisitor.java:189)
|
||||||
|
at org.python.pydev.parser.jython.ast.VisitorBase.visitModule(VisitorBase.java:10)
|
||||||
|
at org.python.pydev.parser.jython.ast.Module.accept(Module.java:79)
|
||||||
|
at com.python.pydev.refactoring.wizards.rename.AbstractRenameRefactorProcess.getOccurrencesWithScopeAnalyzer(AbstractRenameRefactorProcess.java:257)
|
||||||
|
at com.python.pydev.refactoring.wizards.rename.PyRenameAnyLocalProcess.findReferencesToRenameOnLocalScope(PyRenameAnyLocalProcess.java:38)
|
||||||
|
at com.python.pydev.refactoring.wizards.rename.AbstractRenameRefactorProcess.findReferencesToRename(AbstractRenameRefactorProcess.java:181)
|
||||||
|
at com.python.pydev.refactoring.wizards.rename.PyRenameEntryPoint.checkFinalConditions(PyRenameEntryPoint.java:258)
|
||||||
|
at com.python.pydev.refactoring.wizards.rename.PyRenameEntryPoint.checkFinalConditions(PyRenameEntryPoint.java:227)
|
||||||
|
at com.python.pydev.refactoring.markoccurrences.MarkOccurrencesJob.checkAnnotations(MarkOccurrencesJob.java:226)
|
||||||
|
at com.python.pydev.refactoring.markoccurrences.MarkOccurrencesJob.run(MarkOccurrencesJob.java:126)
|
||||||
|
at org.eclipse.core.internal.jobs.Worker.run(Worker.java:54)
|
||||||
|
Caused by: java.io.IOException: No such file or directory
|
||||||
|
at java.io.UnixFileSystem.createFileExclusively(Native Method)
|
||||||
|
at java.io.File.createNewFile(File.java:947)
|
||||||
|
at org.python.pydev.core.DeltaSaver.addCommand(DeltaSaver.java:252)
|
||||||
|
... 26 more
|
||||||
|
|
||||||
|
!ENTRY com.aptana.shared_core 4 4 2013-04-05 15:26:00.341
|
||||||
|
!MESSAGE Error when analyzing module workflows
|
||||||
|
!STACK 0
|
||||||
|
java.lang.RuntimeException: java.io.IOException: No such file or directory
|
||||||
|
at org.python.pydev.core.DeltaSaver.addCommand(DeltaSaver.java:254)
|
||||||
|
at org.python.pydev.core.DeltaSaver.addInsertCommand(DeltaSaver.java:293)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.ModulesManagerWithBuild.doAddSingleModule(ModulesManagerWithBuild.java:112)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.ModulesManager.getModule(ModulesManager.java:895)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.ModulesManager.getModule(ModulesManager.java:718)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.SystemModulesManager.getRelativeModule(SystemModulesManager.java:139)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.AbstractASTManager.getModule(AbstractASTManager.java:446)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.AbstractASTManager.findModuleFromPath(AbstractASTManager.java:1437)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.AbstractASTManager.findOnImportedMods(AbstractASTManager.java:1289)
|
||||||
|
at org.python.pydev.editor.codecompletion.revisited.AbstractASTManager.findOnImportedMods(AbstractASTManager.java:1200)
|
||||||
|
at com.python.pydev.analysis.visitors.ImportChecker.visitImportToken(ImportChecker.java:186)
|
||||||
|
at com.python.pydev.analysis.visitors.ImportChecker.visitImportToken(ImportChecker.java:167)
|
||||||
|
at com.python.pydev.analysis.visitors.Scope.addImportTokens(Scope.java:173)
|
||||||
|
at com.python.pydev.analysis.scopeanalysis.AbstractScopeAnalyzerVisitor.visitImportFrom(AbstractScopeAnalyzerVisitor.java:562)
|
||||||
|
at com.python.pydev.analysis.scopeanalysis.ScopeAnalyzerVisitor.visitImportFrom(ScopeAnalyzerVisitor.java:106)
|
||||||
|
at org.python.pydev.parser.jython.ast.ImportFrom.accept(ImportFrom.java:99)
|
||||||
|
at org.python.pydev.parser.jython.ast.Module.traverse(Module.java:86)
|
||||||
|
at com.python.pydev.analysis.scopeanalysis.AbstractScopeAnalyzerVisitor.traverse(AbstractScopeAnalyzerVisitor.java:189)
|
||||||
|
at org.python.pydev.parser.jython.ast.VisitorBase.visitModule(VisitorBase.java:10)
|
||||||
|
at org.python.pydev.parser.jython.ast.Module.accept(Module.java:79)
|
||||||
|
at com.python.pydev.refactoring.wizards.rename.AbstractRenameRefactorProcess.getOccurrencesWithScopeAnalyzer(AbstractRenameRefactorProcess.java:257)
|
||||||
|
at com.python.pydev.refactoring.wizards.rename.PyRenameAnyLocalProcess.findReferencesToRenameOnLocalScope(PyRenameAnyLocalProcess.java:38)
|
||||||
|
at com.python.pydev.refactoring.wizards.rename.AbstractRenameRefactorProcess.findReferencesToRename(AbstractRenameRefactorProcess.java:181)
|
||||||
|
at com.python.pydev.refactoring.wizards.rename.PyRenameEntryPoint.checkFinalConditions(PyRenameEntryPoint.java:258)
|
||||||
|
at com.python.pydev.refactoring.wizards.rename.PyRenameEntryPoint.checkFinalConditions(PyRenameEntryPoint.java:227)
|
||||||
|
at com.python.pydev.refactoring.markoccurrences.MarkOccurrencesJob.checkAnnotations(MarkOccurrencesJob.java:226)
|
||||||
|
at com.python.pydev.refactoring.markoccurrences.MarkOccurrencesJob.run(MarkOccurrencesJob.java:126)
|
||||||
|
at org.eclipse.core.internal.jobs.Worker.run(Worker.java:54)
|
||||||
|
Caused by: java.io.IOException: No such file or directory
|
||||||
|
at java.io.UnixFileSystem.createFileExclusively(Native Method)
|
||||||
|
at java.io.File.createNewFile(File.java:947)
|
||||||
|
at org.python.pydev.core.DeltaSaver.addCommand(DeltaSaver.java:252)
|
||||||
|
... 27 more
|
14
tabula/tabula/tabula/__init__.py
Normal file
14
tabula/tabula/tabula/__init__.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Copyright (c) 2013 Mirantis Inc.
|
||||||
|
#
|
||||||
|
# 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.
|
210
tabula/tabula/tabula/api.py
Normal file
210
tabula/tabula/tabula/api.py
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
# Copyright (c) 2013 Mirantis Inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from portasclient.v1.client import Client as glazier_client
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def glazierclient(request):
|
||||||
|
url = "http://127.0.0.1:8082"
|
||||||
|
log.debug('glazierclient connection created using token "%s" and url "%s"'
|
||||||
|
% (request.user.token, url))
|
||||||
|
return glazier_client(endpoint=url, token=request.user.token.token['id'])
|
||||||
|
|
||||||
|
|
||||||
|
def datacenters_create(request, parameters):
|
||||||
|
env = glazierclient(request).environments.create(parameters.get('name', ''))
|
||||||
|
log.debug('Environment::Create {0}'.format(env))
|
||||||
|
return env
|
||||||
|
|
||||||
|
|
||||||
|
def datacenters_delete(request, datacenter_id):
|
||||||
|
result = glazierclient(request).environments.delete(datacenter_id)
|
||||||
|
log.debug('Environment::Delete Id:{0}'.format(datacenter_id))
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def datacenters_get(request, datacenter_id):
|
||||||
|
env = glazierclient(request).environments.get(datacenter_id)
|
||||||
|
log.debug('Environment::Get {0}'.format(env))
|
||||||
|
return env
|
||||||
|
|
||||||
|
|
||||||
|
def datacenters_list(request):
|
||||||
|
log.debug('Environment::List')
|
||||||
|
return glazierclient(request).environments.list()
|
||||||
|
|
||||||
|
|
||||||
|
def datacenters_deploy(request, datacenter_id):
|
||||||
|
sessions = glazierclient(request).sessions.list(datacenter_id)
|
||||||
|
for session in sessions:
|
||||||
|
if session.state == 'open':
|
||||||
|
session_id = session.id
|
||||||
|
if not session_id:
|
||||||
|
return "Sorry, nothing to deploy."
|
||||||
|
log.debug('Obtained session with Id: {0}'.format(session_id))
|
||||||
|
result = glazierclient(request).sessions.deploy(datacenter_id, session_id)
|
||||||
|
log.debug('Environment with Id: {0} deployed in session '
|
||||||
|
'with Id: {1}'.format(datacenter_id, session_id))
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def services_create(request, environment_id, parameters):
|
||||||
|
session_id = None
|
||||||
|
sessions = glazierclient(request).sessions.list(environment_id)
|
||||||
|
|
||||||
|
for s in sessions:
|
||||||
|
if s.state == 'open':
|
||||||
|
session_id = s.id
|
||||||
|
else:
|
||||||
|
glazierclient(request).sessions.delete(environment_id, s.id)
|
||||||
|
|
||||||
|
if session_id is None:
|
||||||
|
session_id = glazierclient(request).sessions.configure(environment_id).id
|
||||||
|
|
||||||
|
if parameters['service_type'] == 'Active Directory':
|
||||||
|
service = glazierclient(request)\
|
||||||
|
.activeDirectories\
|
||||||
|
.create(environment_id, session_id, parameters)
|
||||||
|
else:
|
||||||
|
service = glazierclient(request)\
|
||||||
|
.webServers.create(environment_id, session_id, parameters)
|
||||||
|
|
||||||
|
log.debug('Service::Create {0}'.format(service))
|
||||||
|
return service
|
||||||
|
|
||||||
|
|
||||||
|
def get_time(obj):
|
||||||
|
return obj.updated
|
||||||
|
|
||||||
|
|
||||||
|
def services_list(request, datacenter_id):
|
||||||
|
services = []
|
||||||
|
session_id = None
|
||||||
|
sessions = glazierclient(request).sessions.list(datacenter_id)
|
||||||
|
for s in sessions:
|
||||||
|
session_id = s.id
|
||||||
|
|
||||||
|
if session_id:
|
||||||
|
services = glazierclient(request).activeDirectories.list(datacenter_id,
|
||||||
|
session_id)
|
||||||
|
services += glazierclient(request).webServers.list(datacenter_id,
|
||||||
|
session_id)
|
||||||
|
for i in range(len(services)):
|
||||||
|
reports = glazierclient(request).sessions. \
|
||||||
|
reports(datacenter_id, session_id,
|
||||||
|
services[i].id)
|
||||||
|
|
||||||
|
for report in reports:
|
||||||
|
services[i].operation = report.text
|
||||||
|
|
||||||
|
log.debug('Service::List')
|
||||||
|
return services
|
||||||
|
|
||||||
|
|
||||||
|
def get_active_directories(request, datacenter_id):
|
||||||
|
services = []
|
||||||
|
session_id = None
|
||||||
|
sessions = glazierclient(request).sessions.list(datacenter_id)
|
||||||
|
|
||||||
|
for s in sessions:
|
||||||
|
session_id = s.id
|
||||||
|
|
||||||
|
if session_id:
|
||||||
|
services = glazierclient(request)\
|
||||||
|
.activeDirectories\
|
||||||
|
.list(datacenter_id, session_id)
|
||||||
|
|
||||||
|
log.debug('Service::Active Directories::List')
|
||||||
|
return services
|
||||||
|
|
||||||
|
|
||||||
|
def services_get(request, datacenter_id, service_id):
|
||||||
|
services = services_list(request, datacenter_id)
|
||||||
|
|
||||||
|
for service in services:
|
||||||
|
if service.id == service_id:
|
||||||
|
log.debug('Service::Get {0}'.format(service))
|
||||||
|
return service
|
||||||
|
|
||||||
|
|
||||||
|
def get_data_center_id_for_service(request, service_id):
|
||||||
|
datacenters = datacenters_list(request)
|
||||||
|
|
||||||
|
for dc in datacenters:
|
||||||
|
services = services_list(request, dc.id)
|
||||||
|
for service in services:
|
||||||
|
if service.id == service_id:
|
||||||
|
return dc.id
|
||||||
|
|
||||||
|
|
||||||
|
def get_service_datails(request, service_id):
|
||||||
|
datacenters = datacenters_list(request)
|
||||||
|
services = []
|
||||||
|
for dc in datacenters:
|
||||||
|
services += services_list(request, dc.id)
|
||||||
|
|
||||||
|
for service in services:
|
||||||
|
if service.id == service_id:
|
||||||
|
return service
|
||||||
|
|
||||||
|
|
||||||
|
def get_status_message_for_service(request, service_id):
|
||||||
|
environment_id = get_data_center_id_for_service(request, service_id)
|
||||||
|
session_id = None
|
||||||
|
sessions = glazierclient(request).sessions.list(environment_id)
|
||||||
|
|
||||||
|
for s in sessions:
|
||||||
|
session_id = s.id
|
||||||
|
|
||||||
|
if session_id:
|
||||||
|
reports = glazierclient(request).sessions.\
|
||||||
|
reports(environment_id, session_id, service_id)
|
||||||
|
|
||||||
|
result = 'Initialization.... \n'
|
||||||
|
for report in reports:
|
||||||
|
result += ' ' + str(report.text) + '\n'
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def services_delete(request, datacenter_id, service_id):
|
||||||
|
log.debug('Service::Remove EnvId: {0} '
|
||||||
|
'SrvId: {1}'.format(datacenter_id, service_id))
|
||||||
|
|
||||||
|
services = services_list(request, datacenter_id)
|
||||||
|
|
||||||
|
session_id = None
|
||||||
|
sessions = glazierclient(request).sessions.list(datacenter_id)
|
||||||
|
for session in sessions:
|
||||||
|
if session.state == 'open':
|
||||||
|
session_id = session.id
|
||||||
|
|
||||||
|
if session_id is None:
|
||||||
|
raise Exception("Sorry, you can not delete this service now.")
|
||||||
|
|
||||||
|
for service in services:
|
||||||
|
if service.id is service_id:
|
||||||
|
if service.type is 'Active Directory':
|
||||||
|
glazierclient(request).activeDirectories.delete(datacenter_id,
|
||||||
|
session_id,
|
||||||
|
service_id)
|
||||||
|
elif service.type is 'IIS':
|
||||||
|
glazierclient(request).webServers.delete(datacenter_id,
|
||||||
|
session_id,
|
||||||
|
service_id)
|
120
tabula/tabula/tabula/forms.py
Normal file
120
tabula/tabula/tabula/forms.py
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
# Copyright (c) 2013 Mirantis Inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import string
|
||||||
|
|
||||||
|
from django import forms
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
import re
|
||||||
|
from tabula.tabula import api
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class PasswordField(forms.CharField):
|
||||||
|
# Setup the Field
|
||||||
|
def __init__(self, label, *args, **kwargs):
|
||||||
|
super(PasswordField, self).__init__(min_length=7, required=True,
|
||||||
|
label=label,
|
||||||
|
widget=forms.PasswordInput(
|
||||||
|
render_value=False),
|
||||||
|
*args, **kwargs)
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
|
||||||
|
# Setup Our Lists of Characters and Numbers
|
||||||
|
characters = list(string.letters)
|
||||||
|
special_characters = '!@#$%^&*()_+|\/.,~?><:{}'
|
||||||
|
numbers = [str(i) for i in range(10)]
|
||||||
|
|
||||||
|
# Assume False until Proven Otherwise
|
||||||
|
numCheck = False
|
||||||
|
charCheck = False
|
||||||
|
specCharCheck = False
|
||||||
|
|
||||||
|
# Loop until we Match
|
||||||
|
for char in value:
|
||||||
|
if not charCheck:
|
||||||
|
if char in characters:
|
||||||
|
charCheck = True
|
||||||
|
if not specCharCheck:
|
||||||
|
if char in special_characters:
|
||||||
|
specCharCheck = True
|
||||||
|
if not numCheck:
|
||||||
|
if char in numbers:
|
||||||
|
numCheck = True
|
||||||
|
if numCheck and charCheck and specCharCheck:
|
||||||
|
break
|
||||||
|
|
||||||
|
if not numCheck or not charCheck or not specCharCheck:
|
||||||
|
raise forms.ValidationError(u'Your password must include at least \
|
||||||
|
one letter, at least one number and \
|
||||||
|
at least one special character.')
|
||||||
|
|
||||||
|
return super(PasswordField, self).clean(value)
|
||||||
|
|
||||||
|
|
||||||
|
class WizardFormServiceType(forms.Form):
|
||||||
|
service = forms.ChoiceField(label=_('Service Type'),
|
||||||
|
choices=[
|
||||||
|
('Active Directory', 'Active Directory'),
|
||||||
|
('IIS', 'Internet Information Services')
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
class WizardFormConfiguration(forms.Form):
|
||||||
|
'The functions for this class will dynamically create in views.py'
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class WizardFormADConfiguration(forms.Form):
|
||||||
|
dc_name = forms.CharField(label=_('Domain Name'),
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
dc_count = forms.IntegerField(label=_('Instances Count'),
|
||||||
|
required=True,
|
||||||
|
min_value=1,
|
||||||
|
max_value=100,
|
||||||
|
initial=1)
|
||||||
|
|
||||||
|
adm_password = PasswordField(_('Administrator password'))
|
||||||
|
|
||||||
|
recovery_password = PasswordField(_('Recovery password'))
|
||||||
|
|
||||||
|
def __init__(self, request, *args, **kwargs):
|
||||||
|
super(WizardFormADConfiguration, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class WizardFormIISConfiguration(forms.Form):
|
||||||
|
iis_name = forms.CharField(label=_('IIS Server Name'),
|
||||||
|
required=True)
|
||||||
|
|
||||||
|
adm_password = PasswordField(_('Administrator password'))
|
||||||
|
|
||||||
|
iis_domain = forms.ChoiceField(label=_('Member of the Domain'),
|
||||||
|
required=False)
|
||||||
|
|
||||||
|
def __init__(self, request, *args, **kwargs):
|
||||||
|
super(WizardFormIISConfiguration, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
link = request.__dict__['META']['HTTP_REFERER']
|
||||||
|
datacenter_id = re.search('tabula/(\S+)', link).group(0)[6:-1]
|
||||||
|
|
||||||
|
domains = api.get_active_directories(request, datacenter_id)
|
||||||
|
|
||||||
|
self.fields['iis_domain'].choices = [("", "")] + \
|
||||||
|
[(domain.name, domain.name)
|
||||||
|
for domain in domains]
|
21
tabula/tabula/tabula/overrides.py
Normal file
21
tabula/tabula/tabula/overrides.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Copyright (c) 2013 Mirantis Inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import horizon
|
||||||
|
|
||||||
|
from panel import tabula
|
||||||
|
|
||||||
|
project = horizon.get_dashboard('project')
|
||||||
|
project.register(tabula)
|
26
tabula/tabula/tabula/panel.py
Normal file
26
tabula/tabula/tabula/panel.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Copyright (c) 2013 Mirantis Inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import horizon
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from openstack_dashboard.dashboards.project import dashboard
|
||||||
|
|
||||||
|
|
||||||
|
class tabula(horizon.Panel):
|
||||||
|
name = _("Environments")
|
||||||
|
slug = 'tabula'
|
||||||
|
|
||||||
|
|
||||||
|
dashboard.Project.register(tabula)
|
193
tabula/tabula/tabula/tables.py
Normal file
193
tabula/tabula/tabula/tables.py
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
# Copyright (c) 2013 Mirantis Inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from horizon import messages
|
||||||
|
from horizon import tables
|
||||||
|
from tabula.tabula import api
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class CreateService(tables.LinkAction):
|
||||||
|
name = 'CreateService'
|
||||||
|
verbose_name = _('Create Service')
|
||||||
|
url = 'horizon:project:tabula:create'
|
||||||
|
classes = ('btn-launch', 'ajax-modal')
|
||||||
|
|
||||||
|
def allowed(self, request, datum):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def action(self, request, service):
|
||||||
|
api.services_create(request, service)
|
||||||
|
|
||||||
|
|
||||||
|
class CreateEnvironment(tables.LinkAction):
|
||||||
|
name = 'CreateEnvironment'
|
||||||
|
verbose_name = _('Create Environment')
|
||||||
|
url = 'horizon:project:tabula:create_dc'
|
||||||
|
classes = ('btn-launch', 'ajax-modal')
|
||||||
|
|
||||||
|
def allowed(self, request, datum):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def action(self, request, environment):
|
||||||
|
api.datacenters_create(request, environment)
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteEnvironment(tables.BatchAction):
|
||||||
|
name = 'delete'
|
||||||
|
action_present = _('Delete')
|
||||||
|
action_past = _('Delete')
|
||||||
|
data_type_singular = _('Environment')
|
||||||
|
data_type_plural = _('Environment')
|
||||||
|
classes = ('btn-danger', 'btn-terminate')
|
||||||
|
|
||||||
|
def allowed(self, request, datum):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def action(self, request, environment_id):
|
||||||
|
api.datacenters_delete(request, environment_id)
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteService(tables.BatchAction):
|
||||||
|
name = 'delete'
|
||||||
|
action_present = _('Delete')
|
||||||
|
action_past = _('Delete')
|
||||||
|
data_type_singular = _('Service')
|
||||||
|
data_type_plural = _('Service')
|
||||||
|
classes = ('btn-danger', 'btn-terminate')
|
||||||
|
|
||||||
|
def allowed(self, request, datum):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def action(self, request, service_id):
|
||||||
|
link = request.__dict__['META']['HTTP_REFERER']
|
||||||
|
datacenter_id = re.search('tabula/(\S+)', link).group(0)[6:-1]
|
||||||
|
|
||||||
|
try:
|
||||||
|
api.services_delete(request, datacenter_id, service_id)
|
||||||
|
except:
|
||||||
|
messages.error(request, _('Sorry, you can not delete this '
|
||||||
|
'service right now.'))
|
||||||
|
|
||||||
|
|
||||||
|
class DeployEnvironment(tables.BatchAction):
|
||||||
|
name = 'deploy'
|
||||||
|
action_present = _('Deploy')
|
||||||
|
action_past = _('Deploy')
|
||||||
|
data_type_singular = _('Environment')
|
||||||
|
data_type_plural = _('Environment')
|
||||||
|
classes = 'btn-launch'
|
||||||
|
|
||||||
|
def allowed(self, request, datum):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def action(self, request, environment_id):
|
||||||
|
return api.datacenters_deploy(request, environment_id)
|
||||||
|
|
||||||
|
|
||||||
|
class ShowDataCenterServices(tables.LinkAction):
|
||||||
|
name = 'edit'
|
||||||
|
verbose_name = _('Services')
|
||||||
|
url = 'horizon:project:tabula:services'
|
||||||
|
|
||||||
|
def allowed(self, request, instance):
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateDCRow(tables.Row):
|
||||||
|
ajax = True
|
||||||
|
|
||||||
|
def get_data(self, request, datacenter_id):
|
||||||
|
return api.datacenters_get(request, datacenter_id)
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateServiceRow(tables.Row):
|
||||||
|
ajax = True
|
||||||
|
|
||||||
|
def get_data(self, request, service_id):
|
||||||
|
|
||||||
|
link = request.__dict__['META']['HTTP_REFERER']
|
||||||
|
datacenter_id = re.search('tabula/(\S+)', link).group(0)[6:-1]
|
||||||
|
|
||||||
|
service = api.services_get(request, datacenter_id, service_id)
|
||||||
|
|
||||||
|
return service
|
||||||
|
|
||||||
|
|
||||||
|
STATUS_DISPLAY_CHOICES = (
|
||||||
|
('draft', 'Ready to deploy'),
|
||||||
|
('pending', 'Wait for configuration'),
|
||||||
|
('inprogress', 'Deploy in progress'),
|
||||||
|
('finished', 'Active')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DCTable(tables.DataTable):
|
||||||
|
STATUS_CHOICES = (
|
||||||
|
(None, True),
|
||||||
|
('Ready to deploy', True),
|
||||||
|
('Active', True)
|
||||||
|
)
|
||||||
|
|
||||||
|
name = tables.Column('name',
|
||||||
|
link=('horizon:project:tabula:services'),
|
||||||
|
verbose_name=_('Name'))
|
||||||
|
|
||||||
|
status = tables.Column('status', verbose_name=_('Status'),
|
||||||
|
status=True,
|
||||||
|
status_choices=STATUS_CHOICES,
|
||||||
|
display_choices=STATUS_DISPLAY_CHOICES)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
name = 'tabula'
|
||||||
|
verbose_name = _('Environment')
|
||||||
|
row_class = UpdateDCRow
|
||||||
|
status_columns = ['status']
|
||||||
|
table_actions = (CreateDataCenter,)
|
||||||
|
row_actions = (ShowDataCenterServices, DeleteDataCenter,
|
||||||
|
DeployDataCenter)
|
||||||
|
|
||||||
|
|
||||||
|
class ServicesTable(tables.DataTable):
|
||||||
|
STATUS_CHOICES = (
|
||||||
|
(None, True),
|
||||||
|
('Ready to deploy', True),
|
||||||
|
('Active', True)
|
||||||
|
)
|
||||||
|
|
||||||
|
name = tables.Column('name', verbose_name=_('Name'),
|
||||||
|
link=('horizon:project:tabula:service_details'))
|
||||||
|
|
||||||
|
_type = tables.Column('service_type', verbose_name=_('Type'))
|
||||||
|
|
||||||
|
status = tables.Column('status', verbose_name=_('Status'),
|
||||||
|
status=True,
|
||||||
|
status_choices=STATUS_CHOICES,
|
||||||
|
display_choices=STATUS_DISPLAY_CHOICES)
|
||||||
|
|
||||||
|
operation = tables.Column('operation', verbose_name=_('Operation'))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
name = 'services'
|
||||||
|
verbose_name = _('Services')
|
||||||
|
row_class = UpdateServiceRow
|
||||||
|
status_columns = ['status']
|
||||||
|
table_actions = (CreateService,)
|
58
tabula/tabula/tabula/tabs.py
Normal file
58
tabula/tabula/tabula/tabs.py
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# Copyright (c) 2013 Mirantis Inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from horizon import exceptions
|
||||||
|
from horizon import tabs
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from tabula.tabula import api
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class OverviewTab(tabs.Tab):
|
||||||
|
name = _("Service")
|
||||||
|
slug = "_service"
|
||||||
|
template_name = '_services.html'
|
||||||
|
|
||||||
|
def get_context_data(self, request):
|
||||||
|
data = self.tab_group.kwargs['service']
|
||||||
|
|
||||||
|
return {"service_name": data.name,
|
||||||
|
"service_status": data.status,
|
||||||
|
"service_type": data.service_type,
|
||||||
|
"service_domain": data.domain}
|
||||||
|
|
||||||
|
|
||||||
|
class LogsTab(tabs.Tab):
|
||||||
|
name = _("Logs")
|
||||||
|
slug = "_logs"
|
||||||
|
template_name = '_service_logs.html'
|
||||||
|
|
||||||
|
def get_context_data(self, request):
|
||||||
|
data = self.tab_group.kwargs['service']
|
||||||
|
|
||||||
|
reports = api.get_status_message_for_service(request, data.id)
|
||||||
|
|
||||||
|
return {"reports": reports}
|
||||||
|
|
||||||
|
|
||||||
|
class ServicesTabs(tabs.TabGroup):
|
||||||
|
slug = "services_details"
|
||||||
|
tabs = (OverviewTab, LogsTab)
|
||||||
|
sticky = True
|
36
tabula/tabula/tabula/urls.py
Normal file
36
tabula/tabula/tabula/urls.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# Copyright (c) 2013 Mirantis Inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from django.conf.urls.defaults import patterns, url
|
||||||
|
|
||||||
|
from .views import IndexView, Services, CreateDCView, DetailServiceView
|
||||||
|
from .views import Wizard
|
||||||
|
from .forms import WizardFormServiceType, WizardFormConfiguration
|
||||||
|
|
||||||
|
VIEW_MOD = 'openstack_dashboard.dashboards.project.tabula.views'
|
||||||
|
|
||||||
|
urlpatterns = patterns(VIEW_MOD,
|
||||||
|
url(r'^$', IndexView.as_view(), name='index'),
|
||||||
|
url(r'^create$',
|
||||||
|
Wizard.as_view([WizardFormServiceType,
|
||||||
|
WizardFormConfiguration]),
|
||||||
|
name='create'),
|
||||||
|
url(r'^create_dc$', CreateDCView.as_view(),
|
||||||
|
name='create_dc'),
|
||||||
|
url(r'^(?P<data_center_id>[^/]+)/$',
|
||||||
|
Services.as_view(), name='services'),
|
||||||
|
url(r'^(?P<service_id>[^/]+)/details$',
|
||||||
|
DetailServiceView.as_view(),
|
||||||
|
name='service_details'))
|
205
tabula/tabula/tabula/views.py
Normal file
205
tabula/tabula/tabula/views.py
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
# Copyright (c) 2013 Mirantis Inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
|
from django.views import generic
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.contrib.formtools.wizard.views import SessionWizardView
|
||||||
|
|
||||||
|
from horizon import exceptions
|
||||||
|
from horizon import tabs
|
||||||
|
from horizon import tables
|
||||||
|
from horizon import workflows
|
||||||
|
from horizon.forms.views import ModalFormMixin
|
||||||
|
|
||||||
|
from tabula.tabula import api
|
||||||
|
|
||||||
|
from .tables import DCTable, ServicesTable
|
||||||
|
from .workflows import CreateDC
|
||||||
|
from .tabs import ServicesTabs
|
||||||
|
from .forms import (WizardFormADConfiguration, WizardFormIISConfiguration)
|
||||||
|
|
||||||
|
from horizon import messages
|
||||||
|
|
||||||
|
from django.http import HttpResponseRedirect
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Wizard(ModalFormMixin, SessionWizardView, generic.FormView):
|
||||||
|
template_name = 'services_tabs.html'
|
||||||
|
|
||||||
|
def done(self, form_list, **kwargs):
|
||||||
|
link = self.request.__dict__['META']['HTTP_REFERER']
|
||||||
|
datacenter_id = re.search('tabula/(\S+)', link).group(0)[6:-1]
|
||||||
|
|
||||||
|
url = "/project/tabula/%s/" % datacenter_id
|
||||||
|
|
||||||
|
service_type = form_list[0].data.get('0-service', '')
|
||||||
|
parameters = {'service_type': service_type}
|
||||||
|
data = form_list[1].data
|
||||||
|
if service_type == 'Active Directory':
|
||||||
|
parameters['configuration'] = 'standalone'
|
||||||
|
parameters['name'] = str(data.get('1-dc_name', 'noname'))
|
||||||
|
parameters['domain'] = parameters['name'] # Fix Me in orchestrator
|
||||||
|
parameters['adminPassword'] = str(data.get('1-adm_password', ''))
|
||||||
|
dc_count = int(data.get('1-dc_count', 1))
|
||||||
|
recovery_password = str(data.get('1-recovery_password', ''))
|
||||||
|
parameters['units'] = []
|
||||||
|
parameters['units'].append({'isMaster': True,
|
||||||
|
'recoveryPassword': recovery_password,
|
||||||
|
'location': 'west-dc'})
|
||||||
|
for dc in range(dc_count - 1):
|
||||||
|
parameters['units'].append({
|
||||||
|
'isMaster': False,
|
||||||
|
'recoveryPassword': recovery_password,
|
||||||
|
'location': 'west-dc'
|
||||||
|
})
|
||||||
|
|
||||||
|
elif service_type == 'IIS':
|
||||||
|
password = data.get('1-adm_password', '')
|
||||||
|
parameters['name'] = str(data.get('1-iis_name', 'noname'))
|
||||||
|
parameters['credentials'] = {'username': 'Administrator',
|
||||||
|
'password': password}
|
||||||
|
parameters['domain'] = str(data.get('1-iis_domain', ''))
|
||||||
|
password = form_list[1].data.get('1-adm_password', '')
|
||||||
|
domain = form_list[1].data.get('1-iis_domain', '')
|
||||||
|
dc_user = form_list[1].data.get('1-domain_user_name', '')
|
||||||
|
dc_pass = form_list[1].data.get('1-domain_user_password', '')
|
||||||
|
parameters['name'] = str(form_list[1].data.get('1-iis_name',
|
||||||
|
'noname'))
|
||||||
|
parameters['domain'] = parameters['name']
|
||||||
|
parameters['credentials'] = {'username': 'Administrator',
|
||||||
|
'password': password}
|
||||||
|
parameters['domain'] = str(domain)
|
||||||
|
parameters['location'] = 'west-dc'
|
||||||
|
|
||||||
|
parameters['units'] = []
|
||||||
|
parameters['units'].append({'id': '1',
|
||||||
|
'endpoint': [{'host': '10.0.0.1'}],
|
||||||
|
'location': 'west-dc'})
|
||||||
|
|
||||||
|
service = api.services_create(self.request, datacenter_id, parameters)
|
||||||
|
|
||||||
|
message = "The %s service successfully created." % service_type
|
||||||
|
messages.success(self.request, message)
|
||||||
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
|
def get_form(self, step=None, data=None, files=None):
|
||||||
|
|
||||||
|
form = super(Wizard, self).get_form(step, data, files)
|
||||||
|
if data:
|
||||||
|
self.service_type = data.get('0-service', '')
|
||||||
|
if self.service_type == 'Active Directory':
|
||||||
|
self.form_list['1'] = WizardFormADConfiguration
|
||||||
|
elif self.service_type == 'IIS':
|
||||||
|
self.form_list['1'] = WizardFormIISConfiguration
|
||||||
|
|
||||||
|
return form
|
||||||
|
|
||||||
|
def get_form_kwargs(self, step=None):
|
||||||
|
return {'request': self.request} if step == u'1' else {}
|
||||||
|
|
||||||
|
def get_form_step_data(self, form):
|
||||||
|
LOG.debug(form.data)
|
||||||
|
return form.data
|
||||||
|
|
||||||
|
def get_context_data(self, form, **kwargs):
|
||||||
|
context = super(Wizard, self).get_context_data(form=form, **kwargs)
|
||||||
|
if self.steps.index > 0:
|
||||||
|
context.update({'service_type': self.service_type})
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class IndexView(tables.DataTableView):
|
||||||
|
table_class = WinDCTable
|
||||||
|
template_name = 'index.html'
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
try:
|
||||||
|
data_centers = api.datacenters_list(self.request)
|
||||||
|
except:
|
||||||
|
data_centers = []
|
||||||
|
exceptions.handle(self.request,
|
||||||
|
_('Unable to retrieve data centers list.'))
|
||||||
|
return data_centers
|
||||||
|
|
||||||
|
|
||||||
|
class Services(tables.DataTableView):
|
||||||
|
table_class = ServicesTable
|
||||||
|
template_name = 'services.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(Services, self).get_context_data(**kwargs)
|
||||||
|
context['dc_name'] = self.dc_name
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
try:
|
||||||
|
dc_id = self.kwargs['data_center_id']
|
||||||
|
self.datacenter_id = dc_id
|
||||||
|
datacenter = api.datacenters_get(self.request, dc_id)
|
||||||
|
self.dc_name = datacenter.name
|
||||||
|
services = api.services_list(self.request, dc_id)
|
||||||
|
except:
|
||||||
|
services = []
|
||||||
|
exceptions.handle(self.request,
|
||||||
|
_('Unable to retrieve list of services for '
|
||||||
|
'data center "%s".') % self.dc_name)
|
||||||
|
self._services = services
|
||||||
|
return self._services
|
||||||
|
|
||||||
|
|
||||||
|
class DetailServiceView(tabs.TabView):
|
||||||
|
tab_group_class = ServicesTabs
|
||||||
|
template_name = 'service_details.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(DetailServiceView, self).get_context_data(**kwargs)
|
||||||
|
context["service"] = self.get_data()
|
||||||
|
context["service_name"] = self.get_data().name
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
if not hasattr(self, "_service"):
|
||||||
|
try:
|
||||||
|
service_id = self.kwargs['service_id']
|
||||||
|
service = api.get_service_datails(self.request, service_id)
|
||||||
|
except:
|
||||||
|
redirect = reverse('horizon:project:tabula:index')
|
||||||
|
exceptions.handle(self.request,
|
||||||
|
_('Unable to retrieve details for '
|
||||||
|
'service "%s".') % service_id,
|
||||||
|
redirect=redirect)
|
||||||
|
self._service = service
|
||||||
|
return self._service
|
||||||
|
|
||||||
|
def get_tabs(self, request, *args, **kwargs):
|
||||||
|
service = self.get_data()
|
||||||
|
return self.tab_group_class(request, service=service, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class CreateDCView(workflows.WorkflowView):
|
||||||
|
workflow_class = CreateDC
|
||||||
|
template_name = 'create_dc.html'
|
||||||
|
|
||||||
|
def get_initial(self):
|
||||||
|
initial = super(CreateDCView, self).get_initial()
|
||||||
|
initial['project_id'] = self.request.user.tenant_id
|
||||||
|
initial['user_id'] = self.request.user.id
|
||||||
|
return initial
|
96
tabula/tabula/tabula/workflows.py
Normal file
96
tabula/tabula/tabula/workflows.py
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
# Copyright (c) 2013 Mirantis Inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
|
from django.utils.text import normalize_newlines
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
from horizon import exceptions
|
||||||
|
from horizon import forms
|
||||||
|
from horizon import workflows
|
||||||
|
|
||||||
|
from tabula.tabula import api
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class SelectProjectUserAction(workflows.Action):
|
||||||
|
project_id = forms.ChoiceField(label=_("Project"))
|
||||||
|
user_id = forms.ChoiceField(label=_("User"))
|
||||||
|
|
||||||
|
def __init__(self, request, *args, **kwargs):
|
||||||
|
super(SelectProjectUserAction, self).__init__(request, *args, **kwargs)
|
||||||
|
# Set our project choices
|
||||||
|
projects = [(tenant.id, tenant.name)
|
||||||
|
for tenant in request.user.authorized_tenants]
|
||||||
|
self.fields['project_id'].choices = projects
|
||||||
|
|
||||||
|
# Set our user options
|
||||||
|
users = [(request.user.id, request.user.username)]
|
||||||
|
self.fields['user_id'].choices = users
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
name = _("Project & User")
|
||||||
|
# Unusable permission so this is always hidden. However, we
|
||||||
|
# keep this step in the workflow for validation/verification purposes.
|
||||||
|
permissions = ("!",)
|
||||||
|
|
||||||
|
|
||||||
|
class SelectProjectUser(workflows.Step):
|
||||||
|
action_class = SelectProjectUserAction
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigureDCAction(workflows.Action):
|
||||||
|
name = forms.CharField(label=_("Environment Name"), required=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
name = _("Environment")
|
||||||
|
help_text_template = "_data_center_help.html"
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigureDC(workflows.Step):
|
||||||
|
action_class = ConfigureDCAction
|
||||||
|
contibutes = ('name',)
|
||||||
|
|
||||||
|
def contribute(self, data, context):
|
||||||
|
if data:
|
||||||
|
context['name'] = data.get('name', '')
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class CreateDC(workflows.Workflow):
|
||||||
|
slug = "create"
|
||||||
|
name = _("Create Environment")
|
||||||
|
finalize_button_name = _("Create")
|
||||||
|
success_message = _('Created environment "%s".')
|
||||||
|
failure_message = _('Unable to create environment "%s".')
|
||||||
|
success_url = "horizon:project:tabula:index"
|
||||||
|
default_steps = (SelectProjectUser, ConfigureDC)
|
||||||
|
|
||||||
|
def format_status_message(self, message):
|
||||||
|
name = self.context.get('name', 'noname')
|
||||||
|
return message % name
|
||||||
|
|
||||||
|
def handle(self, request, context):
|
||||||
|
try:
|
||||||
|
datacenter = api.datacenters_create(request, context)
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
exceptions.handle(request)
|
||||||
|
return False
|
Reference in New Issue
Block a user