You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

370 lines
11 KiB
Python

#!/usr/bin/env python
import oursql, time, sys, requests, re
import smtplib
import datetime
db = oursql.connect(host="localhost", user="root", passwd="", db="transfer")
mail_start = """
Hello,
As announced a short while ago, we are going to migrate your VPSes to new servers.
We've had some unforeseen technical difficulties that led to a delay, but these
have been resolved. This e-mail is to inform you that the migration process has
been started.
The following of your VPSes are affected:
%s
You can follow the queue position and progress of your VPS migrations here: %s
You will receive further notifications for every individual VPS that is being migrated.
Please ensure that you add admin@bluevm.com to your address book, so that
our e-mails do not end up in your spam folder.
Regards,
BlueVM Staff
https://bluevm.com/
"""
mail_vps_start = """
Hello,
This e-mail is to inform you that the migration of your VPS with the username %s has started.
You will receive an e-mail when this migration is finished.
You can follow the progress of all your VPS migrations at %s
Regards,
BlueVM Staff
https://bluevm.com/
"""
mail_vps_success = """
Hello,
This e-mail is to inform you that the migration of your VPS with the username %s has
finished successfully.
Your new IP addresses are:
%s
You can follow the progress of all your VPS migrations at %s
Regards,
BlueVM Staff
https://bluevm.com/
"""
mail_vps_error = """
Hello,
This e-mail is to inform you that the migration of your VPS with the username %s has
been aborted because an error has occurred. No data loss has occurred, and staff
will be looking into the issue shortly.
You can follow the progress of all your VPS migrations at %s
Regards,
BlueVM Staff
https://bluevm.com/
"""
class Panel(object):
def __init__(self, target):
self.target = target
self.sess = requests.Session()
def login(self, username, password):
page = self.sess.get("%s/login/" % self.target).text
match = re.search('<input type=hidden name=id value="([^"]+)">', page)
if match is None:
raise Exception("Not a valid HyperVM panel.")
fid = match.group(1)
error_string = "Login Unsuccessful"
post_target = "%s/htmllib/phplib/" % self.target
post_vars = {
"frm_clientname": username,
"frm_password": password,
"id": fid,
"login": "Login"
}
page = self.sess.post(post_target, data=post_vars).text
if error_string in page:
raise Exception("Login failed.")
def run_command(self, node, command):
post_target = "%s/display.php" % self.target
post_vars = {
"frm_o_o[0][class]": "pserver",
"frm_o_o[0][nname]": node,
"frm_pserver_c_ccenter_command": command,
"frm_pserver_c_ccenter_error": "",
"frm_action": "updateform",
"frm_subaction": "commandcenter",
"frm_change": "Execute"
}
page = self.sess.post(post_target, data=post_vars).text
result = re.search("<textarea[^>]+frmtextarea[^>]+>(.*?)<\/textarea>", page, re.DOTALL)
if result is not None:
return result.group(1)
def Vps(self, username):
return Vps(self, username)
class Vps(object):
def __init__(self, panel, username):
self.panel = panel
self.username = username
def get_ip_addresses(self):
get_target = "%s/display.php" % self.panel.target
get_vars = {
"frm_o_o[0][class]": "vps",
"frm_o_o[0][nname]": self.username,
"frm_o_cname": "vmipaddress_a",
"frm_action": "list",
"frm_list_refresh": "yes"
}
page = self.panel.sess.get(get_target, params=get_vars).text
ip_regex = '<td\s+class=collist\s+wrap\s+align=left\s+>\s+([0-9.]+)\s+<\/td>'
ip_list = re.findall(ip_regex, page)
self.ip_list = ip_list
return ip_list
def delete_ip_addresses(self, addresses):
post_target = "%s/display.php" % self.panel.target
post_vars = {
"frm_o_o[0][class]": "vps",
"frm_o_o[0][nname]": self.username,
"frm_accountselect": ",".join(addresses),
"frm_action": "delete",
"frm_o_cname": "vmipaddress_a",
"frm_confirmed": "yes",
"Confrm": "Confirm"
}
page = self.panel.sess.post(post_target, data=post_vars).text
def add_ip_addresses_num(self, num):
post_target = "%s/display.php" % self.panel.target
post_vars = {
"frm_o_o[0][class]": "vps",
"frm_o_o[0][nname]": self.username,
"frm_dttype[var]": "type",
"frm_dttype[val]": "npool",
"frm_o_cname": "vmipaddress_a",
"frm_vmipaddress_a_c_type": "npool",
"frm_vmipaddress_a_c_ip_num": num,
"frm_action": "add",
"frm_change": "Add"
}
page = self.panel.sess.post(post_target, data=post_vars).text
def transfer_to_live(self, target):
post_target = "%s/display.php" % self.panel.target
post_vars = {
"frm_o_o[0][class]": "vps",
"frm_o_o[0][nname]": self.username,
"frm_vps_c_syncserver": target,
"frm_action": "update",
"frm_subaction": "livemigrate",
"frm_change": "Update"
}
page = self.panel.sess.post(post_target, data=post_vars).text
if "Switching the Servers has been run in the background." not in page:
raise Exception("Transfer of %s to %s failed. Maybe an incorrect destination node was specified?" % (self.username, target))
def transfer_to(self, target):
post_target = "%s/display.php" % self.panel.target
post_vars = {
"frm_o_o[0][class]": "vps",
"frm_o_o[0][nname]": self.username,
"frm_vps_c_syncserver": target,
"frm_action": "update",
"frm_subaction": "switchserver",
"frm_change": "Update"
}
page = self.panel.sess.post(post_target, data=post_vars).text
if "Switching the Servers has been run in the background." not in page:
raise Exception("Transfer of %s to %s failed. Maybe an incorrect destination node was specified?" % (self.username, target))
def boot(self):
get_target = "%s/display.php" % self.panel.target
get_vars = {
"frm_o_o[0][class]": "vps",
"frm_o_o[0][nname]": self.username,
"frm_action": "update",
"frm_subaction": "boot"
}
page = self.panel.sess.get(get_target, params=get_vars).text
class Mailserver(object):
def __init__(self, ip, port, ssl, user, password):
if ssl == True:
self.smtp = smtplib.SMTP_SSL()
else:
self.smtp = smtplib.SMTP()
self.smtp.set_debuglevel(0)
self.smtp.connect(ip, port)
self.smtp.login(user, password)
def send(self, to, subject, body):
from_addr = "BlueVM <admin@bluevm.com>"
date = datetime.datetime.now().strftime("%d/%m/%Y %H:%M")
msg = "From: %s\nTo: %s\nSubject: %s\n\n%s\n" % (from_addr, to, subject, body)
self.smtp.sendmail(from_addr, to, msg)
def quit(self):
self.smtp.quit()
def send_mail(to, subject, body):
f = open("email.txt", "a")
f.write("To: %s\nSubject: %s\n\n%s\n\n\n" % (to, subject, body))
f.close()
mailserv = Mailserver("smtp.sendgrid.net", 465, True, "user", "pass")
mailserv.send(to, subject, body)
mailserv.quit()
def generate_url(email):
global db
c = db.cursor()
c.execute("SELECT * FROM emails WHERE `EmailAddress` = ?", (email,))
result = c.fetchall()[0]
return "http://transfer.bluevm.com/?email=%s&key=%s" % (result[1], result[2])
panel = Panel("http://manage.bluevm.com:8888")
panel.login("admin", "password")
print "Logged in."
c = db.cursor()
c.execute("SELECT * FROM emails")
emails = c.fetchall()
total = len(emails)
done = 0
try:
resumed = (sys.argv[1] == "--resume")
except IndexError, e:
resumed = False
if resumed == False:
for email in emails:
c.execute("SELECT * FROM entries WHERE `EmailAddress` = ?", (email[1],))
entries = c.fetchall()
if len(entries) > 0:
send_mail(email[1], "Migration process started", mail_start % ("\n\n".join(entry[2] for entry in entries), generate_url(email[1])))
done += 1
sys.stdout.write("Sent %d of %d announcement emails.\n" % (done, total))
while True:
c.execute("SELECT * FROM servers WHERE `Busy` = 2")
servers = c.fetchall()
for server in servers:
c.execute("SELECT * FROM entries WHERE `TargetNode` = ? AND `Position` = ?", (server[1], server[3]))
vpses = c.fetchall()
c.execute("UPDATE servers SET `Busy` = 4 WHERE `Id` = ?", (server[0],))
if len(vpses) > 0:
send_mail(vpses[0][3], "VPS migration failed", mail_vps_error % (vpses[0][2], generate_url(vpses[0][3])))
sys.stderr.write("WARNING: Transfer of %s to %s failed! Further transfers for this server have been aborted.\n" % (vpses[0][2], server[1]))
else:
sys.stderr.write("WARNING: Transfer of VPS to %s failed! Further transfers for this server have been aborted.\n" % (server[1]))
c.execute("SELECT * FROM servers WHERE `Busy` = 0")
servers = c.fetchall()
if len(servers) > 0:
for server in servers:
c.execute("SELECT * FROM entries WHERE `TargetNode` = ? AND `Position` = ? LIMIT 1", (server[1], server[3]))
results = c.fetchall()
if len(results) > 0:
last = results[0]
vps = panel.Vps(last[2])
ip_list = vps.get_ip_addresses()
ip_count = len(ip_list)
if ip_count > 0:
vps.delete_ip_addresses(ip_list)
sys.stderr.write("[%s] Deleted %d IP addresses: %s\n" % (last[2], ip_count, ", ".join(ip_list)))
vps.add_ip_addresses_num(ip_count)
sys.stderr.write("[%s] Added %d IP addresses from pool.\n" % (last[2], ip_count))
result = panel.run_command(server[1], "mkdir /vz/root/%s" % last[1])
try:
if result.strip() == "":
sys.stdout.write("[%s] Fixed root directory.\n" % last[2])
except AttributeError, e:
sys.stderr.write("[%s] WARNING: Could not fix root directory" % last[2])
vps.boot()
sys.stdout.write("[%s] Booted.\n" % last[2])
send_mail(last[3], "VPS migration finished", mail_vps_success % (last[2], "\n\n".join(vps.get_ip_addresses()), generate_url(last[3])))
c.execute("SELECT * FROM entries WHERE `TargetNode` = ? AND `Finished` = 0 ORDER BY `Position` ASC LIMIT 1", (server[1],))
entries = c.fetchall()
if len(entries) == 0:
c.execute("UPDATE servers SET `Busy` = 3 WHERE `Id` = ?", (server[0],))
sys.stdout.write("Server %s finished migrating.\n" % server[1])
else:
eid = entries[0][0]
c.execute("UPDATE entries SET `Finished` = 1 WHERE `Id` = ?", (eid,))
c.execute("UPDATE servers SET `Busy` = 1, `Current` = `Current` + 1 WHERE `Id` = ?", (server[0],))
send_mail(entries[0][3], "VPS migration started", mail_vps_start % (entries[0][2], generate_url(entries[0][3])))
sys.stdout.write("[%s] Started transfer to %s\n" % (entries[0][2], entries[0][4]))
vps = panel.Vps(entries[0][2])
try:
vps.transfer_to(entries[0][4])
except Exception, e:
c.execute("UPDATE servers SET `Busy` = 4 WHERE `Host` = ?", (entries[0][4],))
c.execute("UPDATE entries SET `Finished` = 3 WHERE `Id` = ?", (entries[0][0],))
send_mail(entries[0][3], "VPS migration failed", mail_vps_error % (entries[0][2], generate_url(entries[0][3])))
sys.stderr.write("[%s] WARNING: Transfer to %s failed! Further transfers for this server have been aborted.\n" % (entries[0][2], entries[0][4]))
else:
c.execute("SELECT * FROM servers WHERE `Busy` != 3 AND `Busy` != 4")
if len(c.fetchall()) == 0:
sys.stdout.write("All migrations done. Check the log for any errors.\n")
exit(0)
time.sleep(5)