diff --git a/power_vm.py b/power_vm.py new file mode 100644 index 0000000..0400a8e --- /dev/null +++ b/power_vm.py @@ -0,0 +1,392 @@ +import requests +import urllib3 +import sys +import time +import threading +import logging +import os +import errno + +from concurrent.futures import ThreadPoolExecutor +from dotenv import load_dotenv +from vmware.vapi.vsphere.client import create_vsphere_client +from com.vmware.vcenter_client import VM +from com.vmware.vcenter.vm_client import Power as PowerHardware +from com.vmware.vcenter.vm.guest_client import Power as PowerGuest + +# Variables +wait_power_on = 60 # Tiempo de espera entre grupos de máquinas +wait_power_off = 60 # Tiempo de espera entre grupos de máquinas +wait_tools_on = 120 # Tiempo de espera para que las tools esten activas +wait_tools_off = 120 # Tiempo de espera para que las tools esten apagadas +timeout_on = 1200 # Tiempo de espera para que se de la máquina por encendida +timeout_off = 1200 # Tiempo de espera hasta forzar el apagado de la maquina +operation = "none" # Modo de operación: encendido o apagado +# Grupo de máquinas sobre las que se aplicará el modo de energía (NOMBRE) +group_selected = "none" +vms = "none" # Máquinas que se van a utilizar en la ejecución del script + +# TEST +vms_test_g1 = ["test1", "test2"] +vms_test_g2 = ["test3", "test4", "test5"] +vms_test_g3 = ["test6", "test7", "test8", "test9", "test10"] + +# TEST2 +vms_test_g4 = ["test1", "test2", "test3", "test4", + "test5", "test6", "test7", "test8", "test9", "test10"] + +# PRODUCCIÓN +vm_sin_grupo = ["alarmas_ayto", "alarmas_cpd", "cita_previa", "daloradius_biblioteca", "deep_freeze2", "editrans2", "edu_latorre", "gestion_switches", "impresoras_ricoh", "inattend2", "lucia", "micollab-mbg", "micollab9.4", "museo_domus", "ofimatico2007", "ofimatico2019", + "pulse_secure", "tarificador", "tc_aplicaciones", "tc_balanceador", "tc_bbdd", "tc_wserver", "untangle_biblioteca", "web_pre", "webdmz", "wifimovi", "tao_consultores1", "tao_consultores2", "tao_consultores3", "temporizador_pleno", "wazuh4.3.6"] +vm_apagadas = ["adnperros", "autocad2010", "control_biblio", "gisabsis", "glpi", "gwdmz15", "helpdesk", + "inventario_igs", "oes2018_pruebas", "open_erp", "pacmer", "presencia", "severino", "verticales_telefonica_camaras"] +vm_grupo_0 = ["datacore-cpd", "datacore-pol", "vcsa7"] +vm_grupo_1 = ["sacrista", "paco", "synology", "hp1440"] +vm_grupo_2 = ["torero", "oraserver19", "oraserver19_pre", + "ndsmaster", "storeonce1", "storeonce2"] +vm_grupo_3 = ["ayto_web_int", "documentum2016", "geoserver_lan", "peseta2", + "taoactuate", "abaco", "intranet2016", "intranet2_pre", "petrolio2"] +vm_grupo_4 = ["ayto_web_ext2", "geoserver_pre", + "tao1", "tao1_pre", "tirisiti2"] +vm_grupo_5 = ["clasico", "gw01-19", "gw02-19", "geoserver_pro", "otrs", "taoapps1_wildfly", "taoapps2_wildfly", "taoapps3_wildfly", "taoapps_pre_wildfly", + "tereseta2", "fortianalyzer", "fortimail", "veeam", "taobalanceador_wildfly", "taosede_9", "taosede_9_pre", "filr4"] +vm_grupo_6 = ["datasync_gms15", "gwdmz"] +vm_grupo_7 = ["centreon2", "zabbix"] + +# ORACLE +vm_oracle_0 = ["oraserver19", "oraserver19_pre"] +vm_oracle_1 = ["ayto_web_int", "documentum2016", "geoserver_lan", "peseta2", + "taoactuate", "abaco", "intranet2016", "intranet2_pre"] +vm_oracle_2 = ["ayto_web_ext2", "geoserver_pre", "tao1", "tao1_pre"] +vm_oracle_3 = ["geoserver_pro", "taoapps1_wildfly", "taoapps2_wildfly", "taoapps3_wildfly", + "taoapps_pre_wildfly", "taobalanceador_wildfly", "taosede_9", "taosede_9_pre"] + +# GEOSERVERS +g1 = ["geoserver_lan"] +g2 = ["geoserver_pre"] +g3 = ["geoserver_pro"] + +# Orden de encendido de las máquinas de test +vms_test1 = [vms_test_g1, vms_test_g2, vms_test_g3] +vms_test2 = [vms_test_g4] +vms_oracle = [vm_oracle_0, vm_oracle_1, vm_oracle_2, vm_oracle_3] +vms_produccion = [vm_grupo_2, vm_grupo_3, vm_grupo_4, + vm_grupo_5, vm_grupo_6, vm_sin_grupo, vm_grupo_7] +vms_geo = [g1, g2, g3] + +# Listados con los diferentes grupos de máquinas +groups = [vms_test1, vms_test2, vms_oracle, vms_produccion, vms_geo] +group_names = ["TEST1", "TEST2", "ORACLE", "PRODUCCION", "GEOSERVERS"] + +def get_vm(client, vm_name): + # Return the identifier of a vm + # Note: The method assumes that there is only one vm with the mentioned name. + names = set([vm_name]) + vms = client.vcenter.VM.list(VM.FilterSpec(names=names)) + + if len(vms) == 0: + return None + + vm = vms[0].vm + return vm + + +def power_off_vm(client, vm, name): + # Si la maquina existe + if vm != None: + # Obtiene el estado (encendida o apagada) + status = client.vcenter.vm.Power.get(vm) + timeout_shutdown = time.time() + timeout_off + + # Repite hasta que se apague o se acabe el tiempo + while status.state == PowerHardware.State.POWERED_ON: + status = client.vcenter.vm.Power.get(vm) + guest_status = client.vcenter.vm.guest.Power.get(vm).state + # El sistema operativo está funcionando + if guest_status == PowerGuest.State.RUNNING: + tools = client.vcenter.vm.guest.Power.get(vm).operations_ready + # Las tools estan operativas + if tools == True: + logging.info("%s: Enviada señal de apagado", name) + client.vcenter.vm.guest.Power.shutdown(vm) + # Espera hasta que las tools ya no esten disponibles o salte el time out + timeout_tools = time.time() + wait_tools_off + while tools == True: + tools = client.vcenter.vm.guest.Power.get( + vm).operations_ready + time.sleep(1) + if time.time() > timeout_tools: + logging.info("%s: Timeout tools", name) + break + logging.info("%s: Apagandose ...", name) + time.sleep(5) + if time.time() > timeout_shutdown: + logging.info("%s: Forzando apagado ...", name) + client.vcenter.vm.power.stop(vm) + break + logging.info("%s: Apagada", name) + return True + + # Si la máquina no existe + else: + logging.info("%s: No existe", name) + return False + + +def power_on_vm(client, vm, name): + # Si la maquina existe + if vm != None: + # Obtiene el estado (encendida o apagada) + status = client.vcenter.vm.Power.get(vm) + + # Repite hasta que se encienda + while status.state == PowerHardware.State.POWERED_OFF: + client.vcenter.vm.Power.start(vm) + logging.info("%s: Enviada señal de encendido", name) + + # Espera hasta que las tools esten disponibles o salte el time out + timeout_tools = time.time() + wait_tools_on + tools = client.vcenter.vm.guest.Power.get(vm).operations_ready + while tools == False: + time.sleep(5) + tools = client.vcenter.vm.guest.Power.get(vm).operations_ready + logging.info("%s: Encendiendose", name) + if time.time() > timeout_tools: + logging.info("%s: Timeout tools", name) + break + + # Comprueba el estado + status = client.vcenter.vm.Power.get(vm) + + # La máquina ya está encendida + logging.info("%s: Encendida", name) + return True + + # Si la máquina no existe + else: + logging.warning("%s: no existe", name) + return False + + +def printHelp(prog): + print("\nFUNCIONAMIENTO") + print("\tpython3 " + prog + " OPCIONES\n") + + print("OPCIONES") + print("\t -h, --help:") + print("\t\tMuestra esta ayuda\n") + + print("\t -l, --list:") + print("\t\tLista los posibles grupos de maquinas virtuales para operar\n") + + print("\t -l, --list NOMBRE_GRUPO:") + print("\t\tLista los nombres de las máquinas virtuales que pertenecen al grupo NOMBRE_GRUPO\n") + + print("\t -g, --group NOMBRE_GRUPO:") + print("\t\tEstablece el grupo de maquinas virtuales sobre el cual se aplicará el modo de energía\n") + + print("\t -p, --power on|off:") + print("\t\tIndica el modo de operación de energía que se aplicará a las máquians virtuales") + print("\t\t--power on: Enciende las máquinas virtuales del grupo seleccionado") + print("\t\t--power off: Apaga las máquinas virtuales del grupo seleccionado\n") + + print("EJEMPLO") + print("\tpython3 " + prog + " --power on --group ORACLE\n") + + +def checkArgs(args): + global operation + operation = "none" + global group_selected + group_selected = "none" + global vms + vms = "none" + + for index in range(0, len(args)): + # POWER + if args[index] == "--power" or args[index] == "-p": + if index < len(args)-1: + if args[index+1] == "on": + operation = "on" + elif args[index+1] == "off": + operation = "off" + + # LIST + if args[index] == "--list" or args[index] == "-l": + operation = "none" + # Comprueba que el argumento list es el último o no contiene un nombre de grupo válido detras + if index == len(args)-1 or (index <= len(args)-1 and args[index+1] not in group_names): + print("Los grupos de máquinas disponibles son los siguientes:") + for group in group_names: + print("- "+group) + + # Comprueba que el argumento posterior pertenece a un grupo + if index < len(args)-1: + if args[index+1] in group_names: + num = group_names.index(args[index+1]) + num_group = 1 + for group in groups[num]: + print("Grupo", num_group) + num_group += 1 + for mv in group: + print("\t" + mv) + + # GROUP + if args[index] == "--group" or args[index] == "-g": + group_selected = "none" + # Comprueba que el argumento posterior pertenece a un grupo + if index < len(args)-1: + if args[index+1] in group_names: + group_selected = args[index+1] + num = group_names.index(group_selected) + vms = groups[num] + + # HELP + if args[index] == "--help" or args[index] == "-h": + operation = "none" + printHelp(args[0]) + + if len(args) == 1: + operation = "none" + printHelp(args[0]) + + +def main(): + # Comprueba los argumentos + checkArgs(sys.argv) + print("operation", operation) + vms = vms_test1 + print("vms", vms) + + # ~Si no tiene los parametros necesarios, sale del programa + if (operation == "none" or vms == "none"): + sys.exit() + + # Crea el directorio para los logs + try: + os.mkdir('log') + except OSError as e: + if e.errno != errno.EEXIST: + raise + + # Obtiene la fecha y hora de inicio + ini_timestr = time.strftime("%Y%m%d-%H%M%S") + + # Inicia el log + logging.basicConfig(level=logging.INFO, + format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s', + encoding='utf-8', + datefmt='%m-%d %H:%M', + filename='log/' + ini_timestr + '.log', + filemode='w') + + # Define a Handler which writes INFO messages or higher to the sys.stderr + console = logging.StreamHandler() + console.setLevel(logging.INFO) + + # Set a format which is simpler for console use + formatter = logging.Formatter('%(levelname)-8s>> %(message)s') + + # Tell the handler to use this format + console.setFormatter(formatter) + + # Add the handler to the root logger + logging.getLogger().addHandler(console) + # logging.info("Empieza el programa\n") + + session = requests.session() + + # Disable cert verification for demo purpose. + # This is not recommended in a production environment. + session.verify = False + + # Disable the secure connection warning for demo purpose. + # This is not recommended in a production environment. + urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + + # Obtiene la información desde el fichero .env + load_dotenv() + vcenter_server = os.getenv('VCENTER_SERVER') + vcenter_username = os.getenv('VCENTER_USERNAME') + vcenter_password = os.getenv('VCENTER_PASSWORD') + + print("vcenter_server", vcenter_server) + + # Connect to a vCenter Server using username and password + client = create_vsphere_client( + server=vcenter_server, username=vcenter_username, password=vcenter_password, session=session) + + # Bucle principal + main_loop(client, operation) + # logging.info("Acaba el programa") + + +def main_loop(client, operation): + + # Enciende máquinas + if operation == "on": + logging.info("Modo de operación: ENCENDER MÁQUINAS\n") + group = 0 + + while group < len(vms): + logging.info("Grupo %s: PROCESANDO", group) + + # Create the thread pool + n_threads = len(vms[group]) + with ThreadPoolExecutor(n_threads) as executor: + _ = [executor.submit(power_on_vm, client, get_vm( + client, vm_name), vm_name) for vm_name in vms[group]] + + # Terminado el grupo de máquinas + logging.info("Grupo %s: TERMINADO\n", group) + + # Resumen + logging.info("Grupo %s: RESUMEN", group) + num_mv = 0 + for vm_name in vms[group]: + vm = get_vm(client, vm_name) + if power_on_vm(client, vm, vm_name): + num_mv += 1 + logging.info("%s de %s máquinas encendidas\n", + num_mv, len(vms[group])) + + group += 1 + if group < len(vms): + logging.info("Esperando %s segundos ...\n", wait_power_on) + time.sleep(wait_power_on) + + # Apaga máquinas + if operation == "off": + logging.info("Modo de operación: APAGAR MÁQUINAS\n") + group = len(vms) - 1 + + while group >= 0: + logging.info("Grupo %s: PROCESANDO", group) + + # Create the thread pool + n_threads = len(vms[group]) + with ThreadPoolExecutor(n_threads) as executor: + _ = [executor.submit(power_off_vm, client, get_vm( + client, vm_name), vm_name) for vm_name in vms[group]] + + # Terminado el grupo de máquinas + logging.info("Grupo %s: TERMINADO\n", group) + + # Resumen + logging.info("Grupo %s: RESUMEN", group) + num_mv = 0 + for vm_name in vms[group]: + vm = get_vm(client, vm_name) + if power_off_vm(client, vm, vm_name): + num_mv += 1 + logging.info("%s de %s máquinas apagadas\n", + num_mv, len(vms[group])) + + group -= 1 + if group >= 0: + logging.info("Esperando %s segundos ...\n", wait_power_off) + time.sleep(wait_power_off) + + +if __name__ == "__main__": + main() diff --git a/zxart.py b/zxart.py new file mode 100644 index 0000000..fbd9558 --- /dev/null +++ b/zxart.py @@ -0,0 +1,132 @@ +import json +import requests +import os +import time +import random +import shutil +from urllib.parse import urlparse + +# Variables para configurar el modo de funcionamiento del programa +json_file = r"/home/sergio/zx/zxart/picture.json" +destination_path = r"/home/sergio/zx/zxart/pictures/" +cache_path = r"/home/sergio/zx/zxart/cache/pictures/" +wait = False # Establece una pausa aleatoria entre descargas +min_wait = 1 # Segundos mínimos a esperar entre descargas +max_wait = 3 # Segundos máximos a esperar entre descargas +tags = ["Loading Screen", "Game"] # Tags de las imagenes seleccionadas. Vacío para todas + + +# Obtiene la lista de direcciones desde un fichero json +def get_urls(): + urls = [] + + # Abre el fichero json y lo importa en un diccionario + f = open(json_file) + data = json.load(f) + + # Procesa el diccionario para obtener la lista de direcciones + # Se distingue el caso de descargar todas las imagenes o solo las que tienen ciertas etiquetas + if len(tags) > 0: + for i in data["zxPicture"]: + if "tags" in i: + for tag in tags: + if tag in i["tags"]: + if "originalUrl" in i: + if i["originalUrl"][-3:] == "scr": + urls.append(i["originalUrl"]) + else: + for i in data["zxPicture"]: + if "originalUrl" in i: + if i["originalUrl"][-3:] == "scr": + urls.append(i["originalUrl"]) + + # Elimina los direcciones duplicadas + urls = list(dict.fromkeys(urls)) + + # Ordena la lista de direcciones + urls.sort() + + # Cierra el fichero + f.close() + + # Devuelve el resultado + return urls + + +# Obtiene el nombre del fichero a partir de una url completa +def url_filename(url): + pos = url.rfind("/") + 1 + filename = url[pos:] + filename = filename.replace("filename:", "pic_") + return filename + + +# Descarga un fichero desde una url a un destino específico +def download_file(url, dest): + try: + r = requests.get(url) + with open(dest, "wb") as f: + f.write(r.content) + + except requests.exceptions.Timeout: + # Maybe set up for a retry, or continue in a retry loop + print("Timeout: {}".format(url)) + + except requests.exceptions.TooManyRedirects: + # Tell the user their URL was bad and try a different one + print("Bad URL: {}".format(url)) + + except requests.exceptions.RequestException as e: + # catastrophic error. bail. + raise SystemExit(e) + + +# Descarga los ficheros a partir de una lista de direcciones +def get_files(urls): + count = 0 + total = len(urls) + for url in urls: + count = count + 1 + downloaded_file = url_filename(url) + destination_file = os.path.join(destination_path, downloaded_file) + cache_file = os.path.join(cache_path, downloaded_file) + # Comprueba si el fichero existe en el destino + if not os.path.isfile(destination_file): + # Si no existe, comprueba si existe en la caché + if os.path.isfile(cache_file): + shutil.copyfile(cache_file, destination_file) + print( + "cached : {:{width}} ({} / {})".format( + downloaded_file, count, total, width=50 + ) + ) + # Si no está en la caché, lo descarga a la caché + else: + download_file(url, cache_file) + # Si la ha descargado a la caché, la copia al destino + if os.path.isfile(cache_file): + shutil.copyfile(cache_file, destination_file) + # download_file(url, destination_file) + print( + "downloaded : {:{width}} ({} / {})".format( + downloaded_file, count, total, width=50 + ) + ) + if wait: + time.sleep(random.randint(min_wait, max_wait)) + # Si el fichero ya existe, no hace nada + else: + print( + "skipping : {:{width}} ({} / {})".format( + downloaded_file, count, total, width=50 + ) + ) + + +def main(): + urls = get_urls() + get_files(urls) + + +if __name__ == "__main__": + main()