Merge remote-tracking branch 'origin/master'

This commit is contained in:
acepace 2016-06-07 14:39:24 +03:00
commit 18ab440ec2
6 changed files with 135 additions and 132 deletions

View File

@ -129,26 +129,19 @@
</div> </div>
<div id="config" style="overflow: visible" class="panel-body panel-collapse collapse in" aria-expanded="true"> <div id="config" style="overflow: visible" class="panel-body panel-collapse collapse in" aria-expanded="true">
<span class="input-group-btn"> <span class="input-group-btn">
<button id="btnLoadVCenterConfig" class="btn btn-default" type="button" <button id="btnLoadConnectorsConfig" class="btn btn-default" type="button"
onclick="loadVcenterConfig()" style="margin-top:-4px"> onclick="loadConnectorsConfig()" style="margin-top:-4px">
Reload VCenter Config Change Connectors Config
</button>
<button id="btnUpdateVCenterConfig" class="btn btn-default" type="button"
onclick="updateVcenterConfig()" style="margin-top:-4px">
Save VCenter Config
</button> </button>
</span> </span>
<div id="vcenter-config"> <div id="connector-config-section">
</div> <div id="connectors-config">
<!--<span class="input-group-btn"> </div>
<button id="btnConfigLoad" style="display: none;" class="btn btn-default" type="button" <button id="btnSaveConnectorConfig" class="btn btn-default" type="button"
onclick="loadMonkeyConfig()" style="margin-top:-4px"> onclick="updateConnectorConfig()" style="margin-top:-4px; visibility: hidden"">
Load Save Conectors Config
</button> </button>
<button id="btnConfigUpdate" style="display: none;" class="btn btn-default" type="button" </div>
onclick="updateMonkeyConfig()" style="margin-top:-4px">
Update
</button>-->
</span> </span>
</div> </div>

View File

@ -12,8 +12,8 @@ const ICONS_EXT = ".png";
var jobsTable = undefined; var jobsTable = undefined;
var logsTable = undefined; var logsTable = undefined;
var vcenterCfg = undefined;
var jobCfg = undefined; var jobCfg = undefined;
var conCfg = undefined;
var selectedJob = undefined; var selectedJob = undefined;
JSONEditor.defaults.theme = 'bootstrap3'; JSONEditor.defaults.theme = 'bootstrap3';
@ -41,73 +41,8 @@ function initAdmin() {
showLog(selectedJob); showLog(selectedJob);
} ); } );
vcenterCfg = new JSONEditor(document.getElementById('vcenter-config'),{
schema: {
type: "object",
title: "vcenter",
properties: {
address: {
title: "Address",
type: "string",
},
port: {
title: "Port",
type: "integer",
},
username: {
title: "Username",
type: "string",
},
password: {
title: "Password",
type: "string",
},
monkey_template_name: {
title: "Monkey Template Name",
type: "string",
},
monkey_vm_info: {
title: "Monkey Creation VM",
type: "object",
properties: {
name: {
title: "Deployment Name",
type: "string",
},
vm_folder: {
title: "VM Folder (opt.)",
type: "string",
},
resource_pool: {
title: "Resource Pool (opt.)",
type: "string",
},
datacenter_name: {
title: "Datacenter (opt.)",
type: "string",
},
cluster_name: {
title: "Cluster (opt.)",
type: "string",
},
datastore_name: {
title: "Datastore (opt.)",
type: "string",
},
}
}
},
options: {
"collapsed": true
},
},
disable_edit_json: false,
disable_properties: true,
});
setInterval(updateJobs, 5000); setInterval(updateJobs, 5000);
setInterval(showLog, 5000); setInterval(showLog, 5000);
loadVcenterConfig();
updateJobs(); updateJobs();
} }
@ -136,24 +71,43 @@ function updateJobs() {
var jobsList = json.objects; var jobsList = json.objects;
for (var i = 0; i < jobsList.length; i++) { for (var i = 0; i < jobsList.length; i++) {
jobsTable.row.add([jobsList[i].id, jobsList[i].creation_time, jobsList[i].type,jobsList[i].execution.state, JSON.stringify(jobsList[i].properties)]); jobsTable.row.add([jobsList[i].id, jobsList[i].creation_time, jobsList[i].type,jobsList[i].state, JSON.stringify(jobsList[i].properties)]);
} }
jobsTable.draw(); jobsTable.draw();
//enableJobsSelect();
}); });
} }
function loadVcenterConfig() { function loadConnectorsConfig() {
$.getJSON('/connector?type=VCenterConnector', function(json) { elem = document.getElementById('connectors-config');
vcenterCfg.setValue(json); elem.innerHTML = ""
conCfg = new JSONEditor(elem,{
schema: {
type: "object",
title: "Connector",
properties: {
connector: {
title: "Type",
$ref: "/connector",
}
},
options: {
"collapsed": false
},
},
ajax: true,
disable_edit_json: false,
disable_collapse: true,
disable_properties: true,
no_additional_properties: true
});
conCfg.on('ready',function() {
document.getElementById("btnSaveConnectorConfig").style.visibility = "visible";
}); });
} }
function updateVcenterConfig() { function updateConnectorConfig() {
var vc_config = vcenterCfg.getValue() var con_config = conCfg.getValue()
vc_config["type"] = "VCenterConnector";
$.ajax({ $.ajax({
headers : { headers : {
'Accept' : 'application/json', 'Accept' : 'application/json',
@ -161,9 +115,12 @@ function updateVcenterConfig() {
}, },
url : '/connector', url : '/connector',
type : 'POST', type : 'POST',
data : JSON.stringify(vc_config), data : JSON.stringify(con_config.connector),
success : function(response, textStatus, jqXhr) { success : function(response, textStatus, jqXhr) {
console.log("New vcenter config successfully updated!"); console.log("New vcenter config successfully updated!");
document.getElementById("btnSaveConnectorConfig").style.visibility = "hidden";
elem = document.getElementById('connectors-config');
elem.innerHTML = ""
}, },
error : function(jqXHR, textStatus, errorThrown) { error : function(jqXHR, textStatus, errorThrown) {
// log the error to the console // log the error to the console

View File

@ -42,8 +42,10 @@ class NetControllerConnector(object):
def set_logger(self, logger): def set_logger(self, logger):
self.log = logger self.log = logger
class NetControllerJob(object): class NetControllerJob(object):
connector_type = NetControllerConnector connector_type = NetControllerConnector
_connector = None _connector = None
_logger = None _logger = None
@ -65,6 +67,8 @@ class NetControllerJob(object):
if self._logger: if self._logger:
self._logger.log(text) self._logger.log(text)
# external API
def get_job_properties(self): def get_job_properties(self):
return self._properties return self._properties
@ -81,3 +85,9 @@ class NetControllerJob(object):
def get_results(self): def get_results(self):
return [] return []
def get_state(self):
return None
def stop(self):
raise NotImplementedError()

View File

@ -12,7 +12,6 @@ class VCenterConnector(NetControllerConnector):
"password": "", "password": "",
"monkey_template_name": "", "monkey_template_name": "",
"monkey_vm_info": { "monkey_vm_info": {
"name": "Monkey Test",
"datacenter_name": "", "datacenter_name": "",
"vm_folder": "", "vm_folder": "",
"datastore_name": "", "datastore_name": "",

View File

@ -14,6 +14,7 @@ mongo = PyMongo(app)
active_connectors = {} active_connectors = {}
class Root(restful.Resource): class Root(restful.Resource):
def get(self): def get(self):
return { return {
@ -25,7 +26,6 @@ class Root(restful.Resource):
class Job(restful.Resource): class Job(restful.Resource):
def get(self, **kw): def get(self, **kw):
id = request.args.get('id') id = request.args.get('id')
timestamp = request.args.get('timestamp')
action = request.args.get('action') action = request.args.get('action')
if action == "log": if action == "log":
@ -33,7 +33,7 @@ class Job(restful.Resource):
result = {} result = {}
if (id): if id:
return mongo.db.job.find_one_or_404({"_id": id}) return mongo.db.job.find_one_or_404({"_id": id})
else: else:
result['timestamp'] = datetime.now().isoformat() result['timestamp'] = datetime.now().isoformat()
@ -69,9 +69,11 @@ class Connector(restful.Resource):
# if no type given - return list of types # if no type given - return list of types
if not contype: if not contype:
conlist = [] conlist = []
checked_con = [] # used for easy checking for reoccurring connectors
for jobclass in available_jobs: for jobclass in available_jobs:
if jobclass.connector_type.__name__ not in conlist: if jobclass.connector_type.__name__ not in checked_con:
conlist.append(jobclass.connector_type.__name__) checked_con.append(jobclass.connector_type.__name__)
conlist.append({"title": jobclass.connector_type.__name__, "$ref": "/connector?type=" + jobclass.connector_type.__name__})
return {"oneOf": conlist} return {"oneOf": conlist}
con = get_connector_by_name(contype) con = get_connector_by_name(contype)
@ -80,14 +82,34 @@ class Connector(restful.Resource):
properties = mongo.db.connector.find_one({"type": con.__class__.__name__}) properties = mongo.db.connector.find_one({"type": con.__class__.__name__})
if properties: if properties:
con.load_properties(properties) con.load_properties(properties)
ret = con.get_properties() con_prop = con.get_properties()
ret["password"] = "" # for better security, don't expose password con_prop["password"] = "" # for better security, don't expose password
return ret
properties = _build_prop_dict(con_prop)
properties["type"] = {
"type": "enum",
"enum": [contype],
"options": {"hidden": True}
}
res = dict({
"title": "%s Connector" % contype,
"type": "object",
"options": {
"disable_collapse": True,
"disable_properties": True,
},
"properties": properties
})
return res
def post(self, **kw): def post(self, **kw):
settings_json = json.loads(request.data) settings_json = json.loads(request.data)
contype = settings_json.get("type") contype = settings_json.get("type")
if not contype:
return {}
# preserve password if empty given # preserve password if empty given
properties = mongo.db.connector.find_one({"type": contype}) properties = mongo.db.connector.find_one({"type": contype})
if properties and (not settings_json.has_key("password") or not settings_json["password"]): if properties and (not settings_json.has_key("password") or not settings_json["password"]):
@ -97,6 +119,7 @@ class Connector(restful.Resource):
{"$set": settings_json}, {"$set": settings_json},
upsert=True) upsert=True)
class JobCreation(restful.Resource): class JobCreation(restful.Resource):
def get(self, **kw): def get(self, **kw):
jobtype = request.args.get('type') jobtype = request.args.get('type')
@ -120,41 +143,32 @@ class JobCreation(restful.Resource):
job.load_job_properties(loaded_job.get("properties")) job.load_job_properties(loaded_job.get("properties"))
if action == "delete": if action == "delete":
if loaded_job.get("execution")["state"] == "pending": if loaded_job.get("state") == "pending":
mongo.db.job.remove({"_id": bson.ObjectId(jobid)}) res = mongo.db.job.remove({"_id": bson.ObjectId(jobid), "state": "pending"})
return {'status': 'ok'} if res["nModified"] == 1:
return {'status': 'ok'}
else:
return {'status': 'error deleting'}
else: else:
return {'status': 'bad state'} return {'status': 'bad state'}
if job and job.connector_type.__name__ in active_connectors.keys(): if job and job.connector_type.__name__ in active_connectors.keys():
properties = { job_prop = job.get_job_properties()
"type": { properties = _build_prop_dict(job_prop, job)
properties["type"] = {
"type": "enum", "type": "enum",
"enum": [job.__class__.__name__], "enum": [job.__class__.__name__],
"options": {"hidden": True} "options": {"hidden": True}
} }
}
if (jobid): if jobid:
properties["_id"] = { properties["_id"] = {
"type": "enum", "type": "enum",
"enum": [jobid], "enum": [jobid],
"name": "ID", "name": "ID",
} }
job_prop = job.get_job_properties()
for prop in job_prop:
properties[prop] = dict({})
properties[prop]["default"] = job_prop[prop]
if type(job_prop[prop]) is int:
properties[prop]["type"] = "number"
elif type(job_prop[prop]) is bool:
properties[prop]["type"] = "boolean"
else:
properties[prop]["type"] = "string"
enum = job.get_property_function(prop)
if enum:
properties[prop]["enum"] = list(active_connectors[job.connector_type.__name__].__getattribute__(enum)())
res = dict({ res = dict({
"title": "%s Job" % jobtype, "title": "%s Job" % jobtype,
"type": "object", "type": "object",
@ -191,19 +205,17 @@ class JobCreation(restful.Resource):
return {'status': 'failed'} return {'status': 'failed'}
else: else:
execution_state = {"taskid": "",
"state" : "pending"}
new_job = { new_job = {
"creation_time": datetime.now(), "creation_time": datetime.now(),
"type": jobtype, "type": jobtype,
"properties": parsed_prop, "properties": parsed_prop,
"execution": execution_state, "taskid": "",
"state" : "pending",
} }
jobid = mongo.db.job.insert(new_job) jobid = mongo.db.job.insert(new_job)
async = tasks_manager.run_task.delay(jobid) async = tasks_manager.run_task.delay(jobid)
execution_state["taskid"] = async.id
mongo.db.job.update({"_id": jobid}, mongo.db.job.update({"_id": jobid},
{"$set": {"execution": execution_state}}) {"$set": {"taskid": async.id}})
return {'status': 'created'} return {'status': 'created'}
@ -227,6 +239,29 @@ def normalize_obj(obj):
return obj return obj
def _build_prop_dict(properties, job_obj=None):
res = dict()
for prop in properties:
res[prop] = dict({})
res[prop]["default"] = properties[prop]
if type(properties[prop]) is int:
res[prop]["type"] = "number"
elif type(properties[prop]) is bool:
res[prop]["type"] = "boolean"
elif type(properties[prop]) is dict:
res[prop]["type"] = "object"
res[prop]["properties"] = _build_prop_dict(properties[prop], job_obj)
else:
res[prop]["type"] = "string"
if job_obj:
enum = job_obj.get_property_function(prop)
if enum:
res[prop]["enum"] = list(
active_connectors[job_obj.connector_type.__name__].__getattribute__(enum)())
return res
def output_json(obj, code, headers=None): def output_json(obj, code, headers=None):
obj = normalize_obj(obj) obj = normalize_obj(obj)
resp = make_response(bson.json_util.dumps(obj), code) resp = make_response(bson.json_util.dumps(obj), code)

View File

@ -33,13 +33,17 @@ class JobExecution(object):
def __init__(self, mongo, jobinfo): def __init__(self, mongo, jobinfo):
self._mongo = mongo self._mongo = mongo
self._jobinfo = jobinfo self._jobinfo = jobinfo
self.update_job_state("processing")
job_class = get_jobclass_by_name(self._jobinfo["type"]) job_class = get_jobclass_by_name(self._jobinfo["type"])
con = job_class.connector_type() con = job_class.connector_type()
refresh_connector_config(self._mongo, con) refresh_connector_config(self._mongo, con)
self._job = job_class(con, self) self._job = job_class(con, self)
self._job.load_job_properties(self._jobinfo["properties"]) self._job.load_job_properties(self._jobinfo["properties"])
prev_log = self._mongo.db.results.find_one({"jobid": self._jobinfo["_id"]})
if prev_log:
self._log = prev_log["log"]
else:
self._log = []
def get_job(self): def get_job(self):
return self._job return self._job
@ -48,9 +52,8 @@ class JobExecution(object):
self._jobinfo = self._mongo.db.job.find_one({"_id": self._jobinfo["_id"]}) self._jobinfo = self._mongo.db.job.find_one({"_id": self._jobinfo["_id"]})
def update_job_state(self, state): def update_job_state(self, state):
self._jobinfo["execution"]["state"] = state
self._mongo.db.job.update({"_id": self._jobinfo["_id"]}, self._mongo.db.job.update({"_id": self._jobinfo["_id"]},
{"$set": {"execution": self._jobinfo["execution"]}}) {"$set": {"state": state}})
def _log_resutls(self, res): def _log_resutls(self, res):
self._mongo.db.results.update({"jobid": self._jobinfo["_id"]}, self._mongo.db.results.update({"jobid": self._jobinfo["_id"]},
@ -65,13 +68,15 @@ class JobExecution(object):
def run(self): def run(self):
self.log("Starting job") self.log("Starting job")
res = False
res = None
try: try:
res = self._job.run() res = self._job.run()
except Exception, e: except Exception, e:
self.log("Exception raised while running: %s" % e) self.log("Exception raised while running: %s" % e)
self.update_job_state("error") self.update_job_state("error")
return False return False
if res: if res:
self.log("Done job startup") self.log("Done job startup")
self.update_job_state("running") self.update_job_state("running")
@ -95,6 +100,10 @@ class JobExecution(object):
@celery.task @celery.task
def run_task(jobid): def run_task(jobid):
print "searching for ", jobid print "searching for ", jobid
aquire_task = mongo.db.job.update({"_id": jobid, "state": "pending"}, {"$set": {"state": "processing"}})
if aquire_task["nModified"] != 1:
return False
job_info = mongo.db.job.find_one({"_id": jobid}) job_info = mongo.db.job.find_one({"_id": jobid})
if not job_info: if not job_info:
return False return False