Azure Metadata Service: Scheduled Events para máquinas virtuales Linux

Se aplica a: ✔️ Máquinas virtuales Linux ✔️ Conjuntos de escalado flexibles ✔️ Conjuntos de escalado uniformes

Scheduled Events es un servicio de Azure Metadata Service que proporciona el tiempo de aplicación para prepararse para el mantenimiento de la máquina virtual. Da información sobre los eventos de mantenimiento próximos (por ejemplo, un reinicio) para que la aplicación pueda prepararlos y así limitar las interrupciones. Está disponible para todos los tipos de máquina virtual de Azure, incluidos IaaS y PaaS, tanto en Windows como en Linux.

Para más información acerca de Scheduled Events en Windows, consulte Scheduled Events para máquinas virtuales Windows.

Los eventos programados proporcionan notificaciones proactivas sobre los próximos eventos. Para obtener información reactiva sobre los eventos que ya se han producido, consulte Información de disponibilidad de máquinas virtuales en Azure Resource Graph y Creación de una regla de alertas de disponibilidad para la máquina virtual de Azure.

Nota

Scheduled Events está disponible con carácter general en todas las regiones de Azure. Consulte el apartado sobre la disponibilidad por región y versión para obtener información sobre la versión más reciente.

¿Por qué usar Scheduled Events?

Muchas aplicaciones pueden aprovechar el tiempo para preparar el mantenimiento de las máquinas virtuales. Este tiempo se puede usar para realizar tareas específicas de la aplicación con el fin de mejorar la disponibilidad, la confiabilidad y la capacidad de servicio, lo que incluye:

  • Punto de control y restauración.
  • Purga de la conexión.
  • Conmutación por error de la réplica principal.
  • Eliminación de un grupo de equilibradores de carga.
  • Registros de eventos.
  • Cierre estable.

Con Scheduled Events, la aplicación puede detectar cuándo se producirá el mantenimiento y desencadenar tareas para limitar sus efectos.

Eventos programados proporciona eventos en los casos de uso siguientes:

Conceptos básicos

Metadata Service expone información sobre la ejecución de máquinas virtuales mediante un punto de conexión de REST accesible desde la propia máquina virtual. La información se encuentra disponible a través de una dirección IP no enrutable y no se expone fuera de la máquina virtual.

Ámbito

Los eventos programados se entregan y se pueden confirmar mediante:

  • Máquinas virtuales independientes.
  • Todas las máquinas virtuales de un servicio en la nube de Azure (clásico).
  • Todas las máquinas virtuales de un conjunto de disponibilidad.
  • Todas las máquinas virtuales de un grupo de selección de ubicación de conjunto de escalado.

Nota:

Los eventos programados para todas las máquinas virtuales (VM) de un conjunto de disponibilidad completo o de un grupo de selección de ubicación para un conjunto de escalado de máquinas virtuales se entregan a las demás máquinas virtuales del mismo grupo o conjunto, independientemente del uso de la zona de disponibilidad.

Por ello, revise el campo Resources del evento para identificar cuáles son las máquinas virtuales que se verán afectadas.

Detección de punto de conexión

En el caso de las máquinas virtuales con red virtual habilitada, el servicio de metadatos está disponible desde una dirección IP no enrutable estática, 169.254.169.254. El punto de conexión completo de la versión más reciente de Scheduled Events es:

http://169.254.169.254/metadata/scheduledevents?api-version=2020-07-01

Si la máquina virtual no se crea dentro de una red virtual (los casos predeterminados para servicios en la nube y máquinas virtuales clásicas) se necesita lógica adicional para detectar la dirección IP que se va a usar. Para aprender a detectar el punto de conexión de host, consulte este ejemplo.

Disponibilidad por región y versión

El servicio Scheduled Events tiene versiones. Las versiones son obligatorias; la actual es 2020-07-01.

Versión Tipo de versión Regions Notas de la versión
01-07-2020 Disponibilidad general All
  • Compatibilidad agregada con la duración de eventos
  • 2019-08-01 Disponibilidad general All
  • Compatibilidad agregada con EventSource
  • 01-04-2019 Disponibilidad general All
  • Compatibilidad agregada con la descripción de eventos
  • 2019-01-01 Disponibilidad general All
  • Compatibilidad agregada con conjuntos de escalado de máquinas virtuales EventType "Terminate"
  • 01-11-2017 Disponibilidad general All
  • Se agregó compatibilidad para la expulsión de la máquina virtual de Azure Spot EventType 'Preempt'
  • 2017-08-01 Disponibilidad general All
  • Se quitó el guion bajo antepuesto de los nombres de recursos en las máquinas virtuales de IaaS
  • Se aplicó el requisito de encabezado de metadatos para todas las solicitudes
  • 2017-03-01 Versión preliminar All
  • Versión inicial
  • Nota

    Versiones preliminares de Scheduled Events admitidas {más recientes} como la versión de API. Este formato ya no es compatible y dejará de utilizarse en el futuro.

    Habilitación y deshabilitación de Scheduled Events

    Scheduled Events se habilita para un servicio la primera vez que se realiza una solicitud de eventos. Debe esperar hasta dos minutos de demora en la respuesta en la primera llamada. Scheduled Events se deshabilita para el servicio si no se realiza una solicitud al punto de conexión durante 24 horas.

    Mantenimiento iniciado por el usuario

    El mantenimiento de las máquinas virtuales iniciado por el usuario a través de Azure Portal, API, CLI o PowerShell, da lugar a un evento programado. Esto permite probar la lógica de preparación del mantenimiento en la aplicación, además de que la aplicación pueda prepararse para el mantenimiento iniciado por el usuario.

    Si reinicia una máquina virtual, se programa un evento del tipo Reboot. Si vuelve a implementar una máquina virtual, se programa un evento del tipo Redeploy. Normalmente, los eventos con un origen del evento de usuario se pueden aprobar inmediatamente para evitar un retraso en las acciones iniciadas por el usuario. Se recomienda tener una máquina virtual principal, y una máquina secundaria que se comunique y apruebe los eventos programados generados por el usuario en el caso de que la máquina virtual principal deje de responder. La aprobación inmediata de eventos evita retrasos en la recuperación de la aplicación a un buen estado.

    Los eventos programados para actualizaciones o restablecimientos de imagen inicial del SO invitado de VMSS se admiten para tamaños de máquina virtual de uso general que solo admiten actualizaciones que conservan la memoria. No funciona para las series H, M, N y G. Los eventos programados para las actualizaciones y restablecimientos de imagen inicial del SO invitado de VMSS están deshabilitados de manera predeterminada. Para habilitar eventos programados para estas operaciones en tamaños de máquina virtual compatibles, habilítelos primero mediante OSImageNotificationProfile.

    Uso de la API

    Información general de alto nivel

    Hay dos componentes principales para controlar eventos programados, la preparación y la recuperación. Todos los eventos programados actuales que afectan a una máquina virtual están disponibles para leerse a través del punto de conexión de eventos programados de IMDS. Cuando el evento ha alcanzado un estado terminal, se quita de la lista de eventos. En el diagrama siguiente se muestran las distintas transiciones de estado que puede experimentar un único evento programado:

    State diagram showing the various transitions a scheduled event can take.

    En el caso de los eventos en estado EventStatus:"Programado", deberá seguir los pasos necesarios para preparar la carga de trabajo. Una vez completada la preparación, debe aprobar el evento mediante la API de eventos programados. De lo contrario, el evento se aprueba automáticamente cuando se alcanza la hora NotBefore. Si la máquina virtual está en la infraestructura compartida, el sistema esperará a que todos los demás inquilinos del mismo hardware también aprueben el trabajo o el tiempo de expiración. Una vez que se recopilan las aprobaciones de todas las máquinas virtuales afectadas o se alcanza la hora NotBefore, Azure genera una nueva carga de eventos programados con el estado EventStatus:"Iniciado" y desencadena el inicio del evento de mantenimiento. Cuando el evento ha alcanzado un estado terminal, se quita de la lista de eventos. Esto sirve como señal para que el cliente recupere sus máquinas virtuales.

    A continuación se muestra el pseudocódigo que muestra un proceso para leer y administrar eventos programados en la aplicación:

    current_list_of_scheduled_events = get_latest_from_se_endpoint()
    #prepare for new events
    for each event in current_list_of_scheduled_events:
      if event not in previous_list_of_scheduled_events:
        prepare_for_event(event)
    #recover from completed events
    for each event in previous_list_of_scheduled_events:
      if event not in current_list_of_scheduled_events:
        receover_from_event(event)
    #prepare for future jobs
    previous_list_of_scheduled_events = current_list_of_scheduled_events
    

    A medida que los eventos programados se usan a menudo para las aplicaciones con requisitos de alta disponibilidad, hay algunos casos excepcionales que se deben tener en cuenta:

    1. Una vez completado y quitado un evento programado de la matriz, no habrá ningún impacto adicional sin un nuevo evento, incluido otro evento con el estado EventStatus:"Programado"
    2. Azure supervisa las operaciones de mantenimiento en toda la flota y, en raras circunstancias, determina que una operación de mantenimiento es demasiado riesgosa para su aplicación. En ese caso, el evento programado pasará directamente de "Programado" a quitarse de la matriz de eventos
    3. En caso de error de hardware, Azure omite el estado "Programado" y pasará inmediatamente al estado EventStatus:"Iniciado".
    4. Aunque el evento sigue en estado EventStatus:"Iniciado", puede haber otro impacto de una duración más corta que la anunciada en el evento programado.

    Como parte de la garantía de disponibilidad de Azure, las máquinas virtuales de diferentes dominios de error no se verán afectadas por las operaciones de mantenimiento de rutina al mismo tiempo. Sin embargo, pueden tener operaciones serializadas una después de otra. Las máquinas virtuales de un dominio de error pueden recibir eventos programados con EventStatus:"Programado" poco después de que se complete el mantenimiento de otro dominio de error. Independientemente de la arquitectura que haya elegido, siga comprobando los nuevos eventos pendientes en las máquinas virtuales.

    Aunque los tiempos exactos de los eventos varían, el diagrama siguiente proporciona una guía aproximada sobre cómo continúa una operación de mantenimiento típica:

    • EventStatus:"Programado" a Tiempo de expiración de aprobación: 15 minutos
    • Duración del impacto: 7 segundos
    • EventStatus:"Iniciado" a Completado (evento quitado de la matriz Events): 10 minutos

    Diagram of a timeline showing the flow of a scheduled event.

    encabezados

    Al realizar consultas a Metadata Service, debe proporcionar el encabezado Metadata:true para asegurarse de que la solicitud no se haya redirigido de manera involuntaria. El encabezado Metadata:true es necesario para todas las solicitudes de eventos programados. Un error al incluir el encabezado en la solicitud generará una respuesta del tipo "Solicitud incorrecta" de Metadata Service.

    Consulta de eventos

    Puede consultar los eventos programados; para ello, simplemente haga la siguiente llamada:

    Ejemplo de Bash

    curl -H Metadata:true http://169.254.169.254/metadata/scheduledevents?api-version=2020-07-01
    

    Ejemplo de PowerShell

    Invoke-RestMethod -Headers @{"Metadata"="true"} -Method GET -Uri "http://169.254.169.254/metadata/scheduledevents?api-version=2020-07-01" | ConvertTo-Json -Depth 64
    

    Ejemplo de Python

    import json
    import requests
    
    metadata_url ="http://169.254.169.254/metadata/scheduledevents"
    header = {'Metadata' : 'true'}
    query_params = {'api-version':'2020-07-01'}
    
    def get_scheduled_events():           
        resp = requests.get(metadata_url, headers = header, params = query_params)
        data = resp.json()
        return data
    
    

    Una respuesta contiene una matriz de eventos programados. Una matriz vacía significa que actualmente no hay eventos programados. En caso de que haya eventos programados, la respuesta contiene una matriz de eventos.

    {
        "DocumentIncarnation": {IncarnationID},
        "Events": [
            {
                "EventId": {eventID},
                "EventType": "Reboot" | "Redeploy" | "Freeze" | "Preempt" | "Terminate",
                "ResourceType": "VirtualMachine",
                "Resources": [{resourceName}],
                "EventStatus": "Scheduled" | "Started",
                "NotBefore": {timeInUTC},       
                "Description": {eventDescription},
                "EventSource" : "Platform" | "User",
                "DurationInSeconds" : {timeInSeconds},
            }
        ]
    }
    

    Propiedades de evento

    Propiedad Descripción
    Encarnación de documentos Entero que aumenta cuando cambia la matriz de eventos. Los documentos con la misma encarnación contienen la misma información de evento y la encarnación se incrementará cuando cambie un evento.
    EventId Es un identificador único global del evento.

    Ejemplo:
    • 602d9444-d2cd-49c7-8624-8643e7171297
    EventType Es el impacto causado por el evento.

    Valores:
    • Freeze: la máquina virtual está programada para pausarse durante unos segundos. Puede que se suspenda la conectividad de la CPU y la red, pero esto no afecta a la memoria ni a los archivos abiertos.
    • Reboot: la máquina virtual está programada para reiniciarse (se borrará la memoria no persistente). En raras ocasiones, una máquina virtual programada para EventType:"Reboot" puede experimentar un evento de inmovilización en lugar de un reinicio. Siga las instrucciones anteriores para saber si el evento ha concluido y es seguro restaurar la carga de trabajo.
    • Redeploy: la máquina virtual está programada para moverse a otro nodo (los discos efímeros se pierden).
    • Preempt: se está eliminando la máquina virtual de Azure Spot (se pierden los discos efímeros). Este evento se pone a disposición de los usuarios de la mejor manera posible.
    • Terminate: la máquina virtual está programada para eliminarse.
    ResourceType Es el tipo de recurso al que este evento afecta.

    Valores:
    • VirtualMachine
    Recursos Es la lista de recursos a los que este evento afecta.

    Ejemplo:
    • ["FrontEnd_IN_0", "BackEnd_IN_0"]
    EventStatus Es el estado de este evento.

    Valores:
    • Scheduled: este evento está programado para iniciarse después de la hora especificada en la propiedad NotBefore.
    • Started: este evento se ha iniciado.
    Ni Completed ni otro estado similar se han proporcionado antes. El evento ya no vuelve cuando finaliza el evento.
    NotBefore Hora a partir de la que puede iniciarse este evento. Se garantiza que el evento no se inicia antes de esta hora. Está en blanco si el evento ya se ha iniciado

    Ejemplo:
    • Lunes, 19 de septiembre de 2016, 18:29:47 GMT
    Descripción Descripción de este evento.

    Ejemplo:
    • El servidor host está en mantenimiento.
    EventSource Iniciador del evento.

    Ejemplo:
    • Platform: la plataforma inicia este evento.
    • User: el usuario inicia este evento.
    DurationInSeconds Duración que se espera de la interrupción causada por el evento.

    Ejemplo:
    • 9: la interrupción causada por el evento durará 9 segundos.
    • 0: el evento no interrumpirá la máquina virtual ni afectará a su disponibilidad (por ejemplo, la actualización a la red)
    • -1: valor predeterminado que se usa si la duración del impacto se desconoce o no es aplicable.

    Programación de eventos

    Cada evento se programa una cantidad mínima de tiempo en el futuro en función de su tipo. Este tiempo se refleja en la propiedad NotBefore de un evento.

    EventType Aviso mínimo
    Freeze 15 minutos
    Reboot 15 minutos
    Volver a implementar 10 minutos
    Terminate Configurable por el usuario: de 5 a 15 minutos

    Esto significa que puede detectar una programación futura del evento al menos durante el tiempo de aviso mínimo antes de que se produzca el evento. Una vez que se programe un evento, pasará al estado Started después de que se apruebe o pase el tiempo NotBefore. Pero en algunas ocasiones, Azure cancelará la operación antes de que se inicie. En ese caso, el evento se quitará de la matriz Events y el impacto no se producirá según lo programado anteriormente.

    Nota:

    En algunos casos, Azure puede predecir errores en el host debidos a que el hardware está degradado e intentará mitigar la interrupción del servicio mediante la programación de una migración. Las máquinas virtuales afectadas recibirán un evento programado con un valor de NotBefore que habitualmente es unos días posteriores. El tiempo real varía en función de la valoración de riesgo de error predicha. Azure intenta avisar con 7 días de antelación siempre que sea posible, pero el tiempo real varía y puede ser menor si la predicción es que hay una alta probabilidad de que se produzcan errores en el hardware de forma inminente. Para minimizar el riesgo para el servicio si se produce un error en el hardware antes de la migración iniciada por el sistema, se recomienda volver a implementar automáticamente la máquina virtual lo antes posible.

    Nota

    En caso de que el nodo de host experimente un error de hardware, Azure omitirá el período de aviso mínimo e iniciará de inmediato del proceso de recuperación de las máquinas virtuales afectadas. De esta forma, se reduce el tiempo de recuperación en caso de que las máquinas virtuales afectadas no puedan responder. Durante el proceso de recuperación se crea un evento para todas las máquinas virtuales afectadas con EventType = Reboot y EventStatus = Started.

    Frecuencia de sondeo

    Puede sondear el punto de conexión para obtener las actualizaciones con la frecuencia que quiera. Sin embargo, cuanto más tiempo transcurre entre solicitudes, más tiempo se pierde para reaccionar ante un evento próximo. La mayoría de los eventos tienen un aviso previo de 5 a 15 minutos, aunque en algunos casos el aviso previo podría llegar con muy poca antelación, como 30 segundos. Para asegurarse de que tiene tanto tiempo como sea posible para llevar a cabo acciones de mitigación, se recomienda sondear el servicio una vez por segundo.

    Inicio de un evento

    Cuando se haya enterado de un evento próximo y completado la lógica para llevar a cabo un apagado estable, puede aprobar el evento pendiente mediante una llamada de POST a Metadata Service con EventId. Esta llamada indica a Azure que puede acortar el tiempo de notificación mínimo (cuando sea posible). Es posible que el evento no se inicie inmediatamente después de la aprobación; en algunos casos, Azure exige la aprobación de todas las máquinas virtuales hospedadas en el nodo para continuar con el evento.

    A continuación se muestra el JSON de ejemplo en el cuerpo de la solicitud POST. La solicitud debe contener una lista de StartRequests. Cada StartRequest contiene el elemento EventId para el evento que desea acelerar:

    {
    	"StartRequests" : [
    		{
    			"EventId": {EventId}
    		}
    	]
    }
    

    El servicio siempre devuelve un código correcto 200 si se pasa un identificador de evento válido, incluso si otra máquina virtual ya ha aprobado el evento. Un código de error 400 indica que el encabezado de solicitud o la carga tienen un formato incorrecto.

    Nota:

    Los eventos no continuarán a menos que se aprueben a través de un mensaje POST o que haya transcurrido la hora de NotBefore. Esto incluye eventos desencadenados por el usuario, como reinicios de la máquina virtual desde Azure Portal.

    Ejemplo de Bash

    curl -H Metadata:true -X POST -d '{"StartRequests": [{"EventId": "f020ba2e-3bc0-4c40-a10b-86575a9eabd5"}]}' http://169.254.169.254/metadata/scheduledevents?api-version=2020-07-01
    

    Ejemplo de PowerShell

    Invoke-RestMethod -Headers @{"Metadata" = "true"} -Method POST -body '{"StartRequests": [{"EventId": "5DD55B64-45AD-49D3-BBC9-F57D4EA97BD7"}]}' -Uri http://169.254.169.254/metadata/scheduledevents?api-version=2020-07-01 | ConvertTo-Json -Depth 64
    

    Ejemplo de Python

    import json
    import requests
    
    def confirm_scheduled_event(event_id):  
       # This payload confirms a single event with id event_id
       payload = json.dumps({"StartRequests": [{"EventId": event_id }]})
       response = requests.post("http://169.254.169.254/metadata/scheduledevents", 
                                headers =  {'Metadata' : 'true'}, 
                                params = {'api-version':'2020-07-01'}, 
                                data = payload)    
       return response.status_code
    

    Nota

    Reconocer un evento permite a este continuar para todos sus elementos Resources, no solo para la máquina virtual que lo reconoce. Por tanto, puede optar por elegir un líder para que coordine el reconocimiento. Este puede ser tan sencillo como la primera máquina del campo Resources.

    Respuestas de ejemplo

    A continuación se muestra un ejemplo de eventos visto por dos máquinas virtuales que se migraron en vivo a otro nodo.

    DocumentIncarnation cambia cada vez que hay información nueva en Events. Una aprobación del evento permitiría que la inmovilización continuara tanto para WestNO_0 como para WestNO_1. Un valor de DurationInSeconds de -1 indica que la plataforma no sabe cuánto va a tardar la operación.

    {
        "DocumentIncarnation":  1,
        "Events":  [
                   ]
    }
    
    {
        "DocumentIncarnation":  2,
        "Events":  [
                       {
                           "EventId":  "C7061BAC-AFDC-4513-B24B-AA5F13A16123",
                           "EventStatus":  "Scheduled",
                           "EventType":  "Freeze",
                           "ResourceType":  "VirtualMachine",
                           "Resources":  [
                                             "WestNO_0",
                                             "WestNO_1"
                                         ],
                           "NotBefore":  "Mon, 11 Apr 2022 22:26:58 GMT",
                           "Description":  "Virtual machine is being paused because of a memory-preserving Live Migration operation.",
                           "EventSource":  "Platform",
                           "DurationInSeconds":  5
                       }
                   ]
    }
    
    {
        "DocumentIncarnation":  3,
        "Events":  [
                       {
                           "EventId":  "C7061BAC-AFDC-4513-B24B-AA5F13A16123",
                           "EventStatus":  "Started",
                           "EventType":  "Freeze",
                           "ResourceType":  "VirtualMachine",
                           "Resources":  [
                                             "WestNO_0",
                                             "WestNO_1"
                                         ],
                           "NotBefore":  "",
                           "Description":  "Virtual machine is being paused because of a memory-preserving Live Migration operation.",
                           "EventSource":  "Platform",
                           "DurationInSeconds":  5
                       }
                   ]
    }
    
    {
        "DocumentIncarnation":  4,
        "Events":  [
                   ]
    }
    
    

    Ejemplo de Python

    En el siguiente ejemplo se realiza una consulta a Metadata Service para eventos programados y se aprueban todos los eventos pendientes:

    #!/usr/bin/python
    import json
    import requests
    from time import sleep
    
    # The URL to access the metadata service
    metadata_url ="http://169.254.169.254/metadata/scheduledevents"
    # This must be sent otherwise the request will be ignored
    header = {'Metadata' : 'true'}
    # Current version of the API
    query_params = {'api-version':'2020-07-01'}
    
    def get_scheduled_events():           
        resp = requests.get(metadata_url, headers = header, params = query_params)
        data = resp.json()
        return data
    
    def confirm_scheduled_event(event_id):  
        # This payload confirms a single event with id event_id
        # You can confirm multiple events in a single request if needed      
        payload = json.dumps({"StartRequests": [{"EventId": event_id }]})
        response = requests.post(metadata_url, 
                                headers= header,
                                params = query_params, 
                                data = payload)    
        return response.status_code
    
    def log(event): 
        # This is an optional placeholder for logging events to your system 
        print(event["Description"])
        return
    
    def advanced_sample(last_document_incarnation): 
        # Poll every second to see if there are new scheduled events to process
        # Since some events may have necessarily short warning periods, it is 
        # recommended to poll frequently
        found_document_incarnation = last_document_incarnation
        while (last_document_incarnation == found_document_incarnation):
            sleep(1)
            payload = get_scheduled_events()    
            found_document_incarnation = payload["DocumentIncarnation"]        
            
        # We recommend processing all events in a document together, 
        # even if you won't be actioning on them right away
        for event in payload["Events"]:
    
            # Events that have already started, logged for tracking
            if (event["EventStatus"] == "Started"):
                log(event)
                
            # Approve all user initiated events. These are typically created by an 
            # administrator and approving them immediately can help to avoid delays 
            # in admin actions
            elif (event["EventSource"] == "User"):
                confirm_scheduled_event(event["EventId"])            
                
            # For this application, freeze events less that 9 seconds are considered
            # no impact. This will immediately approve them
            elif (event["EventType"] == "Freeze" and 
                int(event["DurationInSeconds"]) >= 0  and 
                int(event["DurationInSeconds"]) < 9):
                confirm_scheduled_event(event["EventId"])
                
            # Events that may be impactful (for example, reboot or redeploy) may need custom 
            # handling for your application
            else: 
                #TODO Custom handling for impactful events
                log(event)
        print("Processed events from document: " + str(found_document_incarnation))
        return found_document_incarnation
    
    def main():
        # This will track the last set of events seen 
        last_document_incarnation = "-1"
    
        input_text = "\
            Press 1 to poll for new events \n\
            Press 2 to exit \n "
        program_exit = False 
    
        while program_exit == False:
            user_input = input(input_text)    
            if (user_input == "1"):                        
                last_document_incarnation = advanced_sample(last_document_incarnation)
            elif (user_input == "2"):
                program_exit = True       
    
    if __name__ == '__main__':
        main()
    

    Pasos siguientes