added job logging, successful vm clone
This commit is contained in:
parent
4103b30ba0
commit
9ad0c2046d
|
@ -56,6 +56,22 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- /.Jobs section -->
|
<!-- /.Jobs section -->
|
||||||
|
<!-- Log section -->
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<a href="#logs" data-toggle="collapse">Log</a>
|
||||||
|
</div>
|
||||||
|
<div id="logs" class="panel-body panel-collapse collapse in">
|
||||||
|
<table class="table table-bordered table-hover" id="logs-table">
|
||||||
|
<thead>
|
||||||
|
<tr><th>Time</th><th>Data</th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /.Log section -->
|
||||||
</div>
|
</div>
|
||||||
<!-- /.Main section -->
|
<!-- /.Main section -->
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,10 @@ const ICONS_EXT = ".png";
|
||||||
// If variable from local storage != null, assign it, otherwise set it's default value.
|
// If variable from local storage != null, assign it, otherwise set it's default value.
|
||||||
|
|
||||||
var jobsTable = undefined;
|
var jobsTable = undefined;
|
||||||
|
var logsTable = undefined;
|
||||||
var vcenterCfg = undefined;
|
var vcenterCfg = undefined;
|
||||||
var jobCfg = undefined;
|
var jobCfg = undefined;
|
||||||
|
var selectedJob = undefined;
|
||||||
|
|
||||||
JSONEditor.defaults.theme = 'bootstrap3';
|
JSONEditor.defaults.theme = 'bootstrap3';
|
||||||
|
|
||||||
|
@ -22,6 +24,9 @@ function initAdmin() {
|
||||||
"ordering": true,
|
"ordering": true,
|
||||||
"order": [[1, "desc"]],
|
"order": [[1, "desc"]],
|
||||||
});
|
});
|
||||||
|
logsTable = $("#logs-table").DataTable({
|
||||||
|
"ordering": false,
|
||||||
|
});
|
||||||
jobsTable.on( 'click', 'tr', function () {
|
jobsTable.on( 'click', 'tr', function () {
|
||||||
if ( $(this).hasClass('selected') ) {
|
if ( $(this).hasClass('selected') ) {
|
||||||
$(this).removeClass('selected');
|
$(this).removeClass('selected');
|
||||||
|
@ -31,7 +36,9 @@ function initAdmin() {
|
||||||
$(this).addClass('selected');
|
$(this).addClass('selected');
|
||||||
}
|
}
|
||||||
jobdata = jobsTable.row(this).data();
|
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'),{
|
vcenterCfg = new JSONEditor(document.getElementById('vcenter-config'),{
|
||||||
|
@ -98,12 +105,31 @@ function initAdmin() {
|
||||||
disable_properties: true,
|
disable_properties: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
window.setTimeout(updateJobs, 5000);
|
setInterval(updateJobs, 2000);
|
||||||
|
setInterval(showLog, 2000);
|
||||||
loadVcenterConfig();
|
loadVcenterConfig();
|
||||||
updateJobs();
|
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() {
|
function updateJobs() {
|
||||||
$.getJSON('/job', function(json) {
|
$.getJSON('/job', function(json) {
|
||||||
jobsTable.clear();
|
jobsTable.clear();
|
||||||
|
@ -150,9 +176,15 @@ function updateVcenterConfig() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function emptySelection() {
|
||||||
|
showLog();
|
||||||
|
selectedJob = undefined;
|
||||||
|
jobsTable.$('tr.selected').removeClass('selected');
|
||||||
|
}
|
||||||
|
|
||||||
function createNewJob(id, state) {
|
function createNewJob(id, state) {
|
||||||
if (!id) {
|
if (!id) {
|
||||||
jobsTable.$('tr.selected').removeClass('selected');
|
emptySelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
elem = document.getElementById('job-config');
|
elem = document.getElementById('job-config');
|
||||||
|
|
|
@ -6,8 +6,8 @@ available_jobs = [VCenterJob, DemoJob]
|
||||||
|
|
||||||
def get_connector_by_name(name):
|
def get_connector_by_name(name):
|
||||||
for jobclass in available_jobs:
|
for jobclass in available_jobs:
|
||||||
if name == jobclass.connector.__name__:
|
if name == jobclass.connector_type.__name__:
|
||||||
return jobclass.connector()
|
return jobclass.connector_type()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -36,9 +36,17 @@ class NetControllerConnector(object):
|
||||||
def disconnect(self):
|
def disconnect(self):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def log(self, text):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def set_logger(self, logger):
|
||||||
|
self.log = logger
|
||||||
|
|
||||||
class NetControllerJob(object):
|
class NetControllerJob(object):
|
||||||
connector = NetControllerConnector
|
connector_type = NetControllerConnector
|
||||||
|
_connector = None
|
||||||
|
_logger = None
|
||||||
|
|
||||||
_properties = {
|
_properties = {
|
||||||
# property: value
|
# property: value
|
||||||
}
|
}
|
||||||
|
@ -47,9 +55,15 @@ class NetControllerJob(object):
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, existing_connector = None):
|
def __init__(self, existing_connector=None, logger=None):
|
||||||
if existing_connector:
|
self._connector = existing_connector
|
||||||
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):
|
def get_job_properties(self):
|
||||||
return self._properties
|
return self._properties
|
||||||
|
|
|
@ -35,7 +35,7 @@ class DemoConnector(NetControllerConnector):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
class DemoJob(NetControllerJob):
|
class DemoJob(NetControllerJob):
|
||||||
connector = DemoConnector
|
connector_type = DemoConnector
|
||||||
_properties = {
|
_properties = {
|
||||||
"vlan": 0,
|
"vlan": 0,
|
||||||
}
|
}
|
||||||
|
@ -45,4 +45,5 @@ class DemoJob(NetControllerJob):
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
import time
|
import time
|
||||||
|
self.log("Running demo job...")
|
||||||
time.sleep(30)
|
time.sleep(30)
|
|
@ -63,20 +63,25 @@ class VCenterConnector(NetControllerConnector):
|
||||||
def get_entities_on_vlan(self, vlanid):
|
def get_entities_on_vlan(self, vlanid):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def deploy_monkey(self, vlanid, vm_name):
|
def deploy_monkey(self, vm_name):
|
||||||
if not self._properties["monkey_template_name"]:
|
if not self._properties["monkey_template_name"]:
|
||||||
raise Exception("Monkey template not configured")
|
raise Exception("Monkey template not configured")
|
||||||
|
|
||||||
|
if not self.is_connected():
|
||||||
|
self.connect()
|
||||||
|
|
||||||
vcontent = self._service_instance.RetrieveContent() # get updated vsphare state
|
vcontent = self._service_instance.RetrieveContent() # get updated vsphare state
|
||||||
monkey_template = self._get_obj(vcontent, [vim.VirtualMachine], self._properties["monkey_template_name"])
|
monkey_template = self._get_obj(vcontent, [vim.VirtualMachine], self._properties["monkey_template_name"])
|
||||||
if not monkey_template:
|
if not monkey_template:
|
||||||
raise Exception("Monkey template not found")
|
raise Exception("Monkey template not found")
|
||||||
|
|
||||||
task = self._clone_vm(vcontent, monkey_template, vm_name)
|
self.log("Cloning vm: (%s -> %s)" % (monkey_template, vm_name))
|
||||||
if not task:
|
monkey_vm = self._clone_vm(vcontent, monkey_template, vm_name)
|
||||||
|
if not monkey_vm:
|
||||||
raise Exception("Error deploying monkey VM")
|
raise Exception("Error deploying monkey VM")
|
||||||
|
self.log("Finished cloning")
|
||||||
|
|
||||||
monkey_vm = task.entity
|
return monkey_vm
|
||||||
|
|
||||||
def disconnect(self):
|
def disconnect(self):
|
||||||
Disconnect(self._service_instance)
|
Disconnect(self._service_instance)
|
||||||
|
@ -101,7 +106,7 @@ class VCenterConnector(NetControllerConnector):
|
||||||
else:
|
else:
|
||||||
datastore = self._get_obj(vcontent, [vim.Datastore], vm.datastore[0].info.name)
|
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"]:
|
if self._properties["monkey_vm_info"]["resource_pool"]:
|
||||||
resource_pool = self._get_obj(vcontent, [vim.ResourcePool], self._properties["monkey_vm_info"]["resource_pool"])
|
resource_pool = self._get_obj(vcontent, [vim.ResourcePool], self._properties["monkey_vm_info"]["resource_pool"])
|
||||||
else:
|
else:
|
||||||
|
@ -116,12 +121,12 @@ class VCenterConnector(NetControllerConnector):
|
||||||
clonespec = vim.vm.CloneSpec()
|
clonespec = vim.vm.CloneSpec()
|
||||||
clonespec.location = relospec
|
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)
|
task = vm.Clone(folder=destfolder, name=name, spec=clonespec)
|
||||||
return self._wait_for_task(task)
|
return self._wait_for_task(task)
|
||||||
|
|
||||||
|
def _wait_for_task(self, task):
|
||||||
@staticmethod
|
|
||||||
def _wait_for_task(task):
|
|
||||||
""" wait for a vCenter task to finish """
|
""" wait for a vCenter task to finish """
|
||||||
task_done = False
|
task_done = False
|
||||||
while not task_done:
|
while not task_done:
|
||||||
|
@ -129,6 +134,7 @@ class VCenterConnector(NetControllerConnector):
|
||||||
return task.info.result
|
return task.info.result
|
||||||
|
|
||||||
if task.info.state == 'error':
|
if task.info.state == 'error':
|
||||||
|
self.log("Error waiting for task: %s" % repr(task.info))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -153,9 +159,9 @@ class VCenterConnector(NetControllerConnector):
|
||||||
|
|
||||||
|
|
||||||
class VCenterJob(NetControllerJob):
|
class VCenterJob(NetControllerJob):
|
||||||
connector = VCenterConnector
|
connector_type = VCenterConnector
|
||||||
_properties = {
|
_properties = {
|
||||||
"vlan": 0,
|
"vlan": "",
|
||||||
"vm_name": "",
|
"vm_name": "",
|
||||||
}
|
}
|
||||||
_enumerations = {
|
_enumerations = {
|
||||||
|
@ -163,5 +169,9 @@ class VCenterJob(NetControllerJob):
|
||||||
}
|
}
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
pass
|
if not self._connector:
|
||||||
|
return False
|
||||||
|
|
||||||
|
monkey_vm = self._connector.deploy_monkey(self._properties["vm_name"])
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,13 @@ class Root(restful.Resource):
|
||||||
|
|
||||||
class Job(restful.Resource):
|
class Job(restful.Resource):
|
||||||
def get(self, **kw):
|
def get(self, **kw):
|
||||||
id = kw.get('id')
|
id = request.args.get('id')
|
||||||
timestamp = request.args.get('timestamp')
|
timestamp = request.args.get('timestamp')
|
||||||
|
action = request.args.get('action')
|
||||||
|
|
||||||
|
if action == "log":
|
||||||
|
return {"log": get_job_log(id)}
|
||||||
|
|
||||||
result = {}
|
result = {}
|
||||||
|
|
||||||
if (id):
|
if (id):
|
||||||
|
@ -65,8 +70,8 @@ class Connector(restful.Resource):
|
||||||
if not contype:
|
if not contype:
|
||||||
conlist = []
|
conlist = []
|
||||||
for jobclass in available_jobs:
|
for jobclass in available_jobs:
|
||||||
if jobclass.connector.__name__ not in conlist:
|
if jobclass.connector_type.__name__ not in conlist:
|
||||||
conlist.append(jobclass.connector.__name__)
|
conlist.append(jobclass.connector_type.__name__)
|
||||||
return {"oneOf": conlist}
|
return {"oneOf": conlist}
|
||||||
|
|
||||||
con = get_connector_by_name(contype)
|
con = get_connector_by_name(contype)
|
||||||
|
@ -101,7 +106,7 @@ class JobCreation(restful.Resource):
|
||||||
res = []
|
res = []
|
||||||
update_connectors()
|
update_connectors()
|
||||||
for con in available_jobs:
|
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__})
|
res.append({"title": con.__name__, "$ref": "/jobcreate?type=" + con.__name__})
|
||||||
return {"oneOf": res}
|
return {"oneOf": res}
|
||||||
|
|
||||||
|
@ -121,7 +126,7 @@ class JobCreation(restful.Resource):
|
||||||
else:
|
else:
|
||||||
return {'status': 'bad state'}
|
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 = {
|
properties = {
|
||||||
"type": {
|
"type": {
|
||||||
"type": "enum",
|
"type": "enum",
|
||||||
|
@ -148,7 +153,7 @@ class JobCreation(restful.Resource):
|
||||||
properties[prop]["type"] = "string"
|
properties[prop]["type"] = "string"
|
||||||
enum = job.get_property_function(prop)
|
enum = job.get_property_function(prop)
|
||||||
if enum:
|
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({
|
res = dict({
|
||||||
"title": "%s Job" % jobtype,
|
"title": "%s Job" % jobtype,
|
||||||
|
@ -229,11 +234,17 @@ def output_json(obj, code, headers=None):
|
||||||
return resp
|
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():
|
def update_connectors():
|
||||||
for con in available_jobs:
|
for con in available_jobs:
|
||||||
connector_name = con.connector.__name__
|
connector_name = con.connector_type.__name__
|
||||||
if connector_name not in active_connectors:
|
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():
|
if not active_connectors[connector_name].is_connected():
|
||||||
refresh_connector_config(mongo, active_connectors[connector_name])
|
refresh_connector_config(mongo, active_connectors[connector_name])
|
||||||
|
|
|
@ -23,6 +23,7 @@ fapp.config.from_object('dbconfig')
|
||||||
celery = make_celery(fapp)
|
celery = make_celery(fapp)
|
||||||
mongo = PyMongo(fapp)
|
mongo = PyMongo(fapp)
|
||||||
|
|
||||||
|
|
||||||
class JobExecution(object):
|
class JobExecution(object):
|
||||||
_jobinfo = None
|
_jobinfo = None
|
||||||
_job = None
|
_job = None
|
||||||
|
@ -35,9 +36,10 @@ class JobExecution(object):
|
||||||
self.update_job_state("processing")
|
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()
|
con = job_class.connector_type()
|
||||||
refresh_connector_config(self._mongo, con)
|
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):
|
def get_job(self):
|
||||||
return self._job
|
return self._job
|
||||||
|
@ -56,22 +58,27 @@ class JobExecution(object):
|
||||||
upsert=True)
|
upsert=True)
|
||||||
|
|
||||||
def log(self, text):
|
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"]},
|
self._mongo.db.results.update({"jobid": self._jobinfo["_id"]},
|
||||||
{"$set": {"log": self._log}},
|
{"$set": {"log": self._log}},
|
||||||
upsert=True)
|
upsert=True)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.log("Starting job")
|
self.log("Starting job")
|
||||||
|
res = False
|
||||||
try:
|
try:
|
||||||
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
|
||||||
self.log("done job startup")
|
if res:
|
||||||
self.update_job_state("running")
|
self.log("Done job startup")
|
||||||
return True
|
self.update_job_state("running")
|
||||||
|
else:
|
||||||
|
self.log("Job startup error")
|
||||||
|
self.update_job_state("error")
|
||||||
|
return res
|
||||||
|
|
||||||
def get_results(self):
|
def get_results(self):
|
||||||
self.log("Trying to get results")
|
self.log("Trying to get results")
|
||||||
|
@ -92,9 +99,15 @@ def run_task(jobid):
|
||||||
if not job_info:
|
if not job_info:
|
||||||
return False
|
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():
|
if not job_exec.get_job():
|
||||||
job_exec.update_job_state(job_info, "error")
|
job_exec.update_job_state("error")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not job_exec.run():
|
if not job_exec.run():
|
||||||
|
|
Loading…
Reference in New Issue