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_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_logs(cu): try: with open('dvls.log', 'r') as f: log = f.readlines() return json_response(data=log) except Exception as e: logger.error('No se ha podido leer el fichero de logs: %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_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 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 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 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 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 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)
async def exception_handler(request, exception): """ 意外错误 :param request: :param exception: :return: """ logger.error(traceback.format_exc()) return json_fail_response()
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 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 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 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 decorated(*args, **kwargs): token = None if 'JWT-Token' in request.headers: token = request.headers['JWT-Token'] if not token: logger.error( 'No se ha encontrado el token de sesión en las cabeceras') return json_response(status=401) try: data = jwt.decode(token, app.config['SECRET_KEY']) current_user = data['username'] except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] print(exc_type, fname, exc_tb.tb_lineno) logger.error('El token de sesión no es válido o ha caducado') return json_response(status=401) return f(current_user, *args, **kwargs)
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 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)
def login(): logger.debug('Autenticando usuario') try: auth = request.authorization if not auth or not auth.username or not auth.password: logger.error( 'No se ha podido autenticar al usuario: faltan las credenciales!' ) return json_response(status=400) p = pam.pam() # user = p.authenticate(username=auth.username, password=auth.password, service='dvls') user = p.authenticate(username=auth.username, password=auth.password) if user and (auth.username == app.config['CONN_USER']): token = jwt.encode( dict(username=auth.username, exp=datetime.utcnow() + timedelta(minutes=60)), app.config['SECRET_KEY']) return json_response(data=dict(token=token.decode('UTF-8'))) logger.error( 'No se ha podido autenticar al usuario: las credenciales son incorrectas!' ) return json_response(status=403) except Exception as e: logger.error('No se ha podido autenticar al usuario: %s', str(e)) return json_response(status=500)
def create_domain(self, data): 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') socketio = SocketIO(message_queue=app.config['CELERY_BROKER_URL']) socketio.emit( 'task-finished', { 'task_type': 'add-domain', 'task_id': self.request.id.__str__(), 'status': -1 }) return -1 try: subprocess.check_call(cmd) logger.info('Dominio creado') socketio = SocketIO(message_queue=app.config['CELERY_BROKER_URL']) socketio.emit( 'task-finished', { 'task_type': 'add-domain', 'task_id': self.request.id.__str__(), 'status': 0 }) return 0 except Exception as e: logger.error('No se ha podido crear el dominio: %s', str(e)) socketio = SocketIO(message_queue=app.config['CELERY_BROKER_URL']) socketio.emit( 'task-finished', { 'task_type': 'add-domain', 'task_id': self.request.id.__str__(), 'status': -1 }) return -1
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