Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
CIRCLE
/
cloud
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
94
Merge Requests
10
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
9ab17d5f
authored
Sep 20, 2016
by
Sulyok Gábor
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit previous changes for Setty
parent
39a7ffc9
Pipeline
#225
failed with stage
in 0 seconds
Changes
21
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
1812 additions
and
929 deletions
+1812
-929
circle/setty/admin.py
+11
-3
circle/setty/controller.py
+203
-0
circle/setty/migrations/0013_saltstack_changes.py
+76
-0
circle/setty/migrations/0014_auto_20160320_1724.py
+35
-0
circle/setty/migrations/0015_allow_blank_elementcategory_parent.py
+19
-0
circle/setty/migrations/0016_auto_20160320_1753.py
+30
-0
circle/setty/migrations/0017_auto_20160320_1828.py
+30
-0
circle/setty/migrations/0018_auto_20160420_1728.py
+23
-0
circle/setty/migrations/0019_auto_20160420_2043.py
+19
-0
circle/setty/migrations/0020_auto_20160420_2132.py
+19
-0
circle/setty/migrations/0021_element_real_type.py
+20
-0
circle/setty/models.py
+329
-13
circle/setty/saltstackhelper.py
+47
-0
circle/setty/static/setty/apache.jpg
+0
-0
circle/setty/static/setty/lighttpd.jpg
+0
-0
circle/setty/static/setty/nginx.jpg
+0
-0
circle/setty/static/setty/setty.js
+863
-781
circle/setty/static/setty/ubuntu.jpg
+0
-0
circle/setty/static/setty/wordpress.jpg
+0
-0
circle/setty/tables.py
+46
-46
circle/setty/views.py
+42
-86
No files found.
circle/setty/admin.py
View file @
9ab17d5f
...
...
@@ -17,13 +17,21 @@
from
django.contrib
import
admin
from
.models
import
(
Element
,
Service
,
ElementCategory
,
ElementTemplate
,
ElementConnection
,
Service
,
Machine
,
NginxNode
,
MySQLNode
,
PostgreSQLNode
)
admin
.
site
.
register
(
Element
)
admin
.
site
.
register
(
Element
Category
)
admin
.
site
.
register
(
ElementTemplate
)
admin
.
site
.
register
(
ElementConnection
)
admin
.
site
.
register
(
Service
)
admin
.
site
.
register
(
Machine
)
admin
.
site
.
register
(
NginxNode
)
admin
.
site
.
register
(
MySQLNode
)
admin
.
site
.
register
(
PostgreSQLNode
)
circle/setty/controller.py
0 → 100644
View file @
9ab17d5f
from
.models
import
*
from
django.core.exceptions
import
PermissionDenied
from
django.db.models
import
Q
from
django.db.models.loading
import
get_model
from
saltstackhelper
import
*
import
os
class
SettyController
:
salthelper
=
SaltStackHelper
()
@staticmethod
def
saveService
(
serviceId
,
serviceName
,
serviceNodes
,
machines
,
elementConnections
):
service
=
None
try
:
service
=
Service
.
objects
.
get
(
id
=
serviceId
)
except
Service
.
DoesNotExist
:
return
JsonResponse
(
{
'error'
:
'Service not found'
})
service
.
name
=
serviceName
service
.
save
()
#first check machine names
#validMachineNames = self.salthelper.getAllMinionsUngrouped()
Machine
.
objects
.
filter
(
service
=
service
)
.
delete
()
for
machineData
in
machines
:
# if machineData["hostname"] in validMachineNames:
machineSaved
=
Machine
(
service
=
service
)
machineSaved
.
fromDataDictionary
(
machineData
)
machineSaved
.
save
()
ServiceNode
.
objects
.
filter
(
service
=
service
)
.
delete
()
for
node
in
serviceNodes
:
elementTemplateId
=
node
[
"displayId"
]
.
split
(
"_"
)[
0
]
elementTemplate
=
ElementTemplate
.
objects
.
get
(
id
=
elementTemplateId
)
newNode
=
get_model
(
'setty'
,
elementTemplate
.
prototype
)
.
clone
()
newNode
.
service
=
service
newNode
.
fromDataDictionary
(
node
)
newNode
.
save
()
for
elementConnection
in
elementConnections
:
sourceId
=
elementConnection
[
'sourceId'
]
targetId
=
elementConnection
[
'targetId'
]
sourceEndpoint
=
elementConnection
[
'sourceEndpoint'
]
targetEndpoint
=
elementConnection
[
'targetEndpoint'
]
connectionParameters
=
elementConnection
[
'parameters'
]
targetObject
=
Element
.
objects
.
get
(
display_id
=
targetId
)
sourceObject
=
Element
.
objects
.
get
(
display_id
=
sourceId
)
connectionObject
=
ElementConnection
(
target
=
targetObject
,
source
=
sourceObject
,
target_endpoint
=
targetEndpoint
,
source_endpoint
=
sourceEndpoint
,
parameters
=
connectionParameters
)
connectionObject
.
save
()
return
{
"serviceName"
:
serviceName
}
@staticmethod
def
loadService
(
serviceId
):
service
=
None
try
:
service
=
Service
.
objects
.
get
(
id
=
serviceId
)
except
Service
.
DoesNotExist
:
return
JsonResponse
({
'error'
:
'Service not found'
})
machineList
=
Machine
.
objects
.
filter
(
service
=
service
)
serviceNodes
=
[]
elementConnections
=
[]
machines
=
[]
for
machine
in
machineList
:
machines
.
append
(
machine
.
getDataDictionary
())
serviveNodeList
=
ServiceNode
.
objects
.
filter
(
service
=
service
)
elementConnectionList
=
ElementConnection
.
objects
.
filter
(
Q
(
target__in
=
serviveNodeList
)
|
Q
(
source__in
=
serviveNodeList
))
for
servideNode
in
serviveNodeList
:
serviceNodes
.
append
(
servideNode
.
cast
()
.
getDataDictionary
()
)
for
elementConnection
in
elementConnectionList
:
elementConnections
.
append
(
elementConnection
.
getDataDictionary
()
)
return
{
'serviceName'
:
service
.
name
,
'elementConnections'
:
elementConnections
,
'serviceNodes'
:
serviceNodes
,
'machines'
:
machines
}
@staticmethod
def
getInformation
(
elementTemplateId
,
hostname
):
if
elementTemplateId
:
try
:
elementTemplate
=
ElementTemplate
.
objects
.
get
(
id
=
elementTemplateId
)
model
=
get_model
(
'setty'
,
elementTemplate
.
prototype
)
return
model
.
getInformation
()
except
ElementTemplate
.
DoesNotExist
:
return
except
LookupError
:
return
elif
hostname
:
return
Machine
.
getInformation
()
elif
hostname
and
elementTemplateId
:
raise
PermissionDenied
# TODO: something more meaningful
else
:
raise
PermissionDenied
# TODO: something more meaningful
@staticmethod
def
getMachineAvailableList
(
service_id
,
used_hostnames
):
all_minions
=
SettyController
.
salthelper
.
getAllMinionsGrouped
()
result
=
[]
#TODO: filter out used ones
for
item
in
all_minions
[
"up"
]:
result
.
append
(
{
'hostname'
:
item
,
'hardware-info'
:
SettyController
.
salthelper
.
getMinionBasicHardwareInfo
(
item
),
'status'
:
'up'
}
)
for
item
in
all_minions
[
"down"
]:
result
.
append
(
{
'hostname'
:
item
,
'status'
:
'down'
})
return
{
'machinedata'
:
result
}
@staticmethod
def
addMachine
(
hostname
):
try
:
Machine
.
objects
.
get
(
hostname
=
hostname
)
return
{
'error'
:
'already added or doesnt exists'
}
except
:
pass
if
SettyController
.
salthelper
.
checkMinionExists
(
hostname
):
machine
=
Machine
.
clone
()
machine
.
hostname
=
hostname
return
machine
.
getDataDictionary
()
else
:
return
{
'error'
:
'already added or doesnt exists'
}
@staticmethod
def
addServiceNode
(
elementTemplateId
):
if
elementTemplateId
:
try
:
elementTemplate
=
ElementTemplate
.
objects
.
get
(
id
=
elementTemplateId
)
model
=
get_model
(
'setty'
,
elementTemplate
.
prototype
)
return
model
.
clone
()
.
getDataDictionary
()
except
ElementTemplate
.
DoesNotExist
:
return
{
'error'
:
'lofaszka'
}
except
:
return
{
'error'
:
'valami nagyon el lett baszva'
}
else
:
return
{
'error'
:
'templateid'
}
@staticmethod
def
deploy
(
serviceId
):
service
=
Service
.
objects
.
get
(
id
=
serviceId
)
machines
=
Machine
.
objects
.
filter
(
service
=
service
)
elementConnections
=
ElementConnection
.
objects
.
filter
(
Q
(
target__in
=
machines
)
|
Q
(
source__in
=
machines
)
)
firstLevelServiceNodes
=
[]
#phase one: set the machine ptr in serviceNodes which can be accessed by
# connections from machines
for
machine
in
machines
:
for
connection
in
elementConnections
:
serviceNode
=
None
if
connection
.
target
.
cast
()
==
machine
:
serviceNode
=
connection
.
source
.
cast
()
serviceNode
.
setMachineForDeploy
(
machine
)
elif
connection
.
source
.
cast
()
==
machine
:
serviceNode
=
connection
.
target
.
cast
()
serviceNode
.
setMachineForDeploy
(
machine
)
else
:
raise
PermissionDenied
firstLevelServiceNodes
.
append
(
serviceNode
)
#phase two: let the nodes create configurations recursively
configuratedNodes
=
list
()
for
serviceNode
in
firstLevelServiceNodes
:
generatedNodes
=
serviceNode
.
generateConfigurationRecursively
()
if
isinstance
(
generatedNodes
,
list
):
configuratedNodes
=
configuratedNodes
+
generatedNodes
else
:
configuratedNodes
.
append
(
generatedNodes
)
#phase three: sort the nodes by deployment priority(lower the prio, later in the deployement)
configuratedNodes
.
sort
(
reverse
=
True
)
#deploy the nodes
for
node
in
configuratedNodes
:
SettyController
.
salthelper
.
deploy
(
node
.
machine
.
hostname
,
node
.
generatedConfig
)
return
{
'status'
:
'deployed'
}
#cleanup the temporary data
''' for node in configuratedNodes:
node.deployCleanUp()'''
\ No newline at end of file
circle/setty/migrations/0013_saltstack_changes.py
0 → 100644
View file @
9ab17d5f
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
django.db
import
models
,
migrations
import
setty.storage
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'setty'
,
'0012_auto_20160308_1432'
),
]
operations
=
[
migrations
.
CreateModel
(
name
=
'ElementCategory'
,
fields
=
[
(
'id'
,
models
.
AutoField
(
verbose_name
=
'ID'
,
serialize
=
False
,
auto_created
=
True
,
primary_key
=
True
)),
(
'name'
,
models
.
CharField
(
max_length
=
50
)),
(
'parent_category'
,
models
.
ForeignKey
(
to
=
'setty.ElementCategory'
,
null
=
True
)),
],
),
migrations
.
CreateModel
(
name
=
'Machine'
,
fields
=
[
(
'element_ptr'
,
models
.
OneToOneField
(
parent_link
=
True
,
auto_created
=
True
,
primary_key
=
True
,
serialize
=
False
,
to
=
'setty.Element'
)),
(
'hostname'
,
models
.
TextField
()),
(
'alias'
,
models
.
CharField
(
max_length
=
50
)),
(
'config_file'
,
models
.
FileField
(
default
=
None
,
storage
=
setty
.
storage
.
OverwriteStorage
(),
upload_to
=
b
'setty/machine_configs/'
)),
(
'description'
,
models
.
TextField
(
default
=
b
''
)),
(
'status'
,
models
.
CharField
(
max_length
=
1
,
choices
=
[(
1
,
b
'Running'
),
(
2
,
b
'Unreachable'
)])),
],
options
=
{
'abstract'
:
False
,
},
bases
=
(
'setty.element'
,),
),
migrations
.
CreateModel
(
name
=
'ServiceNode'
,
fields
=
[
(
'element_ptr'
,
models
.
OneToOneField
(
parent_link
=
True
,
auto_created
=
True
,
primary_key
=
True
,
serialize
=
False
,
to
=
'setty.Element'
)),
(
'name'
,
models
.
CharField
(
max_length
=
50
)),
(
'config_file'
,
models
.
FileField
(
default
=
None
,
storage
=
setty
.
storage
.
OverwriteStorage
(),
upload_to
=
b
'setty/node_configs/'
)),
(
'description'
,
models
.
TextField
(
default
=
b
''
)),
(
'machine'
,
models
.
ForeignKey
(
to
=
'setty.Machine'
)),
],
bases
=
(
'setty.element'
,),
),
migrations
.
RemoveField
(
model_name
=
'element'
,
name
=
'parameters'
,
),
migrations
.
RemoveField
(
model_name
=
'element'
,
name
=
'service'
,
),
migrations
.
RemoveField
(
model_name
=
'elementtemplate'
,
name
=
'parameters'
,
),
migrations
.
AlterField
(
model_name
=
'service'
,
name
=
'status'
,
field
=
models
.
CharField
(
default
=
1
,
max_length
=
1
,
choices
=
[(
1
,
b
'Draft'
),
(
2
,
b
'Deployed'
)]),
),
migrations
.
AddField
(
model_name
=
'machine'
,
name
=
'service'
,
field
=
models
.
ForeignKey
(
related_name
=
'service_id'
,
to
=
'setty.Service'
),
),
migrations
.
AddField
(
model_name
=
'elementtemplate'
,
name
=
'category'
,
field
=
models
.
ForeignKey
(
to
=
'setty.ElementCategory'
,
null
=
True
),
),
]
circle/setty/migrations/0014_auto_20160320_1724.py
0 → 100644
View file @
9ab17d5f
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
django.db
import
models
,
migrations
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'setty'
,
'0013_saltstack_changes'
),
]
operations
=
[
migrations
.
CreateModel
(
name
=
'NginxNode'
,
fields
=
[
(
'servicenode_ptr'
,
models
.
OneToOneField
(
parent_link
=
True
,
auto_created
=
True
,
primary_key
=
True
,
serialize
=
False
,
to
=
'setty.ServiceNode'
)),
(
'worker_connections'
,
models
.
PositiveIntegerField
()),
],
bases
=
(
'setty.servicenode'
,),
),
migrations
.
CreateModel
(
name
=
'WebServerNode'
,
fields
=
[
(
'servicenode_ptr'
,
models
.
OneToOneField
(
parent_link
=
True
,
auto_created
=
True
,
primary_key
=
True
,
serialize
=
False
,
to
=
'setty.ServiceNode'
)),
(
'useSSL'
,
models
.
BooleanField
(
default
=
False
)),
(
'listeningPort'
,
models
.
PositiveIntegerField
()),
],
bases
=
(
'setty.servicenode'
,),
),
migrations
.
RemoveField
(
model_name
=
'elementtemplate'
,
name
=
'tags'
,
),
]
circle/setty/migrations/0015_allow_blank_elementcategory_parent.py
0 → 100644
View file @
9ab17d5f
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
django.db
import
models
,
migrations
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'setty'
,
'0014_auto_20160320_1724'
),
]
operations
=
[
migrations
.
AlterField
(
model_name
=
'elementcategory'
,
name
=
'parent_category'
,
field
=
models
.
ForeignKey
(
blank
=
True
,
to
=
'setty.ElementCategory'
,
null
=
True
),
),
]
circle/setty/migrations/0016_auto_20160320_1753.py
0 → 100644
View file @
9ab17d5f
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
django.db
import
models
,
migrations
import
setty.storage
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'setty'
,
'0015_allow_blank_elementcategory_parent'
),
]
operations
=
[
migrations
.
AddField
(
model_name
=
'elementtemplate'
,
name
=
'prototype'
,
field
=
models
.
TextField
(
default
=
b
'<SYNTAX ERROR>'
),
),
migrations
.
AlterField
(
model_name
=
'elementtemplate'
,
name
=
'compatibles'
,
field
=
models
.
ManyToManyField
(
related_name
=
'compatibles_rel_+'
,
to
=
'setty.ElementTemplate'
,
blank
=
True
),
),
migrations
.
AlterField
(
model_name
=
'elementtemplate'
,
name
=
'logo'
,
field
=
models
.
FileField
(
storage
=
setty
.
storage
.
OverwriteStorage
(),
upload_to
=
b
'setty/'
,
blank
=
True
),
),
]
circle/setty/migrations/0017_auto_20160320_1828.py
0 → 100644
View file @
9ab17d5f
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
django.db
import
models
,
migrations
import
setty.storage
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'setty'
,
'0016_auto_20160320_1753'
),
]
operations
=
[
migrations
.
RemoveField
(
model_name
=
'nginxnode'
,
name
=
'servicenode_ptr'
,
),
migrations
.
AddField
(
model_name
=
'nginxnode'
,
name
=
'webservernode_ptr'
,
field
=
models
.
OneToOneField
(
parent_link
=
True
,
auto_created
=
True
,
primary_key
=
True
,
default
=
None
,
serialize
=
False
,
to
=
'setty.WebServerNode'
),
preserve_default
=
False
,
),
migrations
.
AlterField
(
model_name
=
'elementtemplate'
,
name
=
'logo'
,
field
=
models
.
FileField
(
storage
=
setty
.
storage
.
OverwriteStorage
(),
upload_to
=
b
'setty/'
),
),
]
circle/setty/migrations/0018_auto_20160420_1728.py
0 → 100644
View file @
9ab17d5f
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
django.db
import
models
,
migrations
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'setty'
,
'0017_auto_20160320_1828'
),
]
operations
=
[
migrations
.
RemoveField
(
model_name
=
'servicenode'
,
name
=
'machine'
,
),
migrations
.
AddField
(
model_name
=
'servicenode'
,
name
=
'service'
,
field
=
models
.
ForeignKey
(
default
=
None
,
to
=
'setty.Service'
),
),
]
circle/setty/migrations/0019_auto_20160420_2043.py
0 → 100644
View file @
9ab17d5f
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
django.db
import
models
,
migrations
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'setty'
,
'0018_auto_20160420_1728'
),
]
operations
=
[
migrations
.
AlterField
(
model_name
=
'servicenode'
,
name
=
'service'
,
field
=
models
.
ForeignKey
(
related_name
=
'node_service_id'
,
default
=
None
,
to
=
'setty.Service'
),
),
]
circle/setty/migrations/0020_auto_20160420_2132.py
0 → 100644
View file @
9ab17d5f
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
django.db
import
models
,
migrations
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'setty'
,
'0019_auto_20160420_2043'
),
]
operations
=
[
migrations
.
AlterField
(
model_name
=
'servicenode'
,
name
=
'service'
,
field
=
models
.
ForeignKey
(
default
=
None
,
to
=
'setty.Service'
),
),
]
circle/setty/migrations/0021_element_real_type.py
0 → 100644
View file @
9ab17d5f
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
django.db
import
models
,
migrations
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'contenttypes'
,
'0002_remove_content_type_name'
),
(
'setty'
,
'0020_auto_20160420_2132'
),
]
operations
=
[
migrations
.
AddField
(
model_name
=
'element'
,
name
=
'real_type'
,
field
=
models
.
ForeignKey
(
default
=
None
,
editable
=
False
,
to
=
'contenttypes.ContentType'
),
),
]
circle/setty/models.py
View file @
9ab17d5f
...
...
@@ -17,46 +17,101 @@
from
django.db
import
models
from
django.db.models
import
Model
from
django.contrib.contenttypes.models
import
ContentType
from
django.contrib.auth.models
import
User
from
taggit.managers
import
TaggableManager
from
django.utils.translation
import
ugettext_lazy
as
_
from
storage
import
OverwriteStorage
import
os
# TODO: derive from object or keep the tricky base function calling?
# TODO: exceptions
SALTSTACK_STATE_FOLDER
=
"/srv/salt"
def
replaceParameter
(
config
,
parameterToReplace
,
newValue
):
configEdited
=
config
.
replace
(
parameterToReplace
,
str
(
newValue
))
return
configEdited
class
Service
(
models
.
Model
):
SERVICE_STATUS_CHOICES
=
((
1
,
'Draft'
),
(
2
,
'Deployed'
))
class
Service
(
Model
):
user
=
models
.
ForeignKey
(
User
)
name
=
models
.
TextField
(
verbose_name
=
"Name"
)
status
=
models
.
CharField
(
max_length
=
50
)
status
=
models
.
CharField
(
choices
=
SERVICE_STATUS_CHOICES
,
max_length
=
1
,
default
=
1
)
def
__unicode__
(
self
):
return
self
.
name
class
ElementTemplate
(
Model
):
class
ElementCategory
(
models
.
Model
):
name
=
models
.
CharField
(
max_length
=
50
)
parent_category
=
models
.
ForeignKey
(
'self'
,
on_delete
=
models
.
CASCADE
,
null
=
True
,
blank
=
True
)
def
__unicode__
(
self
):
return
self
.
name
class
ElementTemplate
(
models
.
Model
):
name
=
models
.
CharField
(
max_length
=
50
)
logo
=
models
.
FileField
(
upload_to
=
'setty/'
,
storage
=
OverwriteStorage
())
description
=
models
.
TextField
()
parameters
=
models
.
TextField
()
compatibles
=
models
.
ManyToManyField
(
'self'
)
tags
=
TaggableManager
(
blank
=
True
,
verbose_name
=
_
(
"tags"
))
compatibles
=
models
.
ManyToManyField
(
'self'
,
blank
=
True
)
category
=
models
.
ForeignKey
(
ElementCategory
,
on_delete
=
models
.
CASCADE
,
null
=
True
)
prototype
=
models
.
TextField
(
default
=
"<SYNTAX ERROR>"
)
def
__unicode__
(
self
):
return
self
.
name
# http://stackoverflow.com/questions/929029/how-do-i-access-the-child-classes-of-an-object-in-django-without-knowing-the-name/929982#929982
# Super base class to prevent messing the code in the controller
# it saves the type info to DB, and when the objects are queried, the cast method returns the real class
# not the base
class
Element
(
Model
):
service
=
models
.
ForeignKey
(
Service
,
on_delete
=
models
.
CASCADE
)
parameters
=
models
.
TextField
()
class
InheritanceCastModel
(
models
.
Model
):
real_type
=
models
.
ForeignKey
(
ContentType
,
editable
=
False
,
default
=
None
)
def
save
(
self
,
*
args
,
**
kwargs
):
if
not
self
.
id
:
self
.
real_type
=
self
.
_get_real_type
()
super
(
InheritanceCastModel
,
self
)
.
save
(
*
args
,
**
kwargs
)
def
_get_real_type
(
self
):
return
ContentType
.
objects
.
get_for_model
(
type
(
self
))
def
cast
(
self
):
return
self
.
real_type
.
get_object_for_this_type
(
pk
=
self
.
pk
)
class
Meta
:
abstract
=
True
class
Element
(
InheritanceCastModel
):
display_id
=
models
.
TextField
()
position_left
=
models
.
FloatField
()
position_top
=
models
.
FloatField
()
anchor_number
=
models
.
PositiveSmallIntegerField
()
def
__unicode__
(
self
):
return
"
%
s (
%
s)"
%
(
self
.
service
.
name
,
self
.
display_id
)
def
getDisplayData
(
self
):
return
{
'displayId'
:
self
.
display_id
,
'positionLeft'
:
self
.
position_left
,
'positionTop'
:
self
.
position_top
,
'anchorNumber'
:
self
.
anchor_number
}
def
setDisplayData
(
self
,
data
):
self
.
display_id
=
data
[
"displayId"
]
self
.
position_left
=
data
[
"positionLeft"
]
self
.
position_top
=
data
[
"positionTop"
]
self
.
anchor_number
=
data
[
"anchorNumber"
]
class
ElementConnection
(
Model
):
class
ElementConnection
(
models
.
Model
):
target
=
models
.
ForeignKey
(
Element
,
related_name
=
'target'
,
...
...
@@ -70,4 +125,265 @@ class ElementConnection(Model):
parameters
=
models
.
TextField
()
def
__unicode__
(
self
):
return
"
%
s (
%
d)"
%
(
self
.
target
.
service
.
name
,
self
.
id
)
return
"
%
d"
%
self
.
id
def
getDataDictionary
(
self
):
return
{
'targetEndpoint'
:
self
.
target_endpoint
,
'sourceEndpoint'
:
self
.
source_endpoint
,
'parameters'
:
self
.
parameters
}
class
Machine
(
Element
):
# As a real machine
MACHINE_STATUS_CHOICES
=
(
(
1
,
'Running'
),
(
2
,
'Unreachable'
))
service
=
models
.
ForeignKey
(
Service
,
on_delete
=
models
.
CASCADE
,
related_name
=
"service_id"
)
hostname
=
models
.
TextField
(
null
=
False
)
# also serves as salt-minion id
alias
=
models
.
CharField
(
max_length
=
50
)
#config_file = models.FileField(default=None,upload_to='setty/machine_configs/', storage=OverwriteStorage())
description
=
models
.
TextField
(
default
=
""
)
status
=
models
.
CharField
(
choices
=
MACHINE_STATUS_CHOICES
,
max_length
=
1
)
def
__unicode__
(
self
):
return
"
%
s"
%
self
.
hostname
@staticmethod
def
getInformation
():
return
{
'hostname'
:
hostname
.
get_internal_type
(),
'alias'
:
alias
.
get_internal_type
(),
'description'
:
description
.
get_internal_type
()}
def
getDataDictionary
(
self
):
element_data
=
self
.
getDisplayData
()
self_data
=
{
'hostname'
:
self
.
hostname
,
'alias'
:
self
.
alias
,
'description'
:
self
.
description
}
element_data
.
update
(
self_data
)
return
element_data
def
fromDataDictionary
(
self
,
data
):
self
.
setDisplayData
(
data
)
self
.
hostname
=
data
[
"hostname"
]
self
.
alias
=
data
[
"alias"
]
self
.
description
=
data
[
"description"
]
@staticmethod
def
clone
():
return
Machine
()
class
ServiceNode
(
Element
):
service
=
models
.
ForeignKey
(
Service
,
on_delete
=
models
.
CASCADE
,
default
=
None
)
name
=
models
.
CharField
(
max_length
=
50
)
config_file
=
models
.
FileField
(
default
=
None
,
upload_to
=
'setty/node_configs/'
,
storage
=
OverwriteStorage
())
description
=
models
.
TextField
(
default
=
""
)
machine
=
None
# for deploying
generatedConfig
=
None
def
__unicode__
(
self
):
return
"
%
s"
%
self
.
name
def
getDataDictionary
(
self
):
element_data
=
self
.
getDisplayData
()
self_data
=
{
'name'
:
self
.
name
,
'description'
:
self
.
description
}
element_data
.
update
(
self_data
)
return
element_data
def
fromDataDictionary
(
self
,
data
):
self
.
setDisplayData
(
data
)
self
.
name
=
data
[
'name'
]
self
.
description
=
data
[
'description'
]
@staticmethod
def
getInformation
():
return
{
'name'
:
ServiceNode
.
_meta
.
get_field
(
'name'
)
.
get_internal_type
(),
'description'
:
ServiceNode
.
_meta
.
get_field
(
'description'
)
.
get_internal_type
()}
@staticmethod
def
clone
():
raise
PermissionDenied
def
__cmp__
(
self
,
other
):
return
self
.
getDeploymentPriority
(
self
)
.
__cmp__
(
other
.
getDeploymentPriority
(
other
))
# functions for deployement
def
setMachineForDeploy
(
self
,
machine
):
self
.
machine
=
machine
def
getDeploymentPriority
(
self
):
return
0
def
generateConfigurationRecursively
(
self
):
raise
PermissionDenied
class
WebServerNode
(
ServiceNode
):
useSSL
=
models
.
BooleanField
(
default
=
False
)
listeningPort
=
models
.
PositiveIntegerField
()
def
getDataDictionary
(
self
):
element_data
=
ServiceNode
.
getDataDictionary
(
self
)
self_data
=
{
'useSSL'
:
self
.
useSSL
,
'listeningPort'
:
self
.
listeningPort
}
element_data
.
update
(
self_data
)
return
element_data
def
fromDataDictionary
(
self
,
data
):
ServiceNode
.
fromDataDictionary
(
self
,
data
)
self
.
useSSL
=
data
[
'useSSL'
]
self
.
listeningPort
=
data
[
'listeningPort'
]
@staticmethod
def
getInformation
():
superInformation
=
ServiceNode
.
getInformation
()
ownInformation
=
{
'useSSL'
:
WebServerNode
.
_meta
.
get_field
(
'useSSL'
)
.
get_internal_type
(),
'listeningPort'
:
WebServerNode
.
_meta
.
get_field
(
'listeningPort'
)
.
get_internal_type
()}
ownInformation
.
update
(
superInformation
)
return
ownInformation
@staticmethod
def
getDeploymentPriority
(
self
):
return
10
def
generateConfiguration
(
self
,
config
=
""
):
config
=
replaceParameter
(
config
,
r"
%%
USE_SSL
%%
"
,
self
.
useSSL
)
config
=
replaceParameter
(
config
,
r"
%%
LISTENING_PORT
%%
"
,
self
.
listeningPort
)
return
config
class
NginxNode
(
WebServerNode
):
worker_connections
=
models
.
PositiveIntegerField
()
def
getDataDictionary
(
self
):
element_data
=
WebServerNode
.
getDataDictionary
(
self
)
self_data
=
{
'worker_connections'
:
self
.
worker_connections
}
element_data
.
update
(
self_data
)
return
element_data
def
fromDataDictionary
(
self
,
data
):
WebServerNode
.
fromDataDictionary
(
self
,
data
)
self
.
worker_connections
=
data
[
'worker_connections'
]
@staticmethod
def
getInformation
():
superInformation
=
WebServerNode
.
getInformation
()
ownInformation
=
{
'worker_connections'
:
NginxNode
.
_meta
.
get_field
(
'worker_connections'
)
.
get_internal_type
()}
ownInformation
.
update
(
superInformation
)
return
ownInformation
@staticmethod
def
clone
():
return
NginxNode
()
def
generateConfigurationRecursively
(
self
):
config
=
str
()
exampleFilePath
=
os
.
path
.
join
(
SALTSTACK_STATE_FOLDER
,
"nginx.example"
)
with
open
(
exampleFilePath
,
'r'
)
as
configFile
:
config
=
configFile
.
read
()
config
=
WebServerNode
.
generateConfiguration
(
self
,
config
)
config
=
replaceParameter
(
config
,
r"
%%
WORKER_CONNECTIONS
%%
"
,
self
.
worker_connections
)
self
.
generatedConfig
=
"nginx_
%
s.sls"
%
self
.
machine
.
hostname
with
open
(
os
.
path
.
join
(
SALTSTACK_STATE_FOLDER
,
self
.
generatedConfig
),
'w'
)
as
generatedConfigFile
:
generatedConfigFile
.
write
(
config
)
configuredNodes
=
[]
configuredNodes
.
append
(
self
)
return
configuredNodes
class
DatabaseNode
(
ServiceNode
):
adminUserName
=
models
.
CharField
(
max_length
=
50
)
adminPassword
=
models
.
CharField
(
max_length
=
50
)
listeningPort
=
models
.
PositiveIntegerField
()
def
getDataDictionary
(
self
):
element_data
=
ServiceNode
.
getDataDictionary
(
self
)
self_data
=
{
'admin_username'
:
self
.
adminUserName
,
'admin_password'
:
self
.
adminPassword
,
'listeningPort'
:
self
.
listeningPort
}
element_data
.
update
(
self_data
)
return
element_data
def
fromDataDictionary
(
self
,
data
):
ServiceNode
.
fromDataDictionary
(
self
,
data
)
self
.
adminUserName
=
data
[
'admin_username'
]
self
.
adminPassword
=
data
[
'admin_password'
]
self
.
listeningPort
=
data
[
'listeningPort'
]
@staticmethod
def
getInformation
():
superInformation
=
ServiceNode
.
getInformation
()
ownInformation
=
{
'admin_username'
:
DatabaseNode
.
_meta
.
get_field
(
'adminUserName'
)
.
get_internal_type
(),
'admin_password'
:
DatabaseNode
.
_meta
.
get_field
(
'adminPassword'
)
.
get_internal_type
(),
'listeningPort'
:
DatabaseNode
.
_meta
.
get_field
(
'listeningPort'
)
.
get_internal_type
()}
ownInformation
.
update
(
superInformation
)
return
ownInformation
@staticmethod
def
getDeploymentPriority
(
self
):
return
10
def
generateConfiguration
(
self
,
config
=
""
):
config
=
replaceParameter
(
config
,
r"
%%
ADMIN_USERNAME
%%
"
,
self
.
adminUserName
)
config
=
replaceParameter
(
config
,
r"
%%
ADMIN_PASSWORD
%%
"
,
self
.
adminUserName
)
config
=
replaceParameter
(
config
,
r'
%%
LISTENING_PORT
%%
'
,
self
.
listeningPort
)
return
config
class
PostgreSQLNode
(
DatabaseNode
):
@staticmethod
def
clone
():
return
PostgreSQLNode
()
def
generateConfigurationRecursively
(
self
):
config
=
str
()
exampleFilePath
=
os
.
path
.
join
(
SALTSTACK_STATE_FOLDER
,
"postgres.example"
)
with
open
(
exampleFilePath
,
'r'
)
as
configFile
:
config
=
configFile
.
read
()
config
=
DatabaseNode
.
generateConfiguration
(
self
,
config
)
self
.
generatedConfig
=
"postgres_
%
s.sls"
%
self
.
machine
.
hostname
with
open
(
os
.
path
.
join
(
SALTSTACK_STATE_FOLDER
,
self
.
generatedConfig
),
'w'
)
as
generatedConfigFile
:
generatedConfigFile
.
write
(
config
)
return
self
class
MySQLNode
(
DatabaseNode
):
@staticmethod
def
clone
():
return
MySQLNode
()
def
generateConfigurationRecursively
(
self
):
config
=
str
()
exampleFilePath
=
os
.
path
.
join
(
SALTSTACK_STATE_FOLDER
,
"mysql.example"
)
with
open
(
exampleFilePath
,
'r'
)
as
configFile
:
config
=
configFile
.
read
()
config
=
DatabaseNode
.
generateConfiguration
(
self
,
config
)
self
.
generatedConfig
=
"mysql_
%
s.sls"
%
self
.
machine
.
hostname
with
open
(
os
.
path
.
join
(
SALTSTACK_STATE_FOLDER
,
self
.
generatedConfig
),
'w'
)
as
generatedConfigFile
:
generatedConfigFile
.
write
(
config
)
return
self
circle/setty/saltstackhelper.py
0 → 100644
View file @
9ab17d5f
import
salt.loader
import
salt.config
import
salt.runner
import
salt.client
SALTSTACK_STATE_FOLDER
=
"/srv/salt"
class
SaltStackHelper
:
def
__init__
(
self
):
self
.
master_opts
=
salt
.
config
.
client_config
(
'/etc/salt/master'
)
self
.
salt_runner
=
salt
.
runner
.
RunnerClient
(
self
.
master_opts
)
self
.
salt_localclient
=
salt
.
client
.
LocalClient
()
self
.
salt_caller
=
salt
.
client
.
Caller
()
def
getAllMinionsGrouped
(
self
):
query_result
=
self
.
salt_runner
.
cmd
(
'manage.status'
,
[]);
return
query_result
def
getAllMinionsUngrouped
(
self
):
query_result
=
self
.
salt_runner
.
cmd
(
'manage.status'
,
[]);
return
query_result
[
"up"
]
+
query_result
[
"down"
]
def
getRunningMinions
(
self
):
return
self
.
salt_runner
.
cmd
(
'manage.up'
,
[]);
def
getUnavailableMinions
(
self
):
return
self
.
salt_runner
.
cmd
(
'manage.down'
,
[]);
def
getMinionBasicHardwareInfo
(
self
,
hostname
):
query_res
=
self
.
salt_localclient
.
cmd
(
hostname
,
'grains.items'
);
if
query_res
:
return
{
'CpuModel'
:
query_res
[
hostname
][
'cpu_model'
],
'CpuArch'
:
query_res
[
hostname
][
'cpuarch'
],
'TotalMemory'
:
query_res
[
hostname
][
'mem_total'
],
'OSDescription'
:
query_res
[
hostname
][
'lsb_distrib_description'
]
}
return
query_res
def
checkMinionExists
(
self
,
hostname
):
query_res
=
self
.
salt_localclient
.
cmd
(
hostname
,
'network.get_hostname'
);
return
query_res
!=
None
def
deploy
(
self
,
hostname
,
configFilePath
):
print
configFilePath
self
.
salt_localclient
.
cmd
(
hostname
,
'state.apply'
,
[
configFilePath
.
split
(
'.'
)[
0
]]
)
\ No newline at end of file
circle/setty/static/setty/apache.jpg
View file @
9ab17d5f
15.9 KB
|
W:
|
H:
15.9 KB
|
W:
|
H:
2-up
Swipe
Onion skin
circle/setty/static/setty/lighttpd.jpg
View file @
9ab17d5f
18.4 KB
|
W:
|
H:
18.4 KB
|
W:
|
H:
2-up
Swipe
Onion skin
circle/setty/static/setty/nginx.jpg
View file @
9ab17d5f
6.02 KB
|
W:
|
H:
6.02 KB
|
W:
|
H:
2-up
Swipe
Onion skin
circle/setty/static/setty/setty.js
View file @
9ab17d5f
/* Settimng up csrf token, touch event and zoom options. */
function
getCookie
(
name
)
{
var
cookieValue
=
null
;
if
(
document
.
cookie
&&
document
.
cookie
!==
''
)
{
var
cookies
=
document
.
cookie
.
split
(
';'
);
for
(
var
i
=
0
;
i
<
cookies
.
length
;
i
++
)
{
var
cookie
=
jQuery
.
trim
(
cookies
[
i
]);
if
(
cookie
.
substring
(
0
,
name
.
length
+
1
)
==
(
name
+
'='
))
{
cookieValue
=
decodeURIComponent
(
cookie
.
substring
(
name
.
length
+
1
));
break
;
}
}
}
return
cookieValue
;
}
function
csrfSafeMethod
(
method
)
{
return
(
/^
(
GET|HEAD|OPTIONS|TRACE
)
$/
.
test
(
method
));
}
var
csrftoken
=
getCookie
(
'csrftoken'
);
$
.
ajaxSetup
({
beforeSend
:
function
(
xhr
,
settings
)
{
if
(
!
csrfSafeMethod
(
settings
.
type
)
&&
!
this
.
crossDomain
)
{
xhr
.
setRequestHeader
(
"X-CSRFToken"
,
csrftoken
);
}
}
});
/* Setty implementation starts here. */
jsPlumb
.
ready
(
function
()
{
var
jsPlumbInstance
=
jsPlumb
.
getInstance
({
DragOptions
:
{
zIndex
:
2000
},
EndpointHoverStyle
:
{
fillStyle
:
"green"
},
HoverPaintStyle
:
{
strokeStyle
:
"green"
},
Container
:
"dropContainer"
});
var
jsPlumbEndpoint
=
{
endpoint
:
[
"Dot"
,
{
radius
:
10
}],
paintStyle
:
{
fillStyle
:
"#9932cc"
},
isSource
:
true
,
isTarget
:
true
,
deleteEndpointsOnDetach
:
false
,
zIndex
:
20
,
connectorStyle
:
{
strokeStyle
:
"#9932cc"
,
lineWidth
:
8
},
connector
:
[
"Bezier"
,
{
curviness
:
180
}],
maxConnections
:
1
,
dropOptions
:
{
tolerance
:
"fit"
}
};
var
elementConnections
=
[];
var
elementIndex
=
0
;
var
dragContainerScroll
=
0
;
var
workspaceWidth
=
$
(
"#dropContainer"
).
width
();
var
workspaceHeight
=
$
(
"#dropContainer"
).
height
();
var
stackIndexer
=
0
;
var
stackSize
=
0
;
var
objectStack
=
[];
var
undoStack
=
[];
var
redoStack
=
[];
var
clickEvent
=
0
;
var
nextStepConstraint
=
0
;
/* Functions. */
setServiceStatus
=
function
(
status
)
{
if
(
status
==
"unsaved"
)
{
$
(
"#serviceStatus"
).
text
(
gettext
(
"Unsaved"
));
}
else
{
$
(
"#serviceStatus"
).
empty
();
}
};
addInfo
=
function
(
title
,
info
,
object
,
type
)
{
mainDiv
=
$
(
"<div>"
,
{
class
:
"row"
,
html
:
$
(
"<div>"
,
{
class
:
"col-xs-12 text-center"
,
html
:
row
=
$
(
"<h4>"
,
{
html
:
title
})
})
}).
add
(
$
(
"<div>"
,
{
class
:
"row"
,
style
:
"margin-top: 16px"
,
html
:
$
(
"<div>"
,
{
class
:
"col-xs-12 text-center"
,
html
:
row
=
$
(
"<textarea>"
,
{
class
:
"form-control"
,
style
:
"text-align: justify;"
,
rows
:
"18"
,
id
:
"infoInput"
,
disabled
:
!
type
,
text
:
info
})
})
}));
controlDiv
=
$
(
"<div>"
,
{
class
:
"row"
,
style
:
"margin-top: 16px"
,
html
:
$
(
"<div>"
,
{
class
:
"col-xs-3 text-center"
,
html
:
$
(
"<button>"
,
{
class
:
"btn btn-success btn-block"
,
id
:
"addEndpoint"
,
html
:
gettext
(
"Add endpoint"
)
})
}).
add
(
$
(
"<div>"
,
{
class
:
"col-xs-3 text-center"
,
html
:
$
(
"<button>"
,
{
class
:
"btn btn-danger btn-block"
,
id
:
"removeEndpoint"
,
html
:
gettext
(
"Delete endpoint"
)
})
})).
add
(
$
(
"<div>"
,
{
class
:
"col-xs-6 text-center"
,
html
:
$
(
"<button>"
,
{
class
:
"btn btn-info btn-block"
,
id
:
"removeElementFromWorkspace"
,
html
:
gettext
(
"Remove from workspace"
)
})
}))
});
addElementDiv
=
$
(
"<div>"
,
{
class
:
"row"
,
style
:
"margin-top: 16px"
,
html
:
$
(
"<div>"
,
{
class
:
"col-xs-12 text-center"
,
html
:
row
=
$
(
"<button>"
,
{
class
:
"btn btn-success"
,
id
:
"addElementToWorkspace"
,
html
:
gettext
(
"Add to workspace"
)
})
})
});
$
(
"#informationContainer"
).
html
(
type
?
mainDiv
.
add
(
controlDiv
):
mainDiv
.
add
(
addElementDiv
));
$
(
"#changeInformationDialog"
).
modal
(
'show'
);
sharedObject
=
object
;
};
updateConnections
=
function
(
connection
,
remove
)
{
if
(
!
remove
)
{
elementConnections
.
push
(
connection
);
}
else
{
index
=
-
1
;
for
(
var
i
=
0
;
i
<
elementConnections
.
length
;
i
++
)
{
if
(
elementConnections
[
i
]
==
connection
)
{
index
=
i
;
break
;
}
}
if
(
index
!=
-
1
)
{
elementConnections
.
splice
(
index
,
1
);
}
}
setServiceStatus
(
"unsaved"
);
};
checkDuplicateConnection
=
function
(
connection
)
{
for
(
var
i
=
0
;
i
<
elementConnections
.
length
;
i
++
)
{
if
(((
elementConnections
[
i
].
targetId
==
connection
.
targetId
&&
elementConnections
[
i
].
sourceId
==
connection
.
sourceId
)
||
(
elementConnections
[
i
].
targetId
==
connection
.
sourceId
&&
elementConnections
[
i
].
sourceId
==
connection
.
targetId
))
&&
elementConnections
[
i
]
!=
connection
)
{
addMessage
(
gettext
(
"Twofold connection is forbidden."
),
"danger"
);
return
false
;
}
}
return
true
;
};
checkCompatibility
=
function
(
sourceId
,
targetId
)
{
validTargets
=
$
(
"#"
+
sourceId
).
attr
(
"type"
).
split
(
','
);
if
(
jQuery
.
inArray
(
targetId
.
split
(
'_'
)[
1
],
validTargets
)
==
-
1
)
{
addMessage
(
gettext
(
"Connecting incompatible elements is forbidden."
),
"danger"
);
return
false
;
}
return
true
;
};
checkSourceTargetEquality
=
function
(
connection
)
{
if
(
connection
.
targetId
==
connection
.
sourceId
)
{
addMessage
(
gettext
(
"Connecting element to itself is forbidden."
),
"danger"
);
return
false
;
}
return
true
;
};
getAnchorCoordinate
=
function
(
rate
)
{
x
=
Math
.
cos
(
2.0
*
Math
.
PI
*
rate
)
/
2
;
y
=
Math
.
sin
(
2.0
*
Math
.
PI
*
rate
)
/
2
;
dx
=
0
;
dy
=
0
;
if
(
rate
<
0.125
)
{
x
=
0.5
;
dx
=
1
;
}
else
if
(
rate
>
0.125
&&
rate
<
0.375
)
{
y
=
-
0.5
;
dy
=
-
1
;
}
else
if
(
rate
>
0.375
&&
rate
<
0.625
)
{
x
=
-
0.5
;
dx
=
-
1
;
}
else
if
(
rate
>
0.625
&&
rate
<
0.875
)
{
y
=
0.5
;
dy
=
1
;
}
else
if
(
rate
>
0.875
)
{
x
=
-
0.5
;
dx
=
-
1
;
}
else
{
x
=
Math
.
sqrt
(
2
)
*
Math
.
cos
(
2
*
Math
.
PI
*
rate
)
/
2
;
y
=
Math
.
sqrt
(
2
)
*
Math
.
sin
(
2
*
Math
.
PI
*
rate
)
/
2
;
dx
=
Math
.
round
(
2
*
x
);
}
return
[
y
+
0.5
,
-
x
+
0.5
,
dy
,
-
dx
];
};
isConnected
=
function
(
anchorId
)
{
returnValue
=
false
;
$
.
each
(
elementConnections
,
function
(
index
)
{
if
(
elementConnections
[
index
].
endpoints
[
0
].
getUuid
()
==
anchorId
||
elementConnections
[
index
].
endpoints
[
1
].
getUuid
()
==
anchorId
)
{
returnValue
=
true
;
return
;
}
});
return
returnValue
;
};
elementIsConnected
=
function
(
element
)
{
anchors
=
element
.
attr
(
"anchors"
);
id
=
element
.
attr
(
"id"
);
for
(
i
=
0
;
i
<
anchors
;
i
++
)
{
if
(
isConnected
(
i
+
"_"
+
id
))
{
return
true
;
}
}
return
false
;
};
getConnectionparamAndAnchor
=
function
(
anchorId
)
{
parameters
=
""
;
otherAnchor
=
""
;
$
.
each
(
elementConnections
,
function
(
index
)
{
if
(
elementConnections
[
index
].
endpoints
[
0
].
getUuid
()
==
anchorId
)
{
parameters
=
elementConnections
[
index
].
parameters
;
otherAnchor
=
elementConnections
[
index
].
endpoints
[
1
].
getUuid
();
return
;
}
if
(
elementConnections
[
index
].
endpoints
[
1
].
getUuid
()
==
anchorId
)
{
parameters
=
elementConnections
[
index
].
parameters
;
otherAnchor
=
elementConnections
[
index
].
endpoints
[
0
].
getUuid
();
return
;
}
});
return
[
otherAnchor
,
parameters
];
};
addEndpoint
=
function
(
element
)
{
anchors
=
element
.
attr
(
"anchors"
);
if
(
anchors
==
8
)
return
1
;
anchors
++
;
jsPlumbInstance
.
addEndpoint
(
document
.
getElementById
(
element
.
attr
(
"id"
)),
{
uuid
:
(
anchors
-
1
)
+
"_"
+
element
.
attr
(
"id"
)
},
jsPlumbEndpoint
);
for
(
i
=
0
;
i
<
anchors
;
i
++
)
{
jsPlumbInstance
.
getEndpoint
(
i
+
"_"
+
element
.
attr
(
"id"
)).
setAnchor
(
getAnchorCoordinate
(
i
/
(
anchors
)));
}
element
.
attr
(
"anchors"
,
anchors
);
jsPlumbInstance
.
repaintEverything
();
return
0
;
};
removeEndoint
=
function
(
element
)
{
anchors
=
element
.
attr
(
"anchors"
);
if
(
anchors
==
4
)
return
1
;
i
=
--
anchors
;
while
(
isConnected
(
i
+
"_"
+
element
.
attr
(
"id"
))
&&
i
>=
0
)
i
--
;
if
(
i
==
-
1
)
{
addMessage
(
gettext
(
"Removing anchors is obstructed."
),
"danger"
);
return
;
}
else
if
(
i
==
anchors
)
{
jsPlumbInstance
.
deleteEndpoint
(
jsPlumbInstance
.
getEndpoint
(
anchors
+
"_"
+
element
.
attr
(
"id"
)));
}
else
{
newId
=
i
+
"_"
+
element
.
attr
(
"id"
);
oldId
=
anchors
+
"_"
+
element
.
attr
(
"id"
);
data
=
getConnectionparamAndAnchor
(
oldId
);
data
.
splice
(
0
,
0
,
newId
);
jsPlumbInstance
.
deleteEndpoint
(
jsPlumbInstance
.
getEndpoint
(
oldId
));
connectEndpoints
(
data
);
}
for
(
i
=
0
;
i
<
anchors
;
i
++
)
jsPlumbInstance
.
getEndpoint
(
i
+
"_"
+
element
.
attr
(
"id"
)).
setAnchor
(
getAnchorCoordinate
(
i
/
(
anchors
)));
element
.
attr
(
"anchors"
,
anchors
);
jsPlumbInstance
.
repaintEverything
();
return
0
;
};
connectEndpoints
=
function
(
data
)
{
connectionObject
=
jsPlumbInstance
.
connect
({
uuids
:
[
data
[
0
],
data
[
1
]]
});
connectionObject
.
parameters
=
data
[
2
];
setServiceStatus
(
"unsaved"
);
};
disconnectEndpoints
=
function
(
data
)
{
for
(
var
i
=
0
;
i
<
elementConnections
.
length
;
i
++
)
{
if
(
elementConnections
[
i
].
endpoints
[
0
].
getUuid
()
==
data
[
0
]
&&
elementConnections
[
i
].
endpoints
[
1
].
getUuid
()
==
data
[
1
])
{
jsPlumbInstance
.
detach
(
elementConnections
[
i
]);
return
;
}
}
return
;
};
addElement
=
function
(
idOrInstance
,
newId
,
newPositionY
,
endpoints
,
parameters
,
newPositionX
)
{
newInstance
=
""
;
if
(
typeof
idOrInstance
!=
"string"
)
{
newInstance
=
idOrInstance
;
endpoints
=
newInstance
.
attr
(
"anchors"
);
newInstance
.
attr
(
"anchors"
,
0
);
}
else
{
newInstance
=
$
(
'#'
+
idOrInstance
)
.
clone
()
.
prop
(
"id"
,
newId
)
.
prop
(
"title"
,
"Right click to delete"
)
.
removeClass
()
.
addClass
(
"element"
)
.
attr
(
"anchors"
,
0
)
.
attr
(
"parameters"
,
parameters
)
.
css
(
"top"
,
newPositionY
)
.
css
(
"left"
,
newPositionX
);
}
$
(
"#dropContainer"
).
append
(
newInstance
);
for
(
i
=
0
;
i
<=
endpoints
;
i
++
)
{
addEndpoint
(
newInstance
);
}
jsPlumbInstance
.
draggable
(
jsPlumb
.
getSelector
(
".element"
),
{
containment
:
$
(
"#dropContainer"
)
});
setServiceStatus
(
"unsaved"
);
jsPlumbInstance
.
repaintEverything
();
return
newInstance
;
};
removeElement
=
function
(
object
)
{
jsPlumbInstance
.
detachAllConnections
(
object
);
jsPlumbInstance
.
remove
(
object
.
attr
(
"id"
));
};
/* Registering events using JsPlumb. */
jsPlumbInstance
.
bind
(
"connection"
,
function
(
info
)
{
updateConnections
(
info
.
connection
);
info
.
connection
.
parameters
=
""
;
if
(
clickEvent
===
0
)
{
undoStack
.
splice
(
stackIndexer
,
0
,
disconnectEndpoints
);
redoStack
.
splice
(
stackIndexer
,
0
,
connectEndpoints
);
connectionArray
=
[];
connectionArray
.
push
(
info
.
connection
.
endpoints
[
0
].
getUuid
(),
info
.
connection
.
endpoints
[
1
].
getUuid
(),
info
.
connection
.
parameters
);
objectStack
.
splice
(
stackIndexer
,
0
,
connectionArray
);
stackIndexer
++
;
stackSize
++
;
}
});
jsPlumbInstance
.
bind
(
"beforeDrop"
,
function
(
info
)
{
return
checkDuplicateConnection
(
info
.
connection
)
&&
checkSourceTargetEquality
(
info
.
connection
)
&&
checkCompatibility
(
info
.
connection
.
sourceId
,
info
.
connection
.
targetId
);
});
jsPlumbInstance
.
bind
(
"connectionDetached"
,
function
(
info
)
{
updateConnections
(
info
.
connection
,
true
);
if
(
clickEvent
===
0
)
{
undoStack
.
splice
(
stackIndexer
,
0
,
connectEndpoints
);
redoStack
.
splice
(
stackIndexer
,
0
,
disconnectEndpoints
);
connectionArray
=
[];
connectionArray
.
push
(
info
.
connection
.
endpoints
[
0
].
getUuid
(),
info
.
connection
.
endpoints
[
1
].
getUuid
(),
info
.
connection
.
parameters
);
objectStack
.
splice
(
stackIndexer
,
0
,
connectionArray
);
stackIndexer
++
;
stackSize
++
;
}
});
jsPlumbInstance
.
bind
(
"connectionMoved"
,
function
(
info
)
{
updateConnections
(
info
.
connection
,
true
);
});
jsPlumbInstance
.
bind
(
"contextmenu"
,
function
(
info
)
{
jsPlumbInstance
.
detach
(
info
);
});
jsPlumbInstance
.
draggable
(
jsPlumb
.
getSelector
(
".element"
),
{
containment
:
$
(
"#dropContainer"
)
});
/* Registering events using JQuery. */
$
(
'body'
).
on
(
'click'
,
'.elementTemplate'
,
function
()
{
addElement
(
$
(
this
).
attr
(
"id"
),
(
++
elementIndex
)
+
"_"
+
$
(
this
).
attr
(
"id"
),
(
elementIndex
%
21
)
*
30
,
4
,
""
,
(
elementIndex
%
21
)
*
30
);
undoStack
.
splice
(
stackIndexer
,
0
,
removeElement
);
redoStack
.
splice
(
stackIndexer
,
0
,
addElement
);
objectStack
.
splice
(
stackIndexer
,
0
,
newInstance
);
stackSize
++
;
stackIndexer
++
;
});
$
(
'body'
).
on
(
'dblclick'
,
'.element'
,
function
()
{
element
=
$
(
this
);
element
.
addClass
(
"elementSelected"
);
addInfo
(
element
.
attr
(
"alt"
),
element
.
attr
(
"parameters"
),
element
,
1
);
$
(
document
).
scrollTop
(
0
);
});
$
(
'body'
).
on
(
'contextmenu'
,
'.element'
,
function
(
event
)
{
setServiceStatus
(
"unsaved"
);
removeElement
(
$
(
this
));
undoStack
.
splice
(
stackIndexer
,
0
,
addElement
);
redoStack
.
splice
(
stackIndexer
,
0
,
removeElement
);
objectStack
.
splice
(
stackIndexer
,
0
,
$
(
this
));
nextStepConstraint
=
0
;
stackSize
++
;
stackIndexer
++
;
});
$
(
'body'
).
on
(
'keyup'
,
'#infoInput'
,
function
()
{
setServiceStatus
(
"unsaved"
);
newParams
=
$
(
"#infoInput"
).
val
();
sharedObject
.
attr
(
"parameters"
,
newParams
);
});
$
(
'body'
).
on
(
'click'
,
'#addEndpoint'
,
function
()
{
setServiceStatus
(
"unsaved"
);
if
(
addEndpoint
(
sharedObject
))
return
;
undoStack
.
splice
(
stackIndexer
,
0
,
removeEndoint
);
redoStack
.
splice
(
stackIndexer
,
0
,
addEndpoint
);
objectStack
.
splice
(
stackIndexer
,
0
,
sharedObject
);
stackIndexer
++
;
stackSize
++
;
});
$
(
'body'
).
on
(
'click'
,
'#removeEndpoint'
,
function
()
{
setServiceStatus
(
"unsaved"
);
if
(
removeEndoint
(
sharedObject
))
return
;
undoStack
.
splice
(
stackIndexer
,
0
,
addEndpoint
);
redoStack
.
splice
(
stackIndexer
,
0
,
removeEndoint
);
objectStack
.
splice
(
stackIndexer
,
0
,
sharedObject
);
stackIndexer
++
;
stackSize
++
;
});
$
(
'body'
).
on
(
'click'
,
'#removeElementFromWorkspace'
,
function
()
{
setServiceStatus
(
"unsaved"
);
removeElement
(
sharedObject
);
undoStack
.
splice
(
stackIndexer
,
0
,
addElement
);
redoStack
.
splice
(
stackIndexer
,
0
,
removeElement
);
objectStack
.
splice
(
stackIndexer
,
0
,
sharedObject
);
stackSize
++
;
stackIndexer
++
;
$
(
"#changeInformationDialog"
).
modal
(
'hide'
);
});
$
(
'body'
).
on
(
'click'
,
'#removeConnection'
,
function
()
{
jsPlumbInstance
.
detach
(
sharedObject
);
});
$
(
'body'
).
on
(
'click'
,
'#addElementToWorkspace'
,
function
()
{
newInstance
=
addElement
(
sharedObject
.
attr
(
"id"
),
(
++
elementIndex
)
+
"_"
+
sharedObject
.
attr
(
"id"
),
(
elementIndex
%
21
)
*
30
,
4
,
""
,
(
elementIndex
%
21
)
*
30
);
undoStack
.
splice
(
stackIndexer
,
0
,
removeElement
);
redoStack
.
splice
(
stackIndexer
,
0
,
addElement
);
objectStack
.
splice
(
stackIndexer
,
0
,
newInstance
);
stackSize
++
;
stackIndexer
++
;
});
$
(
'body'
).
on
(
'click'
,
'#clearService'
,
function
()
{
//Todo
setServiceStatus
(
"unsaved"
);
elementIndex
=
0
;
});
$
(
'body'
).
on
(
'click'
,
'#undoMovement'
,
function
()
{
if
(
stackIndexer
<
1
)
return
;
stackIndexer
--
;
clickEvent
=
1
;
object
=
objectStack
[
stackIndexer
];
undoStack
[
stackIndexer
](
object
);
clickEvent
=
0
;
});
$
(
'body'
).
on
(
'click'
,
'#redoMovement'
,
function
()
{
if
(
stackIndexer
>=
stackSize
)
return
;
clickEvent
=
1
;
object
=
objectStack
[
stackIndexer
];
redoStack
[
stackIndexer
](
object
);
stackIndexer
++
;
clickEvent
=
0
;
});
$
(
'body'
).
on
(
'click'
,
'#addMachineDialog'
,
function
()
{
// Here comes the ajax post of getting machines
});
$
(
'body'
).
on
(
'click'
,
'.elementTemplateInfo'
,
function
()
{
id
=
$
(
this
).
attr
(
"element"
);
addInfo
(
$
(
"#"
+
id
).
attr
(
"alt"
),
$
(
"#"
+
id
).
attr
(
"desc"
),
$
(
"#"
+
id
),
0
);
});
$
(
'body'
).
on
(
'click'
,
'#serviceName'
,
function
()
{
$
(
'#serviceName'
).
hide
();
$
(
"#serviceNameEdit"
).
css
(
"display"
,
"inline"
).
val
(
$
(
this
).
text
()).
select
();
$
(
"#serviceNameSave"
).
css
(
"display"
,
"inline"
);
setServiceStatus
(
"unsaved"
);
});
$
(
'body'
).
on
(
'click'
,
'#serviceNameSave'
,
function
()
{
$
(
'#serviceNameEdit'
).
hide
();
$
(
this
).
hide
();
$
(
"#serviceName"
).
show
().
text
(
$
(
'#serviceNameEdit'
).
val
());
});
$
(
'body'
).
on
(
'click'
,
'#dragContainerScrollUp'
,
function
()
{
scrollContainer
(
-
1
);
});
$
(
'body'
).
on
(
'click'
,
'#dragContainerScrollDown'
,
function
()
{
scrollContainer
(
1
);
});
$
(
'body'
).
on
(
'hide.bs.modal'
,
'#changeInformationDialog'
,
function
()
{
$
(
'.element'
).
removeClass
(
'elementSelected'
);
});
$
(
'body'
).
on
(
'keyup'
,
'#searchElementTemplate'
,
function
()
{
$
(
".elementTemplate"
).
each
(
function
()
{
$
(
this
).
parent
().
parent
().
hide
();
if
(
$
(
this
).
attr
(
"alt"
).
toLowerCase
().
indexOf
(
$
(
"#searchElementTemplateInput"
).
val
().
toLowerCase
())
>=
0
)
$
(
this
).
parent
().
parent
().
show
();
});
});
$
(
'body'
).
on
(
'mousewheel DOMMouseScroll onmousewheel'
,
function
(
event
)
{
var
e
=
window
.
event
||
event
;
var
delta
=
Math
.
max
(
-
1
,
Math
.
min
(
1
,
(
e
.
wheelDelta
||
-
e
.
detail
)));
$
(
'body'
).
addClass
(
"noScroll"
);
dragContainerScroll
-=
delta
;
if
(
dragContainerScroll
==
$
(
".elementTemplate"
).
length
-
2
)
dragContainerScroll
--
;
if
(
dragContainerScroll
==
-
1
)
dragContainerScroll
++
;
$
(
"#dragContainer"
).
scrollTop
(
dragContainerScroll
*
$
(
"#elementTemplatePanel"
).
height
()
);
$
(
'body'
).
removeClass
(
"noScroll"
);
});
$
(
document
).
on
(
'keydown'
,
function
(
e
)
{
var
eventObject
=
window
.
event
?
event
:
e
;
// Undo (CTRL + Z)
if
(
eventObject
.
keyCode
==
90
&&
eventObject
.
ctrlKey
)
{
eventObject
.
preventDefault
();
$
(
'#undoMovement'
).
click
();
}
// Redo (CTRL + Y)
if
(
eventObject
.
keyCode
==
89
&&
eventObject
.
ctrlKey
)
{
eventObject
.
preventDefault
();
$
(
'#redoMovement'
).
click
();
}
// Add element (CTRL + A)
if
(
eventObject
.
keyCode
==
65
&&
eventObject
.
ctrlKey
)
{
eventObject
.
preventDefault
();
$
(
'#showAddElementDialog'
).
click
();
}
// Clean (CTRL + C)
if
(
eventObject
.
keyCode
==
67
&&
eventObject
.
ctrlKey
)
{
eventObject
.
preventDefault
();
$
(
'#clearService'
).
click
();
}
// Save (CTRL + S)
if
(
eventObject
.
keyCode
==
83
&&
eventObject
.
ctrlKey
)
{
eventObject
.
preventDefault
();
$
(
'#saveService'
).
click
();
}
// Delete (CTRL + D)
if
(
eventObject
.
keyCode
==
68
&&
eventObject
.
ctrlKey
)
{
eventObject
.
preventDefault
();
$
(
'#deleteService'
).
click
();
}
// Close dialog (ESC)
if
(
eventObject
.
keyCode
==
27
)
{
eventObject
.
preventDefault
();
$
(
"#changeInformationDialog"
).
modal
(
'hide'
);
$
(
"#addElementDialog"
).
modal
(
'hide'
);
}
});
$
(
window
).
on
(
'resize'
,
function
()
{
$
(
".element"
).
each
(
function
()
{
rate
=
(
$
(
this
).
position
().
left
)
/
workspaceWidth
;
left
=
rate
*
(
$
(
"#dropContainer"
).
width
());
$
(
this
).
css
(
"left"
,
left
);
});
workspaceWidth
=
$
(
"#dropContainer"
).
width
();
jsPlumbInstance
.
repaintEverything
();
});
/* Registering events concerning persistence. */
$
(
'body'
).
on
(
'click'
,
'#saveService'
,
function
()
{
serviceName
=
$
(
"#serviceName"
).
text
();
connectionSet
=
[];
instanceSet
=
[];
$
.
each
(
elementConnections
,
function
(
index
)
{
connectionSet
.
push
({
"sourceId"
:
elementConnections
[
index
].
sourceId
,
"sourceEndpoint"
:
elementConnections
[
index
].
endpoints
[
0
].
getUuid
(),
"targetId"
:
elementConnections
[
index
].
targetId
,
"targetEndpoint"
:
elementConnections
[
index
].
endpoints
[
1
].
getUuid
(),
"parameters"
:
elementConnections
[
index
].
parameters
});
});
$
.
each
(
$
(
".element"
),
function
()
{
instanceSet
.
push
({
"displayId"
:
$
(
this
).
prop
(
"id"
),
"positionLeft"
:
$
(
this
).
position
().
left
/
workspaceWidth
,
"positionTop"
:
$
(
this
).
position
().
top
/
workspaceHeight
,
"anchorNumber"
:
$
(
this
).
attr
(
"anchors"
),
"parameters"
:
$
(
this
).
attr
(
"parameters"
)
});
});
$
.
post
(
""
,
{
event
:
"saveService"
,
data
:
JSON
.
stringify
({
"serviceName"
:
serviceName
,
"elementConnections"
:
connectionSet
,
"elements"
:
instanceSet
})
},
function
(
result
)
{
addMessage
(
result
.
serviceName
+
gettext
(
" saved successfully."
),
"success"
);
setServiceStatus
(
"saved"
);
});
});
$
(
document
).
ready
(
function
()
{
if
(
!
$
(
"#dropContainer"
).
length
)
return
;
// Protection for not posting sites that differ from setty sites.
$
.
post
(
""
,
{
event
:
"loadService"
},
function
(
result
)
{
$
(
"#serviceName"
).
text
(
result
.
serviceName
);
$
.
each
(
result
.
elements
,
function
(
i
,
element
)
{
addElement
(
element
.
displayId
.
split
(
'_'
)[
1
],
element
.
displayId
,
(
element
.
positionTop
*
workspaceHeight
)
+
"px"
,
element
.
anchorNumber
,
element
.
parameters
,
(
element
.
positionLeft
*
workspaceWidth
)
+
"px"
);
if
(
elementIndex
<
element
.
displayId
.
split
(
'_'
)[
0
])
elementIndex
=
element
.
displayId
.
split
(
'_'
)[
0
];
elementIndex
++
;
});
clickEvent
=
1
;
$
.
each
(
result
.
elementConnections
,
function
(
i
,
connection
)
{
connectEndpoints
([
connection
.
sourceEndpoint
,
connection
.
targetEndpoint
,
connection
.
parameters
]);
});
clickEvent
=
0
;
setServiceStatus
(
"saved"
);
});
});
});
/* Settimng up csrf token, touch event and zoom options. */
function
getCookie
(
name
)
{
var
cookieValue
=
null
;
if
(
document
.
cookie
&&
document
.
cookie
!==
''
)
{
var
cookies
=
document
.
cookie
.
split
(
';'
);
for
(
var
i
=
0
;
i
<
cookies
.
length
;
i
++
)
{
var
cookie
=
jQuery
.
trim
(
cookies
[
i
]);
if
(
cookie
.
substring
(
0
,
name
.
length
+
1
)
==
(
name
+
'='
))
{
cookieValue
=
decodeURIComponent
(
cookie
.
substring
(
name
.
length
+
1
));
break
;
}
}
}
return
cookieValue
;
}
function
csrfSafeMethod
(
method
)
{
return
(
/^
(
GET|HEAD|OPTIONS|TRACE
)
$/
.
test
(
method
));
}
var
csrftoken
=
getCookie
(
'csrftoken'
);
$
.
ajaxSetup
({
beforeSend
:
function
(
xhr
,
settings
)
{
if
(
!
csrfSafeMethod
(
settings
.
type
)
&&
!
this
.
crossDomain
)
{
xhr
.
setRequestHeader
(
"X-CSRFToken"
,
csrftoken
);
}
}
});
/* Setty implementation starts here. */
jsPlumb
.
ready
(
function
()
{
var
jsPlumbInstance
=
jsPlumb
.
getInstance
({
DragOptions
:
{
zIndex
:
2000
},
EndpointHoverStyle
:
{
fillStyle
:
"green"
},
HoverPaintStyle
:
{
strokeStyle
:
"green"
},
Container
:
"dropContainer"
});
var
jsPlumbEndpoint
=
{
endpoint
:
[
"Dot"
,
{
radius
:
10
}],
paintStyle
:
{
fillStyle
:
"#9932cc"
},
isSource
:
true
,
isTarget
:
true
,
deleteEndpointsOnDetach
:
false
,
zIndex
:
20
,
connectorStyle
:
{
strokeStyle
:
"#9932cc"
,
lineWidth
:
8
},
connector
:
[
"Bezier"
,
{
curviness
:
180
}],
maxConnections
:
1
,
dropOptions
:
{
tolerance
:
"fit"
}
};
var
elementConnections
=
[];
var
elementIndex
=
0
;
var
dragContainerScroll
=
0
;
var
workspaceWidth
=
$
(
"#dropContainer"
).
width
();
var
workspaceHeight
=
$
(
"#dropContainer"
).
height
();
var
stackIndexer
=
0
;
var
stackSize
=
0
;
var
objectStack
=
[];
var
undoStack
=
[];
var
redoStack
=
[];
var
clickEvent
=
0
;
var
nextStepConstraint
=
0
;
/* Functions. */
setServiceStatus
=
function
(
status
)
{
if
(
status
==
"unsaved"
)
{
$
(
"#serviceStatus"
).
text
(
"Unsaved"
);
}
else
{
$
(
"#serviceStatus"
).
empty
();
}
};
addInfo
=
function
(
title
,
info
,
type
,
object
)
{
id
=
object
.
attr
(
"id"
).
split
(
"_"
)[
1
];
$
.
post
(
""
,
{
event
:
"getInformation"
,
data
:
JSON
.
stringify
({
"elementTemplateId"
:
object
.
attr
(
"id"
).
split
(
"_"
)[
1
],
"hostname"
:
object
.
attr
(
"hostname"
)})
},
function
(
result
)
{
alert
(
result
);
});
/*
$("#informationContainer").empty();
switch(type){
case "connection":
div =
'<div class="row">' +
'<div class="col-xs-12 text-center">' +
'<h4>' + title + '</h4>' +
'</div>' +
'</div> ' +
'<div class="row">' +
'<div class="col-xs-12">' +
'<textarea class="form-control" rows="28" id="infoInput" placeholder="Config data"></textarea>' +
'</div>' +
'</div> ' +
'<div class="row">' +
'<div class="col-xs-12 text-center">' +
'<button id="removeConnection" class="btn btn-info">Remove connection</button>' +
'</div>' +
'</div>';
break;
case "element":
div =
'<div class="row">' +
'<div class="col-xs-12 text-center">' +
'<h4>' + title + '</h4>' +
'</div>' +
'</div> ' +
'<div class="row">' +
'<div class="col-xs-12">' +
'<textarea class="form-control" rows="24" id="infoInput" placeholder="Config data"></textarea>' +
'</div>' +
'</div> ' +
'<div class="row text-center">' +
'<label>Endpoints</label>' +
'</div>' +
'<div class="row">' +
'<div class="col-xs-6 text-center">' +
'<button id="addEndpoint" class="btn btn-success"><i class="fa fa-plus"></i></button>' +
'</div>' +
'<div class="col-xs-6 text-center">' +
'<button id="removeEndpoint" class="btn btn-danger"><i class="fa fa-minus"></i></button>' +
'</div>' +
'</div> ' +
'<div class="row">' +
'<div class="col-xs-12 text-center">' +
'<button id="removeElementFromWorkspace" class="btn btn-info">Remove from workspace</button>' +
'</div>' +
'</div>';
break;
case "elementTemplate":
div =
'<div class="row">' +
'<div class="col-xs-12 text-center">' +
'<h4>' + title + '</h4>' +
'</div>' +
'</div> ' +
'<div class="row">' +
'<div class="col-xs-12">' +
'<textarea class="form-control" rows="28" id="infoInput" placeholder="Config data"></textarea>' +
'</div>' +
'</div> ' +
'<div class="row">' +
'<div class="col-xs-12 text-center">' +
'<button id="addElementToWorkspace" class="btn btn-success">Add to workspace</button>' +
'</div>' +
'</div>';
break;
}*/
// Here comes the ajax getInformation post.
// elementtemplateid vagy hostname
div
=
0
;
$
(
"#informationContainer"
).
append
(
div
);
$
(
"#infoInput"
).
val
(
info
);
$
(
"#changeInformationDialog"
).
modal
(
'show'
);
sharedObject
=
object
;
};
updateConnections
=
function
(
connection
,
remove
)
{
if
(
!
remove
)
{
elementConnections
.
push
(
connection
);
}
else
{
index
=
-
1
;
for
(
var
i
=
0
;
i
<
elementConnections
.
length
;
i
++
)
{
if
(
elementConnections
[
i
]
==
connection
)
{
index
=
i
;
break
;
}
}
if
(
index
!=
-
1
)
{
elementConnections
.
splice
(
index
,
1
);
}
}
setServiceStatus
(
"unsaved"
);
};
checkDuplicateConnection
=
function
(
connection
)
{
for
(
var
i
=
0
;
i
<
elementConnections
.
length
;
i
++
)
{
if
(((
elementConnections
[
i
].
targetId
==
connection
.
targetId
&&
elementConnections
[
i
].
sourceId
==
connection
.
sourceId
)
||
(
elementConnections
[
i
].
targetId
==
connection
.
sourceId
&&
elementConnections
[
i
].
sourceId
==
connection
.
targetId
))
&&
elementConnections
[
i
]
!=
connection
)
{
addMessage
(
gettext
(
"Twofold connection is forbidden."
),
"danger"
);
return
false
;
}
}
return
true
;
};
checkCompatibility
=
function
(
sourceId
,
targetId
)
{
validTargets
=
$
(
"#"
+
sourceId
).
attr
(
"type"
).
split
(
','
);
if
(
jQuery
.
inArray
(
targetId
.
split
(
'_'
)[
1
],
validTargets
)
==
-
1
)
{
addMessage
(
gettext
(
"Connecting incompatible elements is forbidden."
),
"danger"
);
return
false
;
}
return
true
;
};
checkSourceTargetEquality
=
function
(
connection
)
{
if
(
connection
.
targetId
==
connection
.
sourceId
)
{
addMessage
(
gettext
(
"Connecting element to itself is forbidden."
),
"danger"
);
return
false
;
}
return
true
;
};
getAnchorCoordinate
=
function
(
rate
)
{
x
=
Math
.
cos
(
2.0
*
Math
.
PI
*
rate
)
/
2
;
y
=
Math
.
sin
(
2.0
*
Math
.
PI
*
rate
)
/
2
;
dx
=
0
;
dy
=
0
;
if
(
rate
<
0.125
)
{
x
=
0.5
;
dx
=
1
;
}
else
if
(
rate
>
0.125
&&
rate
<
0.375
)
{
y
=
-
0.5
;
dy
=
-
1
;
}
else
if
(
rate
>
0.375
&&
rate
<
0.625
)
{
x
=
-
0.5
;
dx
=
-
1
;
}
else
if
(
rate
>
0.625
&&
rate
<
0.875
)
{
y
=
0.5
;
dy
=
1
;
}
else
if
(
rate
>
0.875
)
{
x
=
-
0.5
;
dx
=
-
1
;
}
else
{
x
=
Math
.
sqrt
(
2
)
*
Math
.
cos
(
2
*
Math
.
PI
*
rate
)
/
2
;
y
=
Math
.
sqrt
(
2
)
*
Math
.
sin
(
2
*
Math
.
PI
*
rate
)
/
2
;
dx
=
Math
.
round
(
2
*
x
);
}
return
[
y
+
0.5
,
-
x
+
0.5
,
dy
,
-
dx
];
};
isConnected
=
function
(
anchorId
)
{
returnValue
=
false
;
$
.
each
(
elementConnections
,
function
(
index
)
{
if
(
elementConnections
[
index
].
endpoints
[
0
].
getUuid
()
==
anchorId
||
elementConnections
[
index
].
endpoints
[
1
].
getUuid
()
==
anchorId
)
{
returnValue
=
true
;
return
;
}
});
return
returnValue
;
};
elementIsConnected
=
function
(
element
)
{
anchors
=
element
.
attr
(
"anchors"
);
id
=
element
.
attr
(
"id"
);
for
(
i
=
0
;
i
<
anchors
;
i
++
)
{
if
(
isConnected
(
i
+
"_"
+
id
))
{
return
true
;
}
}
return
false
;
};
getConnectionparamAndAnchor
=
function
(
anchorId
)
{
parameters
=
""
;
otherAnchor
=
""
;
$
.
each
(
elementConnections
,
function
(
index
)
{
if
(
elementConnections
[
index
].
endpoints
[
0
].
getUuid
()
==
anchorId
)
{
parameters
=
elementConnections
[
index
].
parameters
;
otherAnchor
=
elementConnections
[
index
].
endpoints
[
1
].
getUuid
();
return
;
}
if
(
elementConnections
[
index
].
endpoints
[
1
].
getUuid
()
==
anchorId
)
{
parameters
=
elementConnections
[
index
].
parameters
;
otherAnchor
=
elementConnections
[
index
].
endpoints
[
0
].
getUuid
();
return
;
}
});
return
[
otherAnchor
,
parameters
];
};
addEndpoint
=
function
(
element
)
{
anchors
=
element
.
attr
(
"anchors"
);
if
(
anchors
==
8
)
return
;
anchors
++
;
jsPlumbInstance
.
addEndpoint
(
document
.
getElementById
(
element
.
attr
(
"id"
)),
{
uuid
:
(
anchors
-
1
)
+
"_"
+
element
.
attr
(
"id"
)
},
jsPlumbEndpoint
);
for
(
i
=
0
;
i
<
anchors
;
i
++
)
{
jsPlumbInstance
.
getEndpoint
(
i
+
"_"
+
element
.
attr
(
"id"
)).
setAnchor
(
getAnchorCoordinate
(
i
/
(
anchors
)));
}
element
.
attr
(
"anchors"
,
anchors
);
jsPlumbInstance
.
repaintEverything
();
};
removeEndoint
=
function
(
element
)
{
anchors
=
element
.
attr
(
"anchors"
);
if
(
anchors
==
4
)
return
;
i
=
--
anchors
;
while
(
isConnected
(
i
+
"_"
+
element
.
attr
(
"id"
))
&&
i
>=
0
)
i
--
;
if
(
i
==
-
1
)
{
addMessage
(
gettext
(
"Removing anchors is obstructed."
),
"danger"
);
return
;
}
else
if
(
i
==
anchors
)
{
jsPlumbInstance
.
deleteEndpoint
(
jsPlumbInstance
.
getEndpoint
(
anchors
+
"_"
+
element
.
attr
(
"id"
)));
}
else
{
newId
=
i
+
"_"
+
element
.
attr
(
"id"
);
oldId
=
anchors
+
"_"
+
element
.
attr
(
"id"
);
data
=
getConnectionparamAndAnchor
(
oldId
);
data
.
splice
(
0
,
0
,
newId
);
jsPlumbInstance
.
deleteEndpoint
(
jsPlumbInstance
.
getEndpoint
(
oldId
));
connectEndpoints
(
data
);
}
for
(
i
=
0
;
i
<
anchors
;
i
++
)
jsPlumbInstance
.
getEndpoint
(
i
+
"_"
+
element
.
attr
(
"id"
)).
setAnchor
(
getAnchorCoordinate
(
i
/
(
anchors
)));
element
.
attr
(
"anchors"
,
anchors
);
jsPlumbInstance
.
repaintEverything
();
};
connectEndpoints
=
function
(
data
)
{
connectionObject
=
jsPlumbInstance
.
connect
({
source
:
data
[
0
],
target
:
data
[
1
]
});
connectionObject
.
parameters
=
data
[
2
];
setServiceStatus
(
"unsaved"
);
};
disconnectEndpoints
=
function
(
data
)
{
for
(
var
i
=
0
;
i
<
elementConnections
.
length
;
i
++
)
{
if
(
elementConnections
[
i
].
endpoints
[
0
].
getUuid
()
==
data
[
0
]
&&
elementConnections
[
i
].
endpoints
[
1
].
getUuid
()
==
data
[
1
])
{
jsPlumbInstance
.
detach
(
elementConnections
[
i
]);
return
;
}
}
return
;
};
addElement
=
function
(
idOrInstance
,
newId
,
newPositionY
,
endpoints
,
parameters
,
newPositionX
)
{
newInstance
=
""
;
if
(
typeof
idOrInstance
!=
"string"
)
{
newInstance
=
idOrInstance
;
endpoints
=
newInstance
.
attr
(
"anchomNumber"
);
newInstance
.
attr
(
"anchors"
,
0
);
}
else
{
newInstance
=
$
(
'#'
+
idOrInstance
)
.
clone
()
.
prop
(
"id"
,
newId
)
.
prop
(
"title"
,
"Right click to delete"
)
.
removeClass
()
.
addClass
(
"element"
)
.
attr
(
"anchors"
,
0
)
.
attr
(
"parameters"
,
parameters
)
.
css
(
"top"
,
newPositionY
)
.
css
(
"left"
,
newPositionX
);
}
$
(
"#dropContainer"
).
append
(
newInstance
);
for
(
i
=
0
;
i
<=
endpoints
;
i
++
)
{
addEndpoint
(
newInstance
);
}
jsPlumbInstance
.
draggable
(
jsPlumb
.
getSelector
(
".element"
),
{
containment
:
$
(
"#dropContainer"
)
});
setServiceStatus
(
"unsaved"
);
jsPlumbInstance
.
repaintEverything
();
return
newInstance
;
};
addMachine
=
function
(
idOrInstance
,
newId
,
newPositionY
,
endpoints
,
parameters
,
newPositionX
)
{
newInstance
=
""
;
newInstance
=
$
(
'<div>'
)
.
prop
(
"id"
,
newId
)
.
prop
(
"title"
,
"Right click to delete"
)
.
removeClass
()
.
addClass
(
"element"
)
.
attr
(
"anchors"
,
0
)
.
attr
(
"parameters"
,
parameters
)
.
css
(
"top"
,
newPositionY
)
.
css
(
"left"
,
newPositionX
);
$
(
"#dropContainer"
).
append
(
newInstance
);
for
(
i
=
0
;
i
<=
endpoints
;
i
++
)
{
addEndpoint
(
newInstance
);
}
jsPlumbInstance
.
draggable
(
jsPlumb
.
getSelector
(
".element"
),
{
containment
:
$
(
"#dropContainer"
)
});
setServiceStatus
(
"unsaved"
);
jsPlumbInstance
.
repaintEverything
();
return
newInstance
;
}
removeElement
=
function
(
object
)
{
jsPlumbInstance
.
detachAllConnections
(
object
);
jsPlumbInstance
.
remove
(
object
.
attr
(
"id"
));
};
/* Registering events using JsPlumb. */
jsPlumbInstance
.
bind
(
"connection"
,
function
(
info
)
{
updateConnections
(
info
.
connection
);
info
.
connection
.
parameters
=
""
;
if
(
clickEvent
===
0
)
{
undoStack
.
splice
(
stackIndexer
,
0
,
disconnectEndpoints
);
redoStack
.
splice
(
stackIndexer
,
0
,
connectEndpoints
);
connectionArray
=
[];
connectionArray
.
push
(
info
.
connection
.
endpoints
[
0
].
getUuid
(),
info
.
connection
.
endpoints
[
1
].
getUuid
(),
info
.
connection
.
parameters
);
objectStack
.
splice
(
stackIndexer
,
0
,
connectionArray
);
stackIndexer
++
;
stackSize
++
;
}
});
jsPlumbInstance
.
bind
(
"beforeDrop"
,
function
(
info
)
{
return
checkDuplicateConnection
(
info
.
connection
)
&&
checkSourceTargetEquality
(
info
.
connection
)
&&
checkCompatibility
(
info
.
connection
.
sourceId
,
info
.
connection
.
targetId
);
});
jsPlumbInstance
.
bind
(
"connectionDetached"
,
function
(
info
)
{
updateConnections
(
info
.
connection
,
true
);
if
(
clickEvent
===
0
)
{
undoStack
.
splice
(
stackIndexer
,
0
,
connectEndpoints
);
redoStack
.
splice
(
stackIndexer
,
0
,
disconnectEndpoints
);
connectionArray
=
[];
connectionArray
.
push
(
info
.
connection
.
endpoints
[
0
].
getUuid
(),
info
.
connection
.
endpoints
[
1
].
getUuid
(),
info
.
connection
.
parameters
);
objectStack
.
splice
(
stackIndexer
,
0
,
connectionArray
);
stackIndexer
++
;
stackSize
++
;
}
});
jsPlumbInstance
.
bind
(
"connectionMoved"
,
function
(
info
)
{
updateConnections
(
info
.
connection
,
true
);
});
jsPlumbInstance
.
bind
(
"contextmenu"
,
function
(
info
)
{
jsPlumbInstance
.
detach
(
info
);
});
jsPlumbInstance
.
bind
(
"dblclick"
,
function
(
info
)
{
info
.
setPaintStyle
({
strokeStyle
:
"red"
,
lineWidth
:
8
});
addInfo
(
$
(
"#"
+
info
.
sourceId
.
split
(
'_'
)[
1
]).
attr
(
"alt"
)
+
' - '
+
$
(
"#"
+
info
.
targetId
.
split
(
'_'
)[
1
]).
attr
(
"alt"
),
info
.
parameters
,
"connection"
,
info
);
});
jsPlumbInstance
.
draggable
(
jsPlumb
.
getSelector
(
".element"
),
{
containment
:
$
(
"#dropContainer"
)
});
/* Registering events using JQuery. */
$
(
'body'
).
on
(
'click'
,
'.elementTemplate'
,
function
()
{
addElement
(
$
(
this
).
attr
(
"id"
),
(
++
elementIndex
)
+
"_"
+
$
(
this
).
attr
(
"id"
),
(
elementIndex
%
21
)
*
30
,
4
,
""
,
(
elementIndex
%
21
)
*
30
);
undoStack
.
splice
(
stackIndexer
,
0
,
removeElement
);
redoStack
.
splice
(
stackIndexer
,
0
,
addElement
);
objectStack
.
splice
(
stackIndexer
,
0
,
newInstance
);
stackSize
++
;
stackIndexer
++
;
});
$
(
'body'
).
on
(
'dblclick'
,
'.element'
,
function
()
{
element
=
$
(
this
);
element
.
addClass
(
"elementSelected"
);
addInfo
(
element
.
attr
(
"alt"
),
element
.
attr
(
"parameters"
),
"element"
,
element
);
$
(
document
).
scrollTop
(
0
);
});
$
(
'body'
).
on
(
'contextmenu'
,
'.element'
,
function
(
event
)
{
setServiceStatus
(
"unsaved"
);
removeElement
(
$
(
this
));
undoStack
.
splice
(
stackIndexer
,
0
,
addElement
);
redoStack
.
splice
(
stackIndexer
,
0
,
removeElement
);
objectStack
.
splice
(
stackIndexer
,
0
,
$
(
this
));
nextStepConstraint
=
0
;
stackSize
++
;
stackIndexer
++
;
});
$
(
'body'
).
on
(
'click'
,
'#closeInfoPanel'
,
function
()
{
$
(
'#informationPanel'
).
hide
();
$
(
'#dragPanel'
).
show
();
});
$
(
'body'
).
on
(
'keyUp'
,
'#infoInput'
,
function
()
{
setServiceStatus
(
"unsaved"
);
newParams
=
$
(
"#infoInput"
).
val
();
if
(
type
==
"connection"
)
object
.
parameters
=
newParams
;
if
(
type
==
"element"
)
object
.
attr
(
"parameters"
,
newParams
);
});
$
(
'body'
).
on
(
'click'
,
'#addEndpoint'
,
function
()
{
addEndpoint
(
sharedObject
);
undoStack
.
splice
(
stackIndexer
,
0
,
removeEndoint
);
redoStack
.
splice
(
stackIndexer
,
0
,
addEndpoint
);
objectStack
.
splice
(
stackIndexer
,
0
,
sharedObject
);
stackIndexer
++
;
stackSize
++
;
});
$
(
'body'
).
on
(
'click'
,
'#removeEndpoint'
,
function
()
{
removeEndoint
(
sharedObject
);
undoStack
.
splice
(
stackIndexer
,
0
,
addEndpoint
);
redoStack
.
splice
(
stackIndexer
,
0
,
removeEndoint
);
objectStack
.
splice
(
stackIndexer
,
0
,
sharedObject
);
stackIndexer
++
;
stackSize
++
;
});
$
(
'body'
).
on
(
'click'
,
'#removeElementFromWorkspace'
,
function
()
{
setServiceStatus
(
"unsaved"
);
removeElement
(
sharedObject
);
undoStack
.
splice
(
stackIndexer
,
0
,
addElement
);
redoStack
.
splice
(
stackIndexer
,
0
,
removeElement
);
objectStack
.
splice
(
stackIndexer
,
0
,
sharedObject
);
stackSize
++
;
stackIndexer
++
;
});
$
(
'body'
).
on
(
'click'
,
'#removeConnection'
,
function
()
{
jsPlumbInstance
.
detach
(
sharedObject
);
});
$
(
'body'
).
on
(
'click'
,
'#addElementToWorkspace'
,
function
()
{
newInstance
=
addElement
(
sharedObject
.
attr
(
"id"
),
(
++
elementIndex
)
+
"_"
+
sharedObject
.
attr
(
"id"
),
(
elementIndex
%
21
)
*
30
,
4
,
""
,
(
elementIndex
%
21
)
*
30
);
undoStack
.
splice
(
stackIndexer
,
0
,
removeElement
);
redoStack
.
splice
(
stackIndexer
,
0
,
addElement
);
objectStack
.
splice
(
stackIndexer
,
0
,
newInstance
);
stackSize
++
;
stackIndexer
++
;
});
$
(
'body'
).
on
(
'click'
,
'#clearService'
,
function
()
{
jsPlumbInstance
.
remove
(
"element"
);
setServiceStatus
(
"unsaved"
);
elementIndex
=
0
;
});
$
(
'body'
).
on
(
'click'
,
'#undoMovement'
,
function
()
{
if
(
stackIndexer
<
1
)
return
;
stackIndexer
--
;
clickEvent
=
1
;
object
=
objectStack
[
stackIndexer
];
undoStack
[
stackIndexer
](
object
);
clickEvent
=
0
;
});
$
(
'body'
).
on
(
'click'
,
'#redoMovement'
,
function
()
{
if
(
stackIndexer
>=
stackSize
)
return
;
clickEvent
=
1
;
object
=
objectStack
[
stackIndexer
];
redoStack
[
stackIndexer
](
object
);
stackIndexer
++
;
clickEvent
=
0
;
});
$
(
'body'
).
on
(
'click'
,
'#addMachineDialog'
,
function
()
{
// Here comes the ajax post of getMachineAvailableList
// posting usedhostnames
//
//
// after it, appending obtained content to addmachinedialogbody
});
$
(
'body'
).
on
(
'click'
,
'.elementTemplateInfo'
,
function
()
{
id
=
$
(
this
).
attr
(
"element"
);
addInfo
(
$
(
"#"
+
id
).
attr
(
"alt"
),
$
(
"#"
+
id
).
attr
(
"desc"
),
"elementTemplate"
,
$
(
"#"
+
id
));
});
$
(
'body'
).
on
(
'click'
,
'#serviceName'
,
function
()
{
$
(
'#serviceName'
).
hide
();
$
(
"#serviceNameEdit"
).
css
(
"display"
,
"inline"
).
val
(
$
(
this
).
text
()).
select
();
$
(
"#serviceNameSave"
).
css
(
"display"
,
"inline"
);
setServiceStatus
(
"unsaved"
);
});
$
(
'body'
).
on
(
'click'
,
'#serviceNameSave'
,
function
()
{
$
(
'#serviceNameEdit'
).
hide
();
$
(
this
).
hide
();
$
(
"#serviceName"
).
show
().
text
(
$
(
'#serviceNameEdit'
).
val
());
});
$
(
'body'
).
on
(
'click'
,
'#dragContainerScrollUp'
,
function
()
{
scrollContainer
(
-
1
);
});
$
(
'body'
).
on
(
'click'
,
'#dragContainerScrollDown'
,
function
()
{
scrollContainer
(
1
);
});
$
(
'body'
).
on
(
'hide.bs.modal'
,
'#changeInformationDialog'
,
function
()
{
$
(
'.element'
).
removeClass
(
'elementSelected'
);
jsPlumbInstance
.
select
().
setPaintStyle
({
strokeStyle
:
'#9932cc'
,
lineWidth
:
8
});
});
$
(
'body'
).
on
(
'keyup'
,
'#searchElementTemplate'
,
function
()
{
$
(
".elementTemplate"
).
each
(
function
()
{
$
(
this
).
parent
().
parent
().
hide
();
if
(
$
(
this
).
attr
(
"alt"
).
toLowerCase
().
indexOf
(
$
(
"#searchElementTemplateInput"
).
val
().
toLowerCase
())
>=
0
)
$
(
this
).
parent
().
parent
().
show
();
});
});
$
(
'body'
).
on
(
'mousewheel DOMMouseScroll onmousewheel'
,
function
(
event
)
{
var
e
=
window
.
event
||
event
;
var
delta
=
Math
.
max
(
-
1
,
Math
.
min
(
1
,
(
e
.
wheelDelta
||
-
e
.
detail
)));
$
(
'body'
).
addClass
(
"noScroll"
);
dragContainerScroll
-=
delta
;
if
(
dragContainerScroll
==
$
(
".elementTemplate"
).
length
-
2
)
dragContainerScroll
--
;
if
(
dragContainerScroll
==
-
1
)
dragContainerScroll
++
;
$
(
"#dragContainer"
).
scrollTop
(
dragContainerScroll
*
$
(
"#elementTemplatePanel"
).
height
()
);
$
(
'body'
).
removeClass
(
"noScroll"
);
});
$
(
document
).
on
(
'keydown'
,
function
(
e
)
{
var
eventObject
=
window
.
event
?
event
:
e
;
// Undo (CTRL + Z)
if
(
eventObject
.
keyCode
==
90
&&
eventObject
.
ctrlKey
)
{
eventObject
.
preventDefault
();
$
(
'#undoMovement'
).
click
();
}
// Redo (CTRL + Y)
if
(
eventObject
.
keyCode
==
89
&&
eventObject
.
ctrlKey
)
{
eventObject
.
preventDefault
();
$
(
'#redoMovement'
).
click
();
}
// Add element (CTRL + A)
if
(
eventObject
.
keyCode
==
65
&&
eventObject
.
ctrlKey
)
{
eventObject
.
preventDefault
();
$
(
'#showAddElementDialog'
).
click
();
}
// Clean (CTRL + C)
if
(
eventObject
.
keyCode
==
67
&&
eventObject
.
ctrlKey
)
{
eventObject
.
preventDefault
();
$
(
'#clearService'
).
click
();
}
// Save (CTRL + S)
if
(
eventObject
.
keyCode
==
83
&&
eventObject
.
ctrlKey
)
{
eventObject
.
preventDefault
();
$
(
'#saveService'
).
click
();
}
// Delete (CTRL + D)
if
(
eventObject
.
keyCode
==
68
&&
eventObject
.
ctrlKey
)
{
eventObject
.
preventDefault
();
$
(
'#deleteService'
).
click
();
}
});
$
(
window
).
on
(
'resize'
,
function
()
{
$
(
".element"
).
each
(
function
()
{
rate
=
(
$
(
this
).
position
().
left
)
/
workspaceWidth
;
left
=
rate
*
(
$
(
"#dropContainer"
).
width
());
$
(
this
).
css
(
"left"
,
left
);
});
workspaceWidth
=
$
(
"#dropContainer"
).
width
();
jsPlumbInstance
.
repaintEverything
();
});
/* Registering events concerning persistence. */
$
(
'body'
).
on
(
'click'
,
'#saveService'
,
function
()
{
serviceName
=
$
(
"#serviceName"
).
text
();
connectionSet
=
[];
instanceSet
=
[];
$
.
each
(
elementConnections
,
function
(
index
)
{
connectionSet
.
push
({
"sourceId"
:
elementConnections
[
index
].
sourceId
,
"sourceEndpoint"
:
elementConnections
[
index
].
endpoints
[
0
].
getUuid
(),
"targetId"
:
elementConnections
[
index
].
targetId
,
"targetEndpoint"
:
elementConnections
[
index
].
endpoints
[
1
].
getUuid
(),
"parameters"
:
elementConnections
[
index
].
parameters
});
});
$
.
each
(
$
(
".element"
),
function
()
{
instanceSet
.
push
({
"displayId"
:
$
(
this
).
prop
(
"id"
),
"positionLeft"
:
$
(
this
).
position
().
left
/
workspaceWidth
,
"positionTop"
:
$
(
this
).
position
().
top
/
workspaceHeight
,
"anchorNumber"
:
$
(
this
).
attr
(
"anchors"
),
"parameters"
:
$
(
this
).
attr
(
"parameters"
)});
});
$
.
post
(
""
,
{
event
:
"saveService"
,
data
:
JSON
.
stringify
({
"serviceName"
:
serviceName
,
"elementConnections"
:
connectionSet
,
"elements"
:
instanceSet
,
"machines"
:
[]})
//TODO: Dani: add machines here
},
function
(
result
)
{
addMessage
(
result
.
serviceName
+
gettext
(
" saved successfully."
),
"success"
);
setServiceStatus
(
"saved"
);
});
});
$
(
document
).
ready
(
function
()
{
if
(
!
$
(
"#dropContainer"
).
length
)
return
;
$
.
post
(
""
,
{
event
:
"loadService"
},
function
(
result
)
{
$
(
"#serviceName"
).
text
(
result
.
serviceName
);
$
.
each
(
result
.
serviceNodes
,
function
(
i
,
element
)
{
addElement
(
element
.
displayId
.
split
(
'_'
)[
1
],
element
.
displayId
,
(
element
.
positionTop
*
workspaceHeight
)
+
"px"
,
element
.
anchorNumber
,
element
.
parameters
,
(
element
.
positionLeft
*
workspaceWidth
)
+
"px"
);
if
(
elementIndex
<
element
.
displayId
.
split
(
'_'
)[
0
])
elementIndex
=
element
.
displayId
.
split
(
'_'
)[
0
];
elementIndex
++
;
});
$
.
each
(
result
.
machines
,
function
(
i
,
element
)
{
addMachine
(
element
.
displayId
.
split
(
'_'
)[
1
],
element
.
displayId
,
(
element
.
positionTop
*
workspaceHeight
)
+
"px"
,
element
.
anchorNumber
,
element
.
parameters
,
(
element
.
positionLeft
*
workspaceWidth
)
+
"px"
);
if
(
elementIndex
<
element
.
displayId
.
split
(
'_'
)[
0
])
elementIndex
=
element
.
displayId
.
split
(
'_'
)[
0
];
elementIndex
++
;
});
clickEvent
=
1
;
$
.
each
(
result
.
elementConnections
,
function
(
i
,
connection
)
{
connectEndpoints
([
connection
.
sourceEndpoint
,
connection
.
targetEndpoint
,
connection
.
parameters
]);
});
clickEvent
=
0
;
setServiceStatus
(
"saved"
);
});
});
});
circle/setty/static/setty/ubuntu.jpg
View file @
9ab17d5f
9.06 KB
|
W:
|
H:
9.06 KB
|
W:
|
H:
2-up
Swipe
Onion skin
circle/setty/static/setty/wordpress.jpg
View file @
9ab17d5f
19.9 KB
|
W:
|
H:
19.9 KB
|
W:
|
H:
2-up
Swipe
Onion skin
circle/setty/tables.py
View file @
9ab17d5f
# Copyright 2014 Budapest University of Technology and Economics (BME IK)
#
# This file is part of CIRCLE Cloud.
#
# CIRCLE is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# CIRCLE is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
from
django_tables2
import
Table
from
django_tables2.columns
import
TemplateColumn
from
setty.models
import
Service
from
django.utils.translation
import
ugettext
as
_
class
ServiceListTable
(
Table
):
name
=
TemplateColumn
(
template_name
=
"setty/tables/column-name.html"
,
attrs
=
{
'th'
:
{
'data-sort'
:
"string"
}}
)
owner
=
TemplateColumn
(
template_name
=
"setty/tables/column-owner.html"
,
verbose_name
=
_
(
"Owner"
),
attrs
=
{
'th'
:
{
'data-sort'
:
"string"
}}
)
running
=
TemplateColumn
(
template_name
=
"setty/tables/column-running.html"
,
verbose_name
=
_
(
"Running"
),
attrs
=
{
'th'
:
{
'data-sort'
:
"string"
}},
)
class
Meta
:
model
=
Service
attrs
=
{
'class'
:
(
'table table-bordered table-striped table-hover'
' template-list-table'
)}
fields
=
(
'name'
,
'owner'
,
'running'
,
)
prefix
=
"service-"
# Copyright 2014 Budapest University of Technology and Economics (BME IK)
#
# This file is part of CIRCLE Cloud.
#
# CIRCLE is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# CIRCLE is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
from
django_tables2
import
Table
from
django_tables2.columns
import
TemplateColumn
from
setty.models
import
Service
from
django.utils.translation
import
ugettext
as
_
class
ServiceListTable
(
Table
):
name
=
TemplateColumn
(
template_name
=
"setty/tables/column-name.html"
,
attrs
=
{
'th'
:
{
'data-sort'
:
"string"
}}
)
owner
=
TemplateColumn
(
template_name
=
"setty/tables/column-owner.html"
,
verbose_name
=
_
(
"Owner"
),
attrs
=
{
'th'
:
{
'data-sort'
:
"string"
}}
)
running
=
TemplateColumn
(
template_name
=
"setty/tables/column-running.html"
,
verbose_name
=
_
(
"Running"
),
attrs
=
{
'th'
:
{
'data-sort'
:
"string"
}},
)
class
Meta
:
model
=
Service
attrs
=
{
'class'
:
(
'table table-bordered table-striped table-hover'
' template-list-table'
)}
fields
=
(
'name'
,
'owner'
,
'running'
,
)
prefix
=
"service-"
circle/setty/views.py
View file @
9ab17d5f
...
...
@@ -24,7 +24,8 @@ from django.shortcuts import redirect
from
braces.views
import
LoginRequiredMixin
from
django.views.generic
import
TemplateView
,
DeleteView
from
django_tables2
import
SingleTableView
from
.models
import
Element
,
ElementTemplate
,
ElementConnection
,
Service
from
saltstackhelper
import
*
from
controller
import
*
from
dashboard.views.util
import
FilterMixin
from
django.utils.translation
import
ugettext
as
_
import
json
...
...
@@ -39,6 +40,7 @@ logger = logging.getLogger(__name__)
class
DetailView
(
LoginRequiredMixin
,
TemplateView
):
template_name
=
"setty/index.html"
salthelper
=
SaltStackHelper
()
def
get_context_data
(
self
,
**
kwargs
):
logger
.
debug
(
'DetailView.get_context_data() called. User:
%
s'
,
...
...
@@ -54,88 +56,37 @@ class DetailView(LoginRequiredMixin, TemplateView):
raise
PermissionDenied
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
logger
.
debug
(
'DetailView.post() called. User:
%
s'
,
unicode
(
self
.
request
.
user
))
service
=
Service
.
objects
.
get
(
id
=
kwargs
[
'pk'
])
if
self
.
request
.
user
==
service
.
user
or
self
.
request
.
user
.
is_superuser
:
if
self
.
request
.
POST
.
get
(
'event'
)
==
"saveService"
:
data
=
json
.
loads
(
self
.
request
.
POST
.
get
(
'data'
))
service
=
Service
.
objects
.
get
(
id
=
kwargs
[
'pk'
])
service
.
name
=
data
[
'serviceName'
]
service
.
save
()
Element
.
objects
.
filter
(
service
=
service
)
.
delete
()
for
element
in
data
[
'elements'
]:
elementObject
=
Element
(
service
=
service
,
parameters
=
element
[
'parameters'
],
display_id
=
element
[
'displayId'
],
position_left
=
element
[
'positionLeft'
],
position_top
=
element
[
'positionTop'
],
anchor_number
=
element
[
'anchorNumber'
]
)
elementObject
.
save
()
for
elementConnection
in
data
[
'elementConnections'
]:
sourceId
=
elementConnection
[
'sourceId'
]
targetId
=
elementConnection
[
'targetId'
]
sourceEndpoint
=
elementConnection
[
'sourceEndpoint'
]
targetEndpoint
=
elementConnection
[
'targetEndpoint'
]
connectionParameters
=
elementConnection
[
'parameters'
]
targetObject
=
Element
.
objects
.
get
(
display_id
=
targetId
,
service
=
service
)
sourceObject
=
Element
.
objects
.
get
(
display_id
=
sourceId
,
service
=
service
)
connectionObject
=
ElementConnection
(
target
=
targetObject
,
source
=
sourceObject
,
target_endpoint
=
targetEndpoint
,
source_endpoint
=
sourceEndpoint
,
parameters
=
connectionParameters
)
connectionObject
.
save
()
return
JsonResponse
({
'serviceName'
:
service
.
name
})
elif
self
.
request
.
POST
.
get
(
'event'
)
==
"loadService"
:
service
=
Service
.
objects
.
get
(
id
=
kwargs
[
'pk'
])
elementList
=
Element
.
objects
.
filter
(
service
=
service
)
elementConnectionList
=
ElementConnection
.
objects
.
filter
(
Q
(
target__in
=
elementList
)
|
Q
(
source__in
=
elementList
))
elements
=
[]
elementConnections
=
[]
for
item
in
elementList
:
elements
.
append
({
'parameters'
:
item
.
parameters
,
'displayId'
:
item
.
display_id
,
'positionLeft'
:
item
.
position_left
,
'positionTop'
:
item
.
position_top
,
'anchorNumber'
:
item
.
anchor_number
})
for
item
in
elementConnectionList
:
elementConnections
.
append
({
'targetEndpoint'
:
item
.
target_endpoint
,
'sourceEndpoint'
:
item
.
source_endpoint
,
'parameters'
:
item
.
parameters
})
return
JsonResponse
(
{
'elements'
:
elements
,
'elementConnections'
:
elementConnections
,
'serviceName'
:
service
.
name
})
else
:
raise
PermissionDenied
else
:
if
self
.
request
.
user
!=
service
.
user
or
not
self
.
request
.
user
.
is_superuser
:
raise
PermissionDenied
result
=
{}
eventName
=
self
.
request
.
POST
.
get
(
'event'
)
serviceId
=
kwargs
[
'pk'
]
if
eventName
==
'loadService'
:
result
=
SettyController
.
loadService
(
serviceId
)
elif
eventName
==
"deploy"
:
result
=
SettyController
.
deploy
(
serviceId
)
data
=
json
.
loads
(
self
.
request
.
POST
.
get
(
'data'
))
if
eventName
==
"saveService"
:
result
=
SettyController
.
saveService
(
serviceId
,
data
[
'serviceName'
],
data
[
'serviceNodes'
],
data
[
'machines'
],
data
[
'elementConnections'
])
elif
eventName
==
"getMachineAvailableList"
:
result
=
SettyController
.
getMachineAvailableList
(
serviceId
,
data
[
"usedHostnames"
])
elif
eventName
==
"addServiceNode"
:
result
=
SettyController
.
addServiceNode
(
data
[
"elementTemplateId"
])
elif
eventName
==
"addMachine"
:
result
=
SettyController
.
addMachine
(
data
[
"hostname"
])
elif
eventName
==
"getInformation"
:
result
=
SettyController
.
getInformation
(
data
[
'elementTemplateId'
],
data
[
'hostname'
])
return
JsonResponse
(
result
)
class
DeleteView
(
LoginRequiredMixin
,
DeleteView
):
...
...
@@ -180,11 +131,16 @@ class CreateView(LoginRequiredMixin, TemplateView):
if
not
service_name
:
service_name
=
"Noname"
service
=
Service
(
name
=
service_name
,
status
=
"stopped"
,
user
=
self
.
request
.
user
)
try
:
serviceNameAvailable
=
Service
.
objects
.
get
(
name
=
service_name
)
raise
PermissionDenied
except
Service
.
DoesNotExist
:
pass
service
=
Service
(
name
=
service_name
,
status
=
1
,
user
=
self
.
request
.
user
)
service
.
save
()
return
redirect
(
'setty.views.service-detail'
,
pk
=
service
.
pk
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment