Automatic gracefully shutdown of virtual machines and ESXI hypervisor from telegram bot

Having a Telegram bot interface to control your network can be convenient for a few reasons:

  1. Remote Access: With a Telegram bot interface, you can control your network from anywhere as long as you have internet access. This can be especially useful if you need to manage your network while you are away from your computer or if you are not physically present at the location of your network.
  2. Ease of Use: Telegram bot interfaces are often designed to be user-friendly and intuitive. This can make it easier for you to manage your network, even if you are not familiar with complex networking tools.
  3. Notifications: Telegram bots can also be set up to send you notifications or alerts when certain events occur on your network. For example, you can receive a notification when a new device connects to your network or when your network experiences downtime.
  4. Automation: You can also use a Telegram bot interface to automate certain tasks on your network, such as turning on or off specific devices or setting up scheduled tasks.

I wrote a bot to shut down my servers remotely, because I don’t know for sure if there will be power in the server room, and if the batteries are enough. But I’m too lazy to turn on my laptop and connect and do it manually.

 

import logging
import random
import asyncio
import aiogram.utils.markdown as md
from aiogram import Bot, Dispatcher, types
from aiogram.dispatcher.filters import Text, IDFilter
from aiogram.dispatcher import FSMContext
from aiogram.dispatcher.filters.state import State, StatesGroup
from aiogram.contrib.fsm_storage.memory import MemoryStorage
import os
import ssl
import time
from pyVim import connect
from pyVmomi import vim


# Set up the logger
logging.basicConfig(level=logging.INFO)

# Create a bot object
bot = Bot(token=os.getenv('TOKEN_API'))
# Specify the IP address, username, and password of the ESXi host system

nas_creds = ("192.168.15.7","root","71passwordexampleI4")
gen9_creds = ("192.168.15.9","root","1passwordexample")
gen10_creds = ("192.168.15.11","root","1уpasswordexampleа")

def ServerShutdown(host_ip,user,password):
    try:
        # SSL context to allow connection to ESXi server with self-signed certificate
        ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
        ssl_context.verify_mode = ssl.CERT_NONE

        # Connect to the ESXi server
        service_instance = connect.SmartConnect(host=host_ip, user=user, pwd=password, sslContext=ssl_context)

        # Retrieve the content for the ESXi server
        content = service_instance.RetrieveContent()

        # Get the host system
        host = content.rootFolder.childEntity[0].hostFolder.childEntity[0].host[0]

        # Gracefully shut down all virtual machines on the host
        vm_list = host.vm
        for vm in vm_list:
            try:
                vm.ShutdownGuest()
            except:
                print(f"Could not gracefully shutdown VM {vm.name}. Proceeding with power off.")
                vm.PowerOff()
        print("VMs power buttons pressed")

        time.sleep(10)
        # Wait for all virtual machines to shut down
        while True:
            if all(vm.runtime.powerState == 'poweredOff' for vm in vm_list):
                break

        # Shut down the host
        try:
            task = host.ShutdownHost_Task(force=True)
            print("Host shutting down...")

        except vim.fault.MethodFault as error:
            print(f"Error shutting down host: {error.msg}")
            task = None
        return 'ok'
    except:
        return 'already off or error'

# Create a dispatcher object
storage = MemoryStorage()
dp = Dispatcher(bot, storage=storage)

# Set the ID of the user who is allowed to use the bot

allowed_ids = [323433243243282,314324324334,6923432423534650]
class MyIDFilter(IDFilter):
    pass
id_filter = IDFilter(allowed_ids)


keyboard = types.InlineKeyboardMarkup()
button1 = types.InlineKeyboardButton(text="NAS", callback_data="button1")
button2 = types.InlineKeyboardButton(text="GEN9", callback_data="button2")
button3 = types.InlineKeyboardButton(text="GEN10", callback_data="button3")
keyboard.add(button1, button2, button3)

# Define the states for the conversation
class Confirmation(StatesGroup):
    start = State()
    code = State()
    first = State()


# Define the function to generate the 4-digit code
def generate_code():
    code = random.randint(1000, 9999)
    return code


# Define the handler for the /start command


@dp.message_handler(id_filter, commands=['start'])
async def start_command(message: types.Message):

    # Send the message with the keyboard
    await message.answer("Please choose host to shutdown:", reply_markup=keyboard)


# Define the handler for the button callback queries
@dp.callback_query_handler(id_filter, Text(equals=["button1", "button2", "button3"]))
async def process_callback_button(callback_query: types.CallbackQuery, state=Confirmation.first):
    # Get the selected button from the callback query
    selected_button = callback_query.data

    # Generate the 4-digit code and save it to the conversation state
    code = generate_code()
    await state.update_data(code=code)
    await state.update_data(selected_button=selected_button)

    # Ask the user to confirm the action by typing the code
    await callback_query.message.answer(f"Please type in the following code to confirm the action: {code}")

    # Transition to the confirmation state
    await Confirmation.code.set()


# Define the handler for the user's confirmation
@dp.message_handler(id_filter, state=Confirmation.code)
async def process_confirmation(message: types.Message, state: FSMContext):
    # Get the stored code from the conversation state
    data = await state.get_data()
    code = data['code']
    selected_button = data['selected_button']

    # Check if the user's confirmation matches the code
    if message.text == str(code):
        if selected_button == 'button1':
            await message.answer(f"OK! Starting shutdown process for NAS! You may choose another ESXI host:",reply_markup=keyboard)
           #ServerShutdown(nas_creds)
            await message.answer(f"NAS {ServerShutdown(*nas_creds)}",reply_markup=keyboard)
        if selected_button == 'button2':
            await message.answer(f"OK! Starting shutdown process for GEN9! You may choose another ESXI host:",reply_markup=keyboard)
           #ServerShutdown(gen9_creds)
            await message.answer(f"GEN9 {ServerShutdown(*gen9_creds)}",reply_markup=keyboard)
        if selected_button == 'button3':
            await message.answer(f"OK! Starting shutdown process for GEN10! You may choose another ESXI host:",reply_markup=keyboard)
           #ServerShutdown(gen10_creds)
            await message.answer(f"GEN10 {ServerShutdown(*gen10_creds)}",reply_markup=keyboard)
    else:
        # If the confirmation is incorrect, inform the user
        await message.answer("Sorry, the code you entered is incorrect. Please choose ESXI host",reply_markup=keyboard)

    # End the conversation
    await dp.current_state().reset_state()
    await dp.current_state().set_state(None)


# Start the bot
if __name__ == '__main__':
    asyncio.run(dp.start_polling()

This is a Python program that uses the aiogram library to create a bot for the messaging platform Telegram. The bot can initiate a graceful shutdown of virtual machines and hosts on an ESXi server. It also has the ability to authenticate and filter authorized users.

See also  How to separate menu between ordinary and privileged users in Django

The program imports the necessary modules, including aiogram, pyVim, and logging.

A Bot object is created with the Telegram API token.

Three ESXi servers’ IP addresses, usernames, and passwords are stored in tuples for future use.

The function ‘ServerShutdown’ uses pyVmomi to connect to an ESXi server, retrieve its content, and gracefully shut down all virtual machines and then the host.

A Dispatcher object is created with aiogram and a filter is defined for allowed user IDs.

A keyboard is created with aiogram’s InlineKeyboardMarkup for the user to choose which host to shut down.

A confirmation flow is set up using the aiogram.contrib.fsm_storage.memory module to generate a 4-digit code and ask the user to confirm the action by typing the code.

See also  Server control by Telegram Bot - Run Shell commands by Python

Message handlers are defined for the /start command, button callback queries, and the user’s confirmation.

When the bot receives a callback query with the selected button, it generates a 4-digit code and saves it to the conversation state. It then asks the user to confirm the action by typing the code.

If the user’s confirmation matches the code, the ServerShutdown function is called with the corresponding ESXi server’s credentials, and the result of the function is returned to the user. The bot also sends a message with the result and provides the user with the option to choose another ESXi host.

 

Author: admin

1 thought on “Automatic gracefully shutdown of virtual machines and ESXI hypervisor from telegram bot

Leave a Reply

Your email address will not be published. Required fields are marked *