diff --git a/monkey_business/cc/admin/ui/index.html b/monkey_business/cc/admin/ui/index.html
index b938c8199..5705f9776 100644
--- a/monkey_business/cc/admin/ui/index.html
+++ b/monkey_business/cc/admin/ui/index.html
@@ -56,6 +56,22 @@
+
+
+
diff --git a/monkey_business/cc/admin/ui/js/monkeysb-admin.js b/monkey_business/cc/admin/ui/js/monkeysb-admin.js
index 0033d6beb..bbc3a6634 100644
--- a/monkey_business/cc/admin/ui/js/monkeysb-admin.js
+++ b/monkey_business/cc/admin/ui/js/monkeysb-admin.js
@@ -11,8 +11,10 @@ const ICONS_EXT = ".png";
// If variable from local storage != null, assign it, otherwise set it's default value.
var jobsTable = undefined;
+var logsTable = undefined;
var vcenterCfg = undefined;
var jobCfg = undefined;
+var selectedJob = undefined;
JSONEditor.defaults.theme = 'bootstrap3';
@@ -22,6 +24,9 @@ function initAdmin() {
"ordering": true,
"order": [[1, "desc"]],
});
+ logsTable = $("#logs-table").DataTable({
+ "ordering": false,
+ });
jobsTable.on( 'click', 'tr', function () {
if ( $(this).hasClass('selected') ) {
$(this).removeClass('selected');
@@ -31,7 +36,9 @@ function initAdmin() {
$(this).addClass('selected');
}
jobdata = jobsTable.row(this).data();
- createNewJob(jobdata[0], jobdata[3]);
+ selectedJob = jobdata[0];
+ createNewJob(selectedJob, jobdata[3]);
+ showLog(selectedJob);
} );
vcenterCfg = new JSONEditor(document.getElementById('vcenter-config'),{
@@ -98,12 +105,31 @@ function initAdmin() {
disable_properties: true,
});
- window.setTimeout(updateJobs, 5000);
+ setInterval(updateJobs, 2000);
+ setInterval(showLog, 2000);
loadVcenterConfig();
updateJobs();
}
+function showLog() {
+ logsTable.clear();
+
+ if (!selectedJob) {
+ return;
+ }
+
+ $.getJSON('/job?action=log&id=' + selectedJob, function(json) {
+ var logsList = json.log;
+ for (var i = 0; i < logsList.length; i++) {
+ logsTable.row.add([logsList[i][0], logsList[i][1]]);
+ }
+
+ logsTable.draw();
+
+ });
+}
+
function updateJobs() {
$.getJSON('/job', function(json) {
jobsTable.clear();
@@ -150,9 +176,15 @@ function updateVcenterConfig() {
}
+function emptySelection() {
+ showLog();
+ selectedJob = undefined;
+ jobsTable.$('tr.selected').removeClass('selected');
+}
+
function createNewJob(id, state) {
if (!id) {
- jobsTable.$('tr.selected').removeClass('selected');
+ emptySelection();
}
elem = document.getElementById('job-config');
diff --git a/monkey_business/cc/common.py b/monkey_business/cc/common.py
index 79822a492..04307233b 100644
--- a/monkey_business/cc/common.py
+++ b/monkey_business/cc/common.py
@@ -6,8 +6,8 @@ available_jobs = [VCenterJob, DemoJob]
def get_connector_by_name(name):
for jobclass in available_jobs:
- if name == jobclass.connector.__name__:
- return jobclass.connector()
+ if name == jobclass.connector_type.__name__:
+ return jobclass.connector_type()
return None
diff --git a/monkey_business/cc/connectors/__init__.py b/monkey_business/cc/connectors/__init__.py
index c1688d2f6..a6ae03932 100644
--- a/monkey_business/cc/connectors/__init__.py
+++ b/monkey_business/cc/connectors/__init__.py
@@ -36,9 +36,17 @@ class NetControllerConnector(object):
def disconnect(self):
return
+ def log(self, text):
+ pass
+
+ def set_logger(self, logger):
+ self.log = logger
class NetControllerJob(object):
- connector = NetControllerConnector
+ connector_type = NetControllerConnector
+ _connector = None
+ _logger = None
+
_properties = {
# property: value
}
@@ -47,9 +55,15 @@ class NetControllerJob(object):
}
- def __init__(self, existing_connector = None):
- if existing_connector:
- self.connector = existing_connector
+ def __init__(self, existing_connector=None, logger=None):
+ self._connector = existing_connector
+ self._logger = logger
+ if logger:
+ self._connector.set_logger(self.log)
+
+ def log(self, text):
+ if self._logger:
+ self._logger.log(text)
def get_job_properties(self):
return self._properties
diff --git a/monkey_business/cc/connectors/demo.py b/monkey_business/cc/connectors/demo.py
index e89ebce17..f32e0d2cd 100644
--- a/monkey_business/cc/connectors/demo.py
+++ b/monkey_business/cc/connectors/demo.py
@@ -35,7 +35,7 @@ class DemoConnector(NetControllerConnector):
return []
class DemoJob(NetControllerJob):
- connector = DemoConnector
+ connector_type = DemoConnector
_properties = {
"vlan": 0,
}
@@ -45,4 +45,5 @@ class DemoJob(NetControllerJob):
def run(self):
import time
+ self.log("Running demo job...")
time.sleep(30)
\ No newline at end of file
diff --git a/monkey_business/cc/connectors/vcenter.py b/monkey_business/cc/connectors/vcenter.py
index 0486ac396..44977d412 100644
--- a/monkey_business/cc/connectors/vcenter.py
+++ b/monkey_business/cc/connectors/vcenter.py
@@ -63,20 +63,25 @@ class VCenterConnector(NetControllerConnector):
def get_entities_on_vlan(self, vlanid):
return []
- def deploy_monkey(self, vlanid, vm_name):
+ def deploy_monkey(self, vm_name):
if not self._properties["monkey_template_name"]:
raise Exception("Monkey template not configured")
+ if not self.is_connected():
+ self.connect()
+
vcontent = self._service_instance.RetrieveContent() # get updated vsphare state
monkey_template = self._get_obj(vcontent, [vim.VirtualMachine], self._properties["monkey_template_name"])
if not monkey_template:
raise Exception("Monkey template not found")
- task = self._clone_vm(vcontent, monkey_template, vm_name)
- if not task:
+ self.log("Cloning vm: (%s -> %s)" % (monkey_template, vm_name))
+ monkey_vm = self._clone_vm(vcontent, monkey_template, vm_name)
+ if not monkey_vm:
raise Exception("Error deploying monkey VM")
+ self.log("Finished cloning")
- monkey_vm = task.entity
+ return monkey_vm
def disconnect(self):
Disconnect(self._service_instance)
@@ -101,7 +106,7 @@ class VCenterConnector(NetControllerConnector):
else:
datastore = self._get_obj(vcontent, [vim.Datastore], vm.datastore[0].info.name)
- # get vm target resoucepool
+ # get vm target resource pool
if self._properties["monkey_vm_info"]["resource_pool"]:
resource_pool = self._get_obj(vcontent, [vim.ResourcePool], self._properties["monkey_vm_info"]["resource_pool"])
else:
@@ -116,12 +121,12 @@ class VCenterConnector(NetControllerConnector):
clonespec = vim.vm.CloneSpec()
clonespec.location = relospec
+ self.log("Starting clone task with the following info: %s" % repr({"folder": destfolder, "name": name, "clonespec": clonespec}))
+
task = vm.Clone(folder=destfolder, name=name, spec=clonespec)
return self._wait_for_task(task)
-
- @staticmethod
- def _wait_for_task(task):
+ def _wait_for_task(self, task):
""" wait for a vCenter task to finish """
task_done = False
while not task_done:
@@ -129,6 +134,7 @@ class VCenterConnector(NetControllerConnector):
return task.info.result
if task.info.state == 'error':
+ self.log("Error waiting for task: %s" % repr(task.info))
return None
@staticmethod
@@ -153,9 +159,9 @@ class VCenterConnector(NetControllerConnector):
class VCenterJob(NetControllerJob):
- connector = VCenterConnector
+ connector_type = VCenterConnector
_properties = {
- "vlan": 0,
+ "vlan": "",
"vm_name": "",
}
_enumerations = {
@@ -163,5 +169,9 @@ class VCenterJob(NetControllerJob):
}
def run(self):
- pass
+ if not self._connector:
+ return False
+
+ monkey_vm = self._connector.deploy_monkey(self._properties["vm_name"])
+ return True
diff --git a/monkey_business/cc/main.py b/monkey_business/cc/main.py
index 1d18fda6b..afe03a646 100644
--- a/monkey_business/cc/main.py
+++ b/monkey_business/cc/main.py
@@ -24,8 +24,13 @@ class Root(restful.Resource):
class Job(restful.Resource):
def get(self, **kw):
- id = kw.get('id')
+ id = request.args.get('id')
timestamp = request.args.get('timestamp')
+ action = request.args.get('action')
+
+ if action == "log":
+ return {"log": get_job_log(id)}
+
result = {}
if (id):
@@ -65,8 +70,8 @@ class Connector(restful.Resource):
if not contype:
conlist = []
for jobclass in available_jobs:
- if jobclass.connector.__name__ not in conlist:
- conlist.append(jobclass.connector.__name__)
+ if jobclass.connector_type.__name__ not in conlist:
+ conlist.append(jobclass.connector_type.__name__)
return {"oneOf": conlist}
con = get_connector_by_name(contype)
@@ -101,7 +106,7 @@ class JobCreation(restful.Resource):
res = []
update_connectors()
for con in available_jobs:
- if con.connector.__name__ in active_connectors:
+ if con.connector_type.__name__ in active_connectors:
res.append({"title": con.__name__, "$ref": "/jobcreate?type=" + con.__name__})
return {"oneOf": res}
@@ -121,7 +126,7 @@ class JobCreation(restful.Resource):
else:
return {'status': 'bad state'}
- if job and job.connector.__name__ in active_connectors.keys():
+ if job and job.connector_type.__name__ in active_connectors.keys():
properties = {
"type": {
"type": "enum",
@@ -148,7 +153,7 @@ class JobCreation(restful.Resource):
properties[prop]["type"] = "string"
enum = job.get_property_function(prop)
if enum:
- properties[prop]["enum"] = list(active_connectors[job.connector.__name__].__getattribute__(enum)())
+ properties[prop]["enum"] = list(active_connectors[job.connector_type.__name__].__getattribute__(enum)())
res = dict({
"title": "%s Job" % jobtype,
@@ -229,11 +234,17 @@ def output_json(obj, code, headers=None):
return resp
+def get_job_log(jobid):
+ res = mongo.db.results.find_one({"jobid": bson.ObjectId(jobid)})
+ if res:
+ return res["log"]
+ return []
+
def update_connectors():
for con in available_jobs:
- connector_name = con.connector.__name__
+ connector_name = con.connector_type.__name__
if connector_name not in active_connectors:
- active_connectors[connector_name] = con.connector()
+ active_connectors[connector_name] = con.connector_type()
if not active_connectors[connector_name].is_connected():
refresh_connector_config(mongo, active_connectors[connector_name])
diff --git a/monkey_business/cc/tasks_manager.py b/monkey_business/cc/tasks_manager.py
index e3995a744..0b57b8bbc 100644
--- a/monkey_business/cc/tasks_manager.py
+++ b/monkey_business/cc/tasks_manager.py
@@ -23,6 +23,7 @@ fapp.config.from_object('dbconfig')
celery = make_celery(fapp)
mongo = PyMongo(fapp)
+
class JobExecution(object):
_jobinfo = None
_job = None
@@ -35,9 +36,10 @@ class JobExecution(object):
self.update_job_state("processing")
job_class = get_jobclass_by_name(self._jobinfo["type"])
- con = job_class.connector()
+ con = job_class.connector_type()
refresh_connector_config(self._mongo, con)
- self._job = job_class(con)
+ self._job = job_class(con, self)
+ self._job.load_job_properties(self._jobinfo["properties"])
def get_job(self):
return self._job
@@ -56,22 +58,27 @@ class JobExecution(object):
upsert=True)
def log(self, text):
- self._log.append("[%s] %s" % (datetime.now(), text))
+ self._log.append([datetime.now().isoformat(), text])
self._mongo.db.results.update({"jobid": self._jobinfo["_id"]},
{"$set": {"log": self._log}},
upsert=True)
def run(self):
self.log("Starting job")
+ res = False
try:
- self._job.run()
+ res = self._job.run()
except Exception, e:
self.log("Exception raised while running: %s" % e)
self.update_job_state("error")
return False
- self.log("done job startup")
- self.update_job_state("running")
- return True
+ if res:
+ self.log("Done job startup")
+ self.update_job_state("running")
+ else:
+ self.log("Job startup error")
+ self.update_job_state("error")
+ return res
def get_results(self):
self.log("Trying to get results")
@@ -92,9 +99,15 @@ def run_task(jobid):
if not job_info:
return False
- job_exec = JobExecution(mongo, job_info)
+ job_exec = None
+ try:
+ job_exec = JobExecution(mongo, job_info)
+ except Exception, e:
+ print "init JobExecution exception - ", e
+ return False
+
if not job_exec.get_job():
- job_exec.update_job_state(job_info, "error")
+ job_exec.update_job_state("error")
return False
if not job_exec.run():