480 lines
14 KiB
Python
480 lines
14 KiB
Python
import sys
|
|
import yaml
|
|
import os
|
|
from PyQt4 import QtGui, QtCore
|
|
|
|
|
|
class DictQuery(dict):
|
|
# https://www.haykranen.nl/2016/02/13/handling-complex
|
|
# -nested-dicts-in-python/
|
|
def get(self, path, default=None):
|
|
keys = path.split(".")
|
|
val = None
|
|
|
|
for key in keys:
|
|
if val:
|
|
if isinstance(val, list):
|
|
val = [v.get(key, default) if v else None for v in val]
|
|
else:
|
|
val = val.get(key, default)
|
|
else:
|
|
val = dict.get(self, key, default)
|
|
|
|
if not val:
|
|
break
|
|
|
|
return val
|
|
|
|
|
|
def create_path(path_str, delimiter="/"):
|
|
"""
|
|
helper function to crate a path from a string
|
|
|
|
Keword Arguments:
|
|
path_str -- a "/" separated path
|
|
delimiter -- you can use a different delimiter
|
|
"""
|
|
|
|
path = ''
|
|
|
|
for d in path_str.split(delimiter):
|
|
path = os.path.join(path, d)
|
|
|
|
return path
|
|
|
|
|
|
def nested_path(dic, path, value, delimiter='.'):
|
|
"""
|
|
sets the value in a dictionary object given a string key
|
|
separated by '.'
|
|
|
|
Keyword Arguments:
|
|
path -- the string key whose path is separated by '.'
|
|
value -- the value we wish to set
|
|
delimiter -- the delimeter value used to separate the path
|
|
"""
|
|
|
|
keys = path.split('.')
|
|
return nested_set(dic, keys, value)
|
|
|
|
|
|
def nested_set(dic, keys, value):
|
|
"""
|
|
sets the value in a dictionary object given a list object
|
|
of keys
|
|
|
|
Keyword Arguments:
|
|
dic -- the dictionary object
|
|
keys -- the list object of keys
|
|
value -- the value we wish to set
|
|
"""
|
|
|
|
for key in keys[:-1]:
|
|
dic = dic.setdefault(key, {})
|
|
dic[keys[-1]] = value
|
|
|
|
|
|
class DeployMeta(QtGui.QMainWindow):
|
|
|
|
PATH_META = "../../../meta/project"
|
|
PATH_TEMPLATES = "../share/templates/meta"
|
|
|
|
path_meta = None
|
|
path_templates = None
|
|
|
|
default_store_file = "test.yml"
|
|
|
|
currentbranch = None
|
|
config_data = None
|
|
|
|
CONFIG_TYPES = {
|
|
'development': 'development.yml',
|
|
'staging': 'staging.yml',
|
|
'production': 'staging.yml',
|
|
}
|
|
|
|
widgets = None
|
|
|
|
widgets_baseinfo = {
|
|
'PROJECT_NAME': {
|
|
'title': 'Project Name',
|
|
'location': 'project.name',
|
|
},
|
|
|
|
'PROJECT_IP': {
|
|
'title': 'Project Host',
|
|
'location': 'project.host',
|
|
},
|
|
|
|
'BRANCH_NAME': {
|
|
'title': 'Branch Name',
|
|
'location': 'project.branch',
|
|
'default': {
|
|
'development': "development",
|
|
'staging': "staging",
|
|
'production': "production"
|
|
}
|
|
},
|
|
|
|
'BRANCH_EXT': {
|
|
'title': 'Branch Extension',
|
|
'location': 'project.extension',
|
|
'default': {
|
|
"development": "dev",
|
|
"staging": "stg",
|
|
"production": "prd"
|
|
}
|
|
},
|
|
|
|
'BRANCH_USER': {
|
|
'title': 'Branch User',
|
|
'location': 'project.user',
|
|
'default': {
|
|
"development": "ronny",
|
|
"staging": "website",
|
|
"production": "website"
|
|
}
|
|
},
|
|
|
|
'BRANCH_GROUP': {
|
|
'title': 'Branch Group',
|
|
'location': 'project.group',
|
|
'default': {
|
|
"development": "wheel",
|
|
"staging": "www-data",
|
|
"production": "www-data"
|
|
}
|
|
},
|
|
|
|
'DATABASE_IP': {
|
|
'title': 'Database IP',
|
|
'location': 'database.host'},
|
|
|
|
'DATABASE_PORT': {
|
|
'title': 'Database Port',
|
|
'location': 'database.port',
|
|
'default': {
|
|
'development': 'DOCKER_PORT',
|
|
"staging": "5432",
|
|
"production": "5432"
|
|
},
|
|
},
|
|
|
|
'DATABASE_NAME': {
|
|
'title': 'Database Name',
|
|
'location': 'database.name',
|
|
},
|
|
|
|
'DATABASE_ADMIN_NAME': {
|
|
'title': 'Database Admin Name',
|
|
'location': 'database.users.admin.name',
|
|
'default': {
|
|
"development": "admin",
|
|
"staging": "admin",
|
|
"production": "admin"
|
|
}
|
|
},
|
|
|
|
'DATABASE_ADMIN_PASS': {
|
|
'title': 'Database Admin Pass',
|
|
'location': 'database.users.admin.pass',
|
|
'default': {
|
|
'development': "admin",
|
|
'staging': "admin",
|
|
'production': "admin"
|
|
}
|
|
},
|
|
|
|
'DATABASE_USER_NAME': {
|
|
'title': 'Database User Name',
|
|
'location': 'database.users.default.name',
|
|
'default': {
|
|
'development': "ronny",
|
|
'staging': "website",
|
|
'production': "website"
|
|
}
|
|
},
|
|
|
|
'DATABASE_USER_PASS': {
|
|
'title': 'Database User Pass',
|
|
'location': 'database.users.default.pass',
|
|
'default': {
|
|
'development': "admin",
|
|
'staging': "admin",
|
|
'production': "admin",
|
|
}
|
|
},
|
|
|
|
'DJANGO_IP': {
|
|
'title': 'Django IP Address',
|
|
'location': 'django.host',
|
|
'default': {
|
|
'development': "127.0.0.1",
|
|
'staging': None,
|
|
'production': None
|
|
}
|
|
},
|
|
|
|
'DJANGO_PORT': {
|
|
'title': 'Django Port',
|
|
'location': 'django.port',
|
|
'default': {
|
|
'development': None,
|
|
'staging': "8000",
|
|
'production': "8010",
|
|
},
|
|
},
|
|
|
|
'NGINX_PORT': {
|
|
'title': 'Nginx Port',
|
|
'location': 'nginx.port',
|
|
'default': {
|
|
'development': "8050",
|
|
'staging': "80",
|
|
'production': "80",
|
|
},
|
|
}
|
|
}
|
|
|
|
def __init__(self):
|
|
super(DeployMeta, self).__init__()
|
|
|
|
# create the base path variables
|
|
self.path_meta = create_path(self.PATH_META)
|
|
self.path_templates = create_path(self.PATH_TEMPLATES)
|
|
|
|
self.widgets = dict()
|
|
for key in self.widgets_baseinfo:
|
|
self.widgets[key] = dict()
|
|
self.widgets[key]['title'] = self.widgets_baseinfo[key]['title']
|
|
self.widgets[key]['location'] = \
|
|
self.widgets_baseinfo[key]['location']
|
|
|
|
if 'default' in self.widgets_baseinfo[key]:
|
|
self.widgets[key]['default'] = \
|
|
self.widgets_baseinfo[key]['default']
|
|
else:
|
|
self.widgets[key]['default'] = None
|
|
|
|
self.widgets[key]['field'] = None
|
|
|
|
self.initUI()
|
|
|
|
def load_config(self, configname):
|
|
|
|
if configname not in self.CONFIG_TYPES.keys():
|
|
print "\nerror, load_config was called with parameter: {confname}," \
|
|
"which is not a legitimate value in CONFIG TYPES." \
|
|
"\nLegitimate values are {configvalues}".format(
|
|
confname=configname,
|
|
configvalues=self.CONFIG_TYPES.keys())
|
|
sys.exit()
|
|
|
|
path_config_full = os.path.join(
|
|
self.path_templates, self.CONFIG_TYPES[configname])
|
|
|
|
configuration_file = yaml.load(file(path_config_full, 'r'))
|
|
return configuration_file
|
|
|
|
def store_config(self):
|
|
|
|
for key in self.widgets:
|
|
configvalue = str(self.widgets[key]['field'].text().__str__())
|
|
|
|
if configvalue.isdigit():
|
|
configvalue = int(configvalue)
|
|
|
|
nested_path(
|
|
self.config_data,
|
|
self.widgets[key]['location'],
|
|
configvalue)
|
|
|
|
dquery = DictQuery(self.config_data)
|
|
|
|
database_name = "{projectname}_{branchext}".format(
|
|
projectname=dquery.get('project.name'),
|
|
branchext=dquery.get('project.extension'))
|
|
|
|
nested_path(
|
|
self.config_data, 'database.name', database_name)
|
|
|
|
nested_path(
|
|
self.config_data, 'virtualenv.name', dquery.get('project.name'))
|
|
|
|
if self.currentbranch == 'development':
|
|
projectpath = "{projectname}.prj".format(
|
|
projectname=dquery.get('project.name'))
|
|
nested_path(
|
|
self.config_data, 'project.paths.home', projectpath)
|
|
|
|
def add_widgetrow(self, key, grid):
|
|
|
|
row = grid.rowCount()
|
|
|
|
title = self.widgets[key]['title']
|
|
|
|
label = QtGui.QLabel(title)
|
|
field = QtGui.QLineEdit()
|
|
|
|
grid.addWidget(label, row, 0)
|
|
grid.addWidget(field, row, 1)
|
|
|
|
self.widgets[key]['field'] = field
|
|
|
|
def create_action(self, key):
|
|
title = key.capitalize()
|
|
menuname = "&%s" % title
|
|
shortcut = "Ctrl+%s" % title[0]
|
|
|
|
desc = "Load Configuration %s" % title
|
|
|
|
loadAction = QtGui.QAction(menuname, self)
|
|
loadAction.setShortcut(shortcut)
|
|
loadAction.setStatusTip(desc)
|
|
loadAction.triggered.connect(self.action_loadconfig)
|
|
|
|
variant = QtCore.QVariant(key)
|
|
loadAction.setData(variant)
|
|
|
|
return loadAction
|
|
|
|
def action_loadconfig(self):
|
|
sender = self.sender()
|
|
self.currentbranch = sender.data().toString().__str__()
|
|
|
|
self.config_data = self.load_config(self.currentbranch)
|
|
|
|
dquery = DictQuery(self.config_data)
|
|
for key in self.widgets:
|
|
configvalue = dquery.get(self.widgets[key]['location'])
|
|
|
|
if self.widgets[key]['default']:
|
|
defaultvalue = self.widgets[key]['default'][self.currentbranch]
|
|
|
|
if configvalue == key and defaultvalue is not None:
|
|
self.widgets[key]['field'].setText(defaultvalue)
|
|
else:
|
|
self.widgets[key]['field'].setText(str(configvalue))
|
|
else:
|
|
self.widgets[key]['field'].setText(configvalue)
|
|
|
|
def action_save_config(self):
|
|
|
|
# first make sure we've loaded up a configuration
|
|
# file to save. currentbranch will be set if we
|
|
# loaded it up
|
|
|
|
if self.currentbranch:
|
|
|
|
self.store_config()
|
|
|
|
# path_newconfig = os.path.join(
|
|
# self.path_meta, self.default_store_file)
|
|
|
|
dialog_title = "Save {currentbranch} configuration".format(
|
|
currentbranch=self.currentbranch)
|
|
|
|
#
|
|
# generate the default path to save to
|
|
|
|
path_default_save = os.path.join(
|
|
self.path_meta, self.default_store_file)
|
|
|
|
#
|
|
# get the path value from the dialog box
|
|
|
|
path_newconfig = QtGui.QFileDialog.getSaveFileName(
|
|
self, dialog_title,
|
|
path_default_save,
|
|
selectedFilter='*.yml')
|
|
|
|
#
|
|
# if the user hit 'cancel' path_newconfig will be empty
|
|
|
|
if path_newconfig:
|
|
stream = file(path_newconfig, 'w')
|
|
yaml.dump(self.config_data, stream)
|
|
|
|
else:
|
|
#
|
|
# display message warning no configuration has been loaded
|
|
|
|
from PyQt4.QtGui import QMessageBox
|
|
msg = QMessageBox()
|
|
msg.setIcon(QMessageBox.Warning)
|
|
msg.setText('Save Error')
|
|
msg.setInformativeText(
|
|
"Cannot save modified configuration until you load one of the"
|
|
" default config types")
|
|
|
|
retval = msg.exec_()
|
|
print "value of qmessagebox in action_save: %s" % retval
|
|
|
|
def setupMenu(self):
|
|
menubar = self.menuBar()
|
|
menubar.setNativeMenuBar(False)
|
|
|
|
exitAction = QtGui.QAction('&Exit', self)
|
|
exitAction.setShortcut('Ctrl+Q')
|
|
exitAction.setStatusTip("Exit application")
|
|
exitAction.triggered.connect(QtGui.qApp.quit)
|
|
|
|
saveAction = QtGui.QAction('&Gore', self)
|
|
saveAction.setShortcut('Ctrl+T')
|
|
saveAction.setStatusTip("Save Config File")
|
|
saveAction.triggered.connect(self.action_save_config)
|
|
|
|
fileMenu = menubar.addMenu('&File')
|
|
fileMenu.addAction(exitAction)
|
|
fileMenu.addAction(saveAction)
|
|
|
|
loadMenu = menubar.addMenu("&Load Configuration")
|
|
|
|
for key in self.CONFIG_TYPES:
|
|
loadMenu.addAction(self.create_action(key))
|
|
|
|
def initUI(self):
|
|
|
|
self.setupMenu()
|
|
|
|
grid = QtGui.QGridLayout()
|
|
grid.setSpacing(10)
|
|
|
|
self.add_widgetrow('PROJECT_NAME', grid)
|
|
self.add_widgetrow('PROJECT_IP', grid)
|
|
self.add_widgetrow('BRANCH_NAME', grid)
|
|
self.add_widgetrow('BRANCH_EXT', grid)
|
|
self.add_widgetrow('BRANCH_USER', grid)
|
|
self.add_widgetrow('BRANCH_GROUP', grid)
|
|
self.add_widgetrow('DATABASE_IP', grid)
|
|
self.add_widgetrow('DATABASE_PORT', grid)
|
|
self.add_widgetrow('DATABASE_NAME', grid)
|
|
self.add_widgetrow('DATABASE_ADMIN_NAME', grid)
|
|
self.add_widgetrow('DATABASE_ADMIN_PASS', grid)
|
|
self.add_widgetrow('DATABASE_USER_NAME', grid)
|
|
self.add_widgetrow('DATABASE_USER_PASS', grid)
|
|
self.add_widgetrow('DJANGO_IP', grid)
|
|
self.add_widgetrow('DJANGO_PORT', grid)
|
|
self.add_widgetrow('NGINX_PORT', grid)
|
|
|
|
central = QtGui.QWidget()
|
|
central.setLayout(grid)
|
|
self.setCentralWidget(central)
|
|
|
|
self.setGeometry(300, 300, 450, 300)
|
|
self.setWindowTitle("Deploy Meta Files")
|
|
self.show()
|
|
self.raise_()
|
|
|
|
|
|
def main():
|
|
app = QtGui.QApplication(sys.argv)
|
|
app.setStyle("cleanlooks")
|
|
ex = DeployMeta()
|
|
sys.exit(app.exec_())
|
|
|
|
print ex
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|