adding means to deploy and test deployment of meta files

new file:   bin/deploy_meta.py
	new file:   bin/test.py
This commit is contained in:
Ronny Abraham 2016-09-15 20:39:22 +03:00
parent 596b6e7da3
commit 4b0768c534
2 changed files with 568 additions and 0 deletions

478
bin/deploy_meta.py Normal file
View file

@ -0,0 +1,478 @@
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/configuration"
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, row, grid):
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', 1, grid)
self.add_widgetrow('PROJECT_IP', 2, grid)
self.add_widgetrow('BRANCH_NAME', 4, grid)
self.add_widgetrow('BRANCH_EXT', 5, grid)
self.add_widgetrow('BRANCH_USER', 6, grid)
self.add_widgetrow('BRANCH_GROUP', 7, grid)
self.add_widgetrow('DATABASE_IP', 8, grid)
self.add_widgetrow('DATABASE_PORT', 9, grid)
self.add_widgetrow('DATABASE_NAME', 10, grid)
self.add_widgetrow('DATABASE_ADMIN_NAME', 11, grid)
self.add_widgetrow('DATABASE_ADMIN_PASS', 12, grid)
self.add_widgetrow('DATABASE_USER_NAME', 13, grid)
self.add_widgetrow('DATABASE_USER_PASS', 14, grid)
self.add_widgetrow('DJANGO_IP', 15, grid)
self.add_widgetrow('DJANGO_PORT', 16, grid)
self.add_widgetrow('NGINX_PORT', 17, grid)
central = QtGui.QWidget()
central.setLayout(grid)
self.setCentralWidget(central)
self.setGeometry(300, 300, 350, 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()

90
bin/test.py Normal file
View file

@ -0,0 +1,90 @@
import ruamel.yaml
import os
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--file1', default='development.yml')
parser.add_argument('--file2', default='test.yml')
def findDiff(d1, d2, path=""):
for k in d1.keys():
if not (k in d2):
print path, ":"
print k + " as key not in d2", "\n"
else:
if type(d1[k]) is dict:
if path == "":
path = k
else:
path = path + "->" + k
findDiff(d1[k], d2[k], path)
else:
if d1[k] != d2[k]:
print path, ":"
print " - ", k, " : ", d1[k]
print " + ", k, " : ", d2[k]
def compare_dictionaries(dict_1, dict_2, dict_1_name, dict_2_name, path=""):
"""Compare two dictionaries recursively to find non mathcing elements
Args:
dict_1: dictionary 1
dict_2: dictionary 2
Returns:
"""
err = ''
key_err = ''
value_err = ''
old_path = path
for k in dict_1.keys():
path = old_path + "[%s]" % k
if not (k in dict_2):
key_err += "Key %s%s not in %s\n" % (
dict_2_name, path, dict_2_name)
else:
if isinstance(dict_1[k], dict) and isinstance(dict_2[k], dict):
err += compare_dictionaries(
dict_1[k], dict_2[k], 'd1', 'd2', path)
else:
if dict_1[k] != dict_2[k]:
value_err += "Value of %s%s (%s) not same as %s%s (%s)\n"\
% (dict_1_name,
path, dict_1[k], dict_2_name, path, dict_2[k])
for k in dict_2.keys():
path = old_path + "[%s]" % k
if not (k in dict_1):
key_err += "Key %s%s not in %s\n" % (
dict_2_name, path, dict_1_name)
return key_err + value_err + err
def create_path(path_str, delimiter="/"):
path = ''
for d in path_str.split(delimiter):
path = os.path.join(path, d)
return path
def main():
path_meta = create_path("../../meta/configuration")
args = parser.parse_args()
path_main = os.path.join(path_meta, args.file1)
path_test = os.path.join(path_meta, args.file2)
yaml_main = ruamel.yaml.load(file(path_main, 'r'))
yaml_test = ruamel.yaml.load(file(path_test, 'r'))
a = compare_dictionaries(yaml_main, yaml_test, 'main', 'test')
print a
main()