diff --git a/monkey_business/cc/admin/ui/index.html b/monkey_business/cc/admin/ui/index.html
index 5705f9776..f85c8885f 100644
--- a/monkey_business/cc/admin/ui/index.html
+++ b/monkey_business/cc/admin/ui/index.html
@@ -129,26 +129,19 @@
-
-
-
-
-
+
diff --git a/monkey_business/cc/admin/ui/js/monkeysb-admin.js b/monkey_business/cc/admin/ui/js/monkeysb-admin.js
index 8c154298a..247381a0a 100644
--- a/monkey_business/cc/admin/ui/js/monkeysb-admin.js
+++ b/monkey_business/cc/admin/ui/js/monkeysb-admin.js
@@ -12,8 +12,8 @@ const ICONS_EXT = ".png";
var jobsTable = undefined;
var logsTable = undefined;
-var vcenterCfg = undefined;
var jobCfg = undefined;
+var conCfg = undefined;
var selectedJob = undefined;
JSONEditor.defaults.theme = 'bootstrap3';
@@ -41,73 +41,8 @@ function initAdmin() {
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(showLog, 5000);
- loadVcenterConfig();
updateJobs();
}
@@ -136,24 +71,43 @@ function updateJobs() {
var jobsList = json.objects;
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();
- //enableJobsSelect();
});
}
-function loadVcenterConfig() {
- $.getJSON('/connector?type=VCenterConnector', function(json) {
- vcenterCfg.setValue(json);
+function loadConnectorsConfig() {
+ elem = document.getElementById('connectors-config');
+ 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() {
- var vc_config = vcenterCfg.getValue()
- vc_config["type"] = "VCenterConnector";
-
+function updateConnectorConfig() {
+ var con_config = conCfg.getValue()
$.ajax({
headers : {
'Accept' : 'application/json',
@@ -161,9 +115,12 @@ function updateVcenterConfig() {
},
url : '/connector',
type : 'POST',
- data : JSON.stringify(vc_config),
+ data : JSON.stringify(con_config.connector),
success : function(response, textStatus, jqXhr) {
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) {
// log the error to the console
diff --git a/monkey_business/cc/connectors/__init__.py b/monkey_business/cc/connectors/__init__.py
index a6ae03932..113de9db4 100644
--- a/monkey_business/cc/connectors/__init__.py
+++ b/monkey_business/cc/connectors/__init__.py
@@ -42,8 +42,10 @@ class NetControllerConnector(object):
def set_logger(self, logger):
self.log = logger
+
class NetControllerJob(object):
connector_type = NetControllerConnector
+
_connector = None
_logger = None
@@ -65,6 +67,8 @@ class NetControllerJob(object):
if self._logger:
self._logger.log(text)
+ # external API
+
def get_job_properties(self):
return self._properties
@@ -80,4 +84,10 @@ class NetControllerJob(object):
raise NotImplementedError()
def get_results(self):
- return []
\ No newline at end of file
+ return []
+
+ def get_state(self):
+ return None
+
+ def stop(self):
+ raise NotImplementedError()
\ No newline at end of file
diff --git a/monkey_business/cc/connectors/vcenter.py b/monkey_business/cc/connectors/vcenter.py
index bdd9c062b..4ddbf3c2d 100644
--- a/monkey_business/cc/connectors/vcenter.py
+++ b/monkey_business/cc/connectors/vcenter.py
@@ -12,7 +12,6 @@ class VCenterConnector(NetControllerConnector):
"password": "",
"monkey_template_name": "",
"monkey_vm_info": {
- "name": "Monkey Test",
"datacenter_name": "",
"vm_folder": "",
"datastore_name": "",
diff --git a/monkey_business/cc/main.py b/monkey_business/cc/main.py
index afe03a646..8a1d37fcb 100644
--- a/monkey_business/cc/main.py
+++ b/monkey_business/cc/main.py
@@ -14,6 +14,7 @@ mongo = PyMongo(app)
active_connectors = {}
+
class Root(restful.Resource):
def get(self):
return {
@@ -25,7 +26,6 @@ class Root(restful.Resource):
class Job(restful.Resource):
def get(self, **kw):
id = request.args.get('id')
- timestamp = request.args.get('timestamp')
action = request.args.get('action')
if action == "log":
@@ -33,7 +33,7 @@ class Job(restful.Resource):
result = {}
- if (id):
+ if id:
return mongo.db.job.find_one_or_404({"_id": id})
else:
result['timestamp'] = datetime.now().isoformat()
@@ -69,9 +69,11 @@ class Connector(restful.Resource):
# if no type given - return list of types
if not contype:
conlist = []
+ checked_con = [] # used for easy checking for reoccurring connectors
for jobclass in available_jobs:
- if jobclass.connector_type.__name__ not in conlist:
- conlist.append(jobclass.connector_type.__name__)
+ if jobclass.connector_type.__name__ not in checked_con:
+ checked_con.append(jobclass.connector_type.__name__)
+ conlist.append({"title": jobclass.connector_type.__name__, "$ref": "/connector?type=" + jobclass.connector_type.__name__})
return {"oneOf": conlist}
con = get_connector_by_name(contype)
@@ -80,14 +82,34 @@ class Connector(restful.Resource):
properties = mongo.db.connector.find_one({"type": con.__class__.__name__})
if properties:
con.load_properties(properties)
- ret = con.get_properties()
- ret["password"] = "" # for better security, don't expose password
- return ret
+ con_prop = con.get_properties()
+ con_prop["password"] = "" # for better security, don't expose password
+
+ 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):
settings_json = json.loads(request.data)
contype = settings_json.get("type")
+ if not contype:
+ return {}
+
# preserve password if empty given
properties = mongo.db.connector.find_one({"type": contype})
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},
upsert=True)
+
class JobCreation(restful.Resource):
def get(self, **kw):
jobtype = request.args.get('type')
@@ -120,41 +143,32 @@ class JobCreation(restful.Resource):
job.load_job_properties(loaded_job.get("properties"))
if action == "delete":
- if loaded_job.get("execution")["state"] == "pending":
- mongo.db.job.remove({"_id": bson.ObjectId(jobid)})
- return {'status': 'ok'}
+ if loaded_job.get("state") == "pending":
+ res = mongo.db.job.remove({"_id": bson.ObjectId(jobid), "state": "pending"})
+ if res["nModified"] == 1:
+ return {'status': 'ok'}
+ else:
+ return {'status': 'error deleting'}
else:
return {'status': 'bad state'}
if job and job.connector_type.__name__ in active_connectors.keys():
- properties = {
- "type": {
+ job_prop = job.get_job_properties()
+ properties = _build_prop_dict(job_prop, job)
+
+ properties["type"] = {
"type": "enum",
"enum": [job.__class__.__name__],
"options": {"hidden": True}
}
- }
- if (jobid):
+
+ if jobid:
properties["_id"] = {
"type": "enum",
"enum": [jobid],
"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({
"title": "%s Job" % jobtype,
"type": "object",
@@ -191,19 +205,17 @@ class JobCreation(restful.Resource):
return {'status': 'failed'}
else:
- execution_state = {"taskid": "",
- "state" : "pending"}
new_job = {
"creation_time": datetime.now(),
"type": jobtype,
"properties": parsed_prop,
- "execution": execution_state,
+ "taskid": "",
+ "state" : "pending",
}
jobid = mongo.db.job.insert(new_job)
async = tasks_manager.run_task.delay(jobid)
- execution_state["taskid"] = async.id
mongo.db.job.update({"_id": jobid},
- {"$set": {"execution": execution_state}})
+ {"$set": {"taskid": async.id}})
return {'status': 'created'}
@@ -227,6 +239,29 @@ def normalize_obj(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):
obj = normalize_obj(obj)
resp = make_response(bson.json_util.dumps(obj), code)
diff --git a/monkey_business/cc/tasks_manager.py b/monkey_business/cc/tasks_manager.py
index 0b57b8bbc..03a82e537 100644
--- a/monkey_business/cc/tasks_manager.py
+++ b/monkey_business/cc/tasks_manager.py
@@ -33,13 +33,17 @@ class JobExecution(object):
def __init__(self, mongo, jobinfo):
self._mongo = mongo
self._jobinfo = jobinfo
- self.update_job_state("processing")
job_class = get_jobclass_by_name(self._jobinfo["type"])
con = job_class.connector_type()
refresh_connector_config(self._mongo, con)
self._job = job_class(con, self)
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):
return self._job
@@ -48,9 +52,8 @@ class JobExecution(object):
self._jobinfo = self._mongo.db.job.find_one({"_id": self._jobinfo["_id"]})
def update_job_state(self, state):
- self._jobinfo["execution"]["state"] = state
self._mongo.db.job.update({"_id": self._jobinfo["_id"]},
- {"$set": {"execution": self._jobinfo["execution"]}})
+ {"$set": {"state": state}})
def _log_resutls(self, res):
self._mongo.db.results.update({"jobid": self._jobinfo["_id"]},
@@ -65,13 +68,15 @@ class JobExecution(object):
def run(self):
self.log("Starting job")
- res = False
+
+ res = None
try:
res = self._job.run()
except Exception, e:
self.log("Exception raised while running: %s" % e)
self.update_job_state("error")
return False
+
if res:
self.log("Done job startup")
self.update_job_state("running")
@@ -95,6 +100,10 @@ class JobExecution(object):
@celery.task
def run_task(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})
if not job_info:
return False