#!/usr/bin/python3 """Agent for setting up accounts for sysdeploy clients""" # pylint: disable=subprocess-run-check,too-few-public-methods import os import sys import cherrypy import libvirt import json from multiprocessing.pool import ThreadPool pool = ThreadPool(processes=1) import uuid from jinja2 import Environment, FileSystemLoader PATH = os.path.dirname(os.path.abspath(__file__)) import configparser config = configparser.ConfigParser() config.read("config.ini") TEMPLATE_ENVIRONMENT = Environment( autoescape=False, loader=FileSystemLoader(os.path.join(PATH, "templates")), trim_blocks=False, ) # TEMPLATE_ENVIRONMENT.globals['STATIC_PREFIX'] = '/templates/' def render_template(template_filename, context): return TEMPLATE_ENVIRONMENT.get_template(template_filename).render(context) class Client: """Main manager class""" """ uris = [ #'qemu+ssh://pool@reserve.homedevops/system', #'qemu+ssh://root@check2.homedevops/system', #'qemu+ssh://root@check3.homedevops/system' 'qemu:///system' ] """ host_str = config["host"]["available_hosts"] uris = [ each.strip() for each in host_str.replace("'", "").replace('"', "").split(",") if each.strip() ] def __init__(self): self.status = {} self.connections = {} for i in self.uris: self.connections[i] = libvirt.open(i) @cherrypy.expose def getavailable(self): out = "" for n, c in self.connections.items(): # cmap = c.getCPUMap() # out += "CPUs: {}".format(str(cmap[0])) # out += "Available: {}".format(str(cmap[1])) nodeinfo = c.getInfo() mem = nodeinfo[1] cpus = nodeinfo[2] usedmem = 0 usedcpus = 0 domain_ids = c.listDomainsID() for did in domain_ids: dom = c.lookupByID(did) state, maxmem, mem2, cpus, cput = dom.info() usedmem += maxmem / 1024 usedcpus += cpus out += "{}: {}/{} CPUs free,{}MiB/{}MiB memory free
\n".format( n, cpus - usedcpus, cpus, str((mem - usedmem)), str(mem) ) return out @cherrypy.expose def create( self, name, iso_source, img_source, domain_type="kvm", memory_unit="MiB", memory=1024, current_memory=1024, vcpu=2, arch="x86_64", machine="pc-q35-4.2", offset="utc", emulator="/usr/bin/qemu-system-x86_64", ): """create a VM with given specs boot it and return the ID for future ref """ # TODO: Check host resources(cpus, mem, etc.) before creating a VM param = { "name": name, "uuid": str(uuid.uuid4()), "iso_source": iso_source, "img_source": img_source, "type": domain_type, "memory_unit": memory_unit, "memory": memory, "current_memory": current_memory, "vcpu": vcpu, "arch": arch, "machine": machine, "offset": offset, "emulator": emulator, } context = {"args": param} xml = render_template("config.tmpl.xml", context) # print(xml) host = self.connections[self.uris[0]] if host: async_result = pool.apply_async(self.__create_vm, (host, xml)) return async_result.get() return "No host available" def __create_vm(self, host, xml): try: vm = host.createXML(xml) return json.dumps({"uuid": vm.UUIDString(), "id": vm.ID(), "status": 1}) except Exception as e: return json.dumps({"err": str(e), "status": 0}) @cherrypy.expose def gethost(self): """ It'll show relevant info about all available hosts """ host_data = [] for i, each in enumerate(self.uris): tmp = {} c = self.connections[each] nodeinfo = c.getInfo() mem = nodeinfo[1] cpus = nodeinfo[2] usedmem = 0 usedcpus = 0 tmp["sn"] = i + 1 tmp["hostname"] = c.getHostname() tmp["status"] = "Alive" if c.isAlive() else "Dead" tmp["uri"] = c.getURI() tmp["cpu"] = {} tmp["mem"] = {} domain_ids = c.listDomainsID() for did in domain_ids: dom = c.lookupByID(did) state, maxmem, mem2, cpus2, cput = dom.info() usedmem += maxmem / 1024 usedcpus += cpus2 tmp["cpu"]["total"] = cpus tmp["cpu"]["free"] = cpus - usedcpus tmp["mem"]["total"] = mem tmp["mem"]["free"] = int(mem - usedmem) host_data.append(tmp) return render_template("host.html", {"host_data": host_data}) @cherrypy.expose def getguest(self): """ It'll show host-wise available guests and detailed info about them """ guest_data = [] cnt = 0 for i, each in enumerate(self.uris): c = self.connections[each] for did in c.listDomainsID(): dom = c.lookupByID(did) tmp = { "sn": cnt + 1, "guestname": dom.name(), "hostname": c.getHostname(), "id_": dom.ID(), "mem": int(dom.info()[1] / 1024), "cpu": dom.info()[3], "status": "Active" if dom.isActive() else "Inactive", "uuid": dom.UUIDString(), } guest_data.append(tmp) cnt += 1 return render_template("guest.html", {"guest_data": guest_data}) @cherrypy.expose def manage(self): # get available hosts from the database(if it's stored in one) # and return the updated template return render_template("manage.html", {}) @cherrypy.expose @cherrypy.tools.json_in() @cherrypy.tools.json_out() def query_host(self): # make ajax call to query host json_obj = cherrypy.request.json print(json_obj) import time time.sleep(1) # simulate delay return { "data": [json_obj["hostname"], 4, 4096, "10.56.0.1", "9C-35-5B-5F-4C-D7"], "gpu": True, "done": True, "msg": "", } @cherrypy.expose @cherrypy.tools.json_in() @cherrypy.tools.json_out() def add_host(self): # make ajax call to add host, maybe save it in a database json_obj = cherrypy.request.json print(json_obj) return {"done": True, "msg": ""} @cherrypy.expose @cherrypy.tools.json_in() @cherrypy.tools.json_out() def edit_host(self): # make ajax call to edit host. Update it in database json_obj = cherrypy.request.json print(json_obj) return {"done": True, "msg": ""} @cherrypy.expose @cherrypy.tools.json_in() @cherrypy.tools.json_out() def delete_host(self): # make ajax call to delete host from database json_obj = cherrypy.request.json print(json_obj) return {"done": True, "msg": ""} ROOT = Client() CONF = os.path.join(os.path.dirname(__file__), "site.conf") if __name__ == "__main__": # cherrypy.config.update(CONF) cherrypy.server.socket_host = config["server"].get("server_host", "127.0.0.1") cherrypy.server.socket_port = config["server"].getint("server_port", 8080) ENGINE = cherrypy.engine # cherrypy.process.plugins.Daemonizer(engine).subscribe() # cherrypy.process.plugins.DropPrivileges( # ENGINE, uid='cherrypy', gid='cherrypy').subscribe() cherrypy.tree.mount( Client(), "/", config={ "/": { "tools.staticdir.on": True, "tools.staticdir.dir": PATH + "/templates", } }, ) if hasattr(ENGINE, "signal_handler"): ENGINE.signal_handler.subscribe() if hasattr(ENGINE, "console_control_handler"): ENGINE.console_control_handler.subscribe() try: ENGINE.start() except Exception: # pylint: disable=broad-except sys.exit(1) else: ENGINE.block()