def stopWebsockify(cu): logger.info('Acabando la conexión de WebSockify') pid = request.json['pid'] kill_process(pid) global startedWebsockify startedWebsockify = False return json_response()
def get_all_domains(cu): logger.info('Obteniendo dominios') try: conn = libvirt.open(app.config['LOCAL_QEMU_URI']) domains = conn.listAllDomains() domains_list = list() for dom in domains: is_active = True if dom.isActive() == 1 else False uuid = dom.UUIDString() name = dom.name() os_type = dom.OSType() total_memory = round(dom.info()[1] / 1024, 2) # KB to MB used_memory = "-" if not is_active else round( dom.info()[2] / 1024, 2) # KB to MB memory = dict(total=total_memory, used=used_memory) vcpus = dom.info()[3] state = dom.info()[0] vnc_port = get_vnc_port(dom) domains_list.append( dict(uuid=uuid, name=name, is_active=is_active, os_type=os_type, memory=memory, vcpus=vcpus, state=state, vnc_port=vnc_port)) conn.close() return json_response(data=domains_list) except Exception as e: logger.error('No se pudo obtener los dominios: %s', str(e)) return json_response(status=500)
def get_hosts(cu): logger.info('Obteniendo los hosts') try: hosts = Host.get() return json_response(data=[h.to_dict() for h in hosts]) except Exception as e: logger.error('No se han podido obtener los hosts: %s', str(e)) return json_response(status=500)
def get_settings(cu): logger.info("Obteniendo parámetros") try: parameters = models.Config.get() return json_response(parameters) except Exception as e: logger.error('Error obteniendo la configuración: %s', e) return json_response(status=500)
def get_templates(cu): logger.info('Obteniendo plantillas') try: templates = Template.get() data = [t.to_dict() for t in templates] return json_response(data=data) except Exception as e: logger.error('No se pudo obtener las plantillas: %s', str(e)) return json_response(status=500)
def add_host(cu): logger.info('Añadiendo host') try: host = Host(request.json) host.save() return json_response() except Exception as e: logger.error('No se ha podido añadir el host: %s', str(e)) return json_response(status=500)
def get_labs(cu): logger.info('Obteniendo laboratorios') try: labs = Lab.get() data = [l.to_dict() for l in labs] return json_response(data=data) except Exception as e: logger.error('No se pudo obtener los laboratorios: %s', str(e)) return json_response(status=500)
def get_hosts(cu, lab_uuid): logger.info('Obteniendo los hosts del laboratorio %s', lab_uuid) try: lab = Lab.get(lab_uuid) hosts = lab.hosts return json_response(data=[h.to_dict() for h in hosts]) except Exception as e: logger.error('No se han podido obtener los hosts: %s', str(e)) return json_response(status=500)
def delete_host(cu, host_uuid): logger.info('Eliminando host %s', host_uuid) try: host = Host.get(host_uuid) Host.delete(host) return json_response() except Exception as e: logger.error('No se ha podido eliminar el host %s: %s', host_uuid, str(e))
def clone_template(cu, template_uuid): logger.info('Desplegando plantilla') try: template = Template.get(template_uuid).to_dict() domain_name = request.json['domain_name'] lab_uuid = request.json['lab_uuid'] lab = Lab.get(lab_uuid) hosts = lab.hosts if hosts.__len__() == 0: logger.error('El laboratorio no tiene ningún host asociado') return json_response(status=500) cmd = [ 'virt-clone', '--connect', app.config['LOCAL_QEMU_URI'], '--original-xml', template['xml_path'], '--name', domain_name ] for count in range(template['images_path'].__len__()): cmd.append('--file') cmd.append(app.config['DOMAIN_IMAGES_DIR'] + domain_name + '-disk' + str(count) + '.qcow2') ssh = paramiko.SSHClient() # Surrounds 'known_hosts' error ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) errors = list() for h in hosts: host = h.ip_address username = h.conn_user try: # NO PASSWORD!! Server SSH key is previously distributed among lab PCs ssh.connect(hostname=host.compressed, username=username, timeout=4) ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command( ' '.join(cmd)) errors = [b.rstrip() for b in ssh_stderr.readlines()] if len(errors) > 0: logger.error( 'No se pudo desplegar la plantilla en el host %s (%s)', h.code, h.ip_address.compressed) logger.error(e for e in errors) except Exception as e: logger.error( 'No se pudo desplegar la plantilla en el host %s (%s): %s', h.code, h.ip_address.compressed, str(e)) errors = True finally: ssh.close() if errors or len(errors) > 0: return json_response(status=500) return json_response() except Exception as e: logger.error('No se pudo desplegar la plantilla: %s', str(e)) return json_response(status=500)
def clear_logs(cu): logger.info('Limpiando el fichero de logs') try: logs_file = open('dvls.log', 'w') logs_file.truncate() logs_file.close() return json_response() except Exception as e: logger.error('No se ha podido limpiar el fichero de logs: %s', str(e)) return json_response(status=500)
def delete_lab(cu, lab_uuid): logger.info('Eliminando laboratorio') try: lab = Lab.get(lab_uuid) Lab.delete(lab) return json_response() except Exception as e: logger.error('No se pudo eliminar el laboratorio seleccionado: %s', str(e)) return json_response(status=500)
def update_lab(cu, lab_uuid): logger.info('Actualizando laboratorio %s', lab_uuid) try: lab = Lab.get(lab_uuid) lab.update(request.json) return json_response() except Exception as e: logger.error('No se pudo actualizar el laboratorio %s: %s', lab_uuid, e) return json_response(status=500)
def get_dashboard(): logger.info('Cargando dashboard') try: conn = libvirt.open(app.config['LOCAL_QEMU_URI']) host_info = get_host_details(conn) host_info['templates'] = len(Template.get()) host_info['labs'] = len(Lab.get()) conn.close() return json_response(data=host_info) except Exception as e: logger.error('No se pudo obtener el dashboard: %s', libvirt.virGetLastErrorMessage()) return json_response(status=500)
def delete_template(cu, template_uuid): logger.info('Eliminando plantilla') try: template = Template.get(template_uuid) for image_path in pickle.loads(template.images_path): subprocess.check_call(['rm', '-f', image_path]) subprocess.check_call(['rm', '-f', template.xml_path]) Template.delete(template) return json_response() except Exception as e: logger.error('No se pudo eliminar la plantilla: %s', str(e)) return json_response(status=500)
def reboot_domain(cu, domain_uuid): logger.info('Reiniciando dominio %s', domain_uuid) try: conn = libvirt.open(app.config['LOCAL_QEMU_URI']) domain = conn.lookupByUUIDString(domain_uuid) domain.reboot(libvirt.VIR_DOMAIN_REBOOT_DEFAULT) time.sleep(3) conn.close() return json_response() except Exception as e: logger.error('No se ha podido reiniciar el dominio %s: %s', domain_uuid, str(e)) return json_response(status=500)
def get_all_domains(cu): logger.info('Obteniendo dominios') try: conn = libvirt.open(app.config['LOCAL_QEMU_URI']) domains = conn.listAllDomains() domains_list = list() for dom in domains: is_active = True if dom.isActive() == 1 else False uuid = dom.UUIDString() name = dom.name() os_type = dom.OSType() total_memory = round(dom.info()[1] / 1024, 2) # KB to MB used_memory = "-" if not is_active else round( dom.info()[2] / 1024, 2) # KB to MB memory = dict(total=total_memory, used=used_memory) vcpus = dom.info()[3] state = dom.info()[0] vnc_port = get_vnc_port(dom) # ADDED SUPPORT FOR DISKS xml = ET.fromstring(dom.XMLDesc()) devices = xml.findall('devices/disk') disk = {} for d in devices: if d.get('device') == 'disk': file_path = d.find('source').get('file') disk_info = dom.blockInfo(file_path) disk['capacity'] = round(disk_info[0] / 1024 / 1024 / 1024, 2) disk['allocation'] = round( disk_info[1] / 1024 / 1024 / 1024, 2) disk['physical'] = round(disk_info[2] / 1024 / 1024 / 1024, 2) domains_list.append( dict(uuid=uuid, name=name, is_active=is_active, os_type=os_type, memory=memory, vcpus=vcpus, state=state, disk=disk, vnc_port=vnc_port)) conn.close() return json_response(data=domains_list) except Exception as e: logger.error('No se pudo obtener los dominios: %s', str(e)) return json_response(status=500)
def update_domain(cu, domain_uuid): logger.info('Actualizando el dominio %s', domain_uuid) data = request.json try: conn = libvirt.open(app.config['LOCAL_QEMU_URI']) dom = conn.lookupByUUIDString(domain_uuid) dom.setMaxMemory(int(data['memory']) * 1024) dom.setVcpusFlags( int(data['vcpus']), libvirt.VIR_DOMAIN_AFFECT_CONFIG | libvirt.VIR_DOMAIN_VCPU_MAXIMUM) dom.setVcpusFlags(int(data['vcpus'])) except Exception as e: logger.error("No se ha podido actualizar el dominio %s: %s", domain_uuid, str(e)) return json_response(status=200)
def create_lab(cu): logger.info('Añadiendo laboratorio') try: lab = Lab(request.json) ip_range = ip_range_to_list(lab.start_ip_range, lab.end_ip_range) for ip in ip_range: data = dict(code=lab.code + '_' + str(ip_range.index(ip)), ip_address=ip, conn_user='******', lab_uuid=lab.uuid) lab.add_host(Host(data)) lab.save() return json_response() except Exception as e: logger.error('No se pudo añadir el laboratorio: %s', str(e)) return json_response(status=500)
def start_domain(cu, domain_uuid): logger.info('Encendiendo dominio %s', domain_uuid) try: conn = libvirt.open(app.config['LOCAL_QEMU_URI']) domain = conn.lookupByUUIDString(domain_uuid) domain.create() time.sleep(3) domain = conn.lookupByUUIDString(domain_uuid) if domain.info()[0] != libvirt.VIR_DOMAIN_RUNNING: logger.error('No se pudo encender el dominio %s', domain_uuid) conn.close() return json_response(status=500) conn.close() return json_response() except Exception as e: logger.error('No se pudo encender el dominio %s: %s', domain_uuid, str(e)) return json_response(status=500)
def after_request(request, response): # 获取响应数据 try: response_data = json.loads(response.body.decode('utf-8')) except Exception: response_data = response.body log_info = { "response_body": str(response_data)[:5000] or "UnKnown Response Data", 'response_header': dict(response.headers), "url": request.url, "path": request.path, "method": request.method, 'http_state': response.status, } logger.info(log_info)
def startWebsockify(cu): logger.info('Iniciando conexión de WebSockify') global startedWebsockify global websockifyPid port = request.json['port'] conn_str = 'localhost:' + str(port) if (startedWebsockify): kill_process(websockifyPid) startedWebsockify = False proc = subprocess.Popen([ './app/api/tools/websockify/run', 'localhost:6080', conn_str, '--cert', '/etc/nginx/ssl/nginx.crt', '--key', '/etc/nginx/ssl/nginx.key' ]) websockifyPid = proc.pid time.sleep(2) startedWebsockify = True return json_response(data=proc.pid)
def shutdown_domain(cu, domain_uuid): logger.info('Apagando el dominio %s', domain_uuid) try: conn = libvirt.open(app.config['LOCAL_QEMU_URI']) domain = conn.lookupByUUIDString(domain_uuid) domain.destroy() time.sleep(3) domain = conn.lookupByUUIDString(domain_uuid) if domain.info()[0] != libvirt.VIR_DOMAIN_SHUTOFF: logger.error('No se pudo apagar el dominio %s', domain_uuid) conn.close() return json_response(status=500) conn.close() return json_response() except Exception as e: logger.error('No se ha podido apagar el dominio %s: %s', domain_uuid, str(e)) return json_response(status=500)
def create_domain(cu): logger.info('Creando dominio') data = request.json cmd = [ 'virt-install', '--connect', app.config['LOCAL_QEMU_URI'], '--name', data['name'], '--memory', str(data['memory']), '--vcpus', str(data['vcpus']), '--os-variant', data['os_variant'], '--noautoconsole' ] if data['graphics']['vnc']: cmd.append('--graphics') cmd.append('vnc,listen=' + data['graphics']['listen'] + ',password='******'graphics']['password']) if data['installation_type'] == "iso": cmd.append('--disk') cmd.append('size=' + str(data['disk']['size'])) cmd.append('--cdrom') cmd.append(data['cdrom']) elif data['installation_type'] == "image": cmd.append('--disk') cmd.append(data['disk']['path']) cmd.append('--import') elif data['installation_type'] == "network": cmd.append('--disk') cmd.append('size=' + str(data['disk']['size'])) cmd.append('--location') cmd.append(data['location']) elif data['installation_type'] == "pxe": cmd.append('--disk') cmd.append('size=' + str(data['disk']['size'])) cmd.append('--network') cmd.append(data['network']) cmd.append('--pxe') else: logger.warn('El método de instalación no es correcto') return json_response(status=400) try: subprocess.check_call(cmd) return json_response() except Exception as e: logger.error('No se ha podido crear el dominio: %s', str(e)) return json_response(status=500)
def before_request(request): # 获取请求参数 request_params = request.args if request.method == "POST": request_params = request.form if not request_params: try: request_params = request.json except Exception: request_params = {} log_info = { 'request_params': request_params, 'request_header': dict(request.headers), "url": request.url, "ip": request.ip, "method": request.method, } logger.info(log_info)
def delete_domain(cu, domain_uuid): logger.info('Eliminando dominio %s', domain_uuid) try: conn = libvirt.open(app.config['LOCAL_QEMU_URI']) domain = conn.lookupByUUIDString(domain_uuid) xml = ET.fromstring(domain.XMLDesc()) devices = xml.findall('devices/disk') for d in devices: if d.get('device') == 'disk': file_path = d.find('source').get('file') disk = conn.storageVolLookupByPath(file_path) disk.delete(libvirt.VIR_STORAGE_VOL_DELETE_NORMAL) domain.undefine() conn.close() return json_response() except Exception as e: logger.error('No se pudo eliminar el dominio %s: %s', domain_uuid, str(e)) return json_response(status=500)
# -*- coding: utf-8 -*- """ (C) Guangcai Ren blueprint_api All rights reserved create time '2019/9/16 10:21' Module usage: """ from app import create_app from app.core import logger app = create_app() workers = app.config.get('WORKERS') app.debug = app.config.get('DEBUG') if __name__ == '__main__': logger.info(""" _____ _ _____ _ _ _ / ____| (_) / ____| | | | | | | (___ __ _ _ __ _ ___ | (___ | |_ __ _ _ __| |_ | | \___ \ / _` | '_ \| |/ __| \___ \| __/ _` | '__| __| | | ____) | (_| | | | | | (__ ____) | || (_| | | | |_ |_| |_____/ \__,_|_| |_|_|\___| |_____/ \__\__,_|_| \__| (_) """) app.run(host="0.0.0.0", port=5000, workers=workers, auto_reload=False)
def clone_template(self, template_uuid, data): logger.info("Desplegando nueva plantilla") socketio = SocketIO(message_queue=app.config['CELERY_BROKER_URL']) try: template = Template.get(template_uuid).to_dict() domain_name = data['domain_name'] lab_uuid = data['lab_uuid'] lab = Lab.get(lab_uuid) hosts = lab.hosts if hosts.__len__() == 0: logger.error('El laboratorio no tiene ningún host asociado') socketio.emit('task-finished', { 'task_type': 'clone-template', 'task_id': self.request.id.__str__(), 'status': -1 }) return -1 cmd = ['virt-clone', '--connect', app.config['LOCAL_QEMU_URI'], '--original-xml', template['xml_path'], '--name', domain_name] for count in range(template['images_path'].__len__()): cmd.append('--file') cmd.append(app.config['DOMAIN_IMAGES_DIR'] + domain_name + '-disk' + str(count) + '.qcow2') ssh = paramiko.SSHClient() # Surrounds 'known_hosts' error ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) k = paramiko.RSAKey.from_private_key_file("/home/dvls/.ssh/id_rsa") errors = list() for h in hosts: host = h.ip_address username = h.conn_user logger.debug('Desplegando en el host %s, IP: %s', h.code, h.ip_address) try: # NO PASSWORD!! Server SSH key is previously distributed among lab PCs logger.debug('Conectando al equipo') ssh.connect(hostname=host.compressed, username=username, timeout=4, pkey=k) ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(' '.join(cmd)) errors = [b.rstrip() for b in ssh_stderr.readlines()] if len(errors) > 0: logger.error('No se pudo desplegar la plantilla en el host %s (%s)', h.code, h.ip_address.compressed) logger.error(cmd) logger.error(errors) except Exception as e: logger.error('No se pudo desplegar la plantilla en el host %s (%s): %s', h.code, h.ip_address.compressed, str(e)) errors = True finally: logger.debug('Desplegado en el equipo %s', h.code) ssh.close() if errors or len(errors) > 0: socketio.emit('task-finished', { 'task_type': 'clone-template', 'task_id': self.request.id.__str__(), 'status': -1 }) return -1 socketio.emit('task-finished', { 'task_type': 'clone-template', 'task_id': self.request.id.__str__(), 'status': 0 }) logger.info("Plantilla desplegada correctamente") return 0 except Exception as e: logger.error('No se pudo desplegar la plantilla: %s', str(e)) socketio.emit('task-finished', { 'task_type': 'clone-template', 'task_id': self.request.id.__str__(), 'status': -1 }) return -1
def create_template(self, data): socketio = SocketIO(message_queue=app.config['CELERY_BROKER_URL']) try: domain_uuid = data['domain_uuid'] template_name = data['template_name'] template_description = data['template_description'] do_sysprep = data['do_sysprep'] # Check domain state conn = libvirt.open(app.config['LOCAL_QEMU_URI']) domain = conn.lookupByUUIDString(domain_uuid) if domain.isActive(): logger.error('No se pudo crear la plantilla: el dominio debe estar apagado') socketio.emit('task-finished', { 'task_type': 'create-template', 'task_id': self.request.id.__str__(), 'status': -1 }) return -1 # Domain cloning (get disks paths and some hardware stuff) info = domain.info() memory = info[2] vcpus = info[3] xml = ET.fromstring(domain.XMLDesc()) devices = xml.findall('devices/disk') disks = list() for d in devices: if d.get('device') == 'disk': file_path = d.find('source').get('file') disks.append(file_path) cmd = ['virt-clone', '--connect', app.config['LOCAL_QEMU_URI'], '--original', domain.name(), '--name', template_name] template_images_path = list() for count in range(disks.__len__()): template_image_path = app.config['TEMPLATE_IMAGES_DIR'] + template_name + '-disk' + str(count) + '.qcow2' template_images_path.append(template_image_path) cmd.append('--file') cmd.append(template_image_path) subprocess.check_call(cmd) if do_sysprep: # Decontextualize the template and dumps XML --> USING POLICYKIT WITH 'virt-sysprep' subprocess.check_call(['pkexec', '/usr/bin/virt-sysprep', '--connect', app.config['LOCAL_QEMU_URI'], '--domain', template_name]) template_xml = str(app.config['TEMPLATE_DEFINITIONS_DIR'] + template_name + '.xml') proc = subprocess.Popen(['virsh', '--connect', app.config['LOCAL_QEMU_URI'], 'dumpxml', template_name], stdout=subprocess.PIPE) out = proc.stdout.read().decode('utf-8') # print(out) file = open(str(template_xml), 'w') file.write(out) file.close() # Undefine template template = conn.lookupByName(template_name) template.undefine() # Add template to database data = dict(name=template_name, description=template_description, vcpus=vcpus, memory=memory, xml_path=template_xml, images_path=template_images_path) template = Template(data) template.save() logger.info("Plantilla creada") socketio.emit('task-finished', { 'task_type': 'create-template', 'task_id': self.request.id.__str__(), 'status': 0 }) return 0 except Exception as e: # TODO - Delete template on fs if Exception is instance of sqlite3.OperationalError logger.error('No se pudo crear la plantilla: %s', str(e)) socketio.emit('task-finished', { 'task_type': 'create-template', 'task_id': self.request.id.__str__(), 'status': -1 }) return -1
def create_domain(cu): logger.info('Creando dominio') data = request.json task = domains.create_domain.delay(data) return json_response(data=task.task_id)