Having a Telegram bot interface to control your network can be convenient for a few reasons:
- 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.
- 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.
- 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.
- 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.
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.
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.
1 thought on “Automatic gracefully shutdown of virtual machines and ESXI hypervisor from telegram bot”