Skip to content

Instantly share code, notes, and snippets.

@reillychase
Created November 19, 2018 14:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save reillychase/372763e498261fa592ad6272a85d20c6 to your computer and use it in GitHub Desktop.
Save reillychase/372763e498261fa592ad6272a85d20c6 to your computer and use it in GitHub Desktop.
ghostifi-server-create-function
import MySQLdb
import os
import sys
from sqlalchemy import create_engine, MetaData
from sqlalchemy.sql import text
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy import Column, String, Integer, Date, Table, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
from urllib import quote_plus as urlquote
from multiprocessing.dummy import Pool as ThreadPool
import json
import urllib
import smtplib
from email.mime.text import MIMEText
import requests
import paramiko
import time
# Set PID file, this prevents the script from running if already running
pid = str(os.getpid())
pidfile = "/tmp/server-py.pid"
if os.path.isfile(pidfile):
print "%s already exists, exiting" % pidfile
sys.exit()
file(pidfile, 'w').write(pid)
try:
# Setup SQLAlchemy
engine = create_engine('mysql://user:%s@localhost:3306/ghostifi' % urlquote('password@!'), echo=False)
metadata = MetaData(bind=engine)
Session = scoped_session(sessionmaker(engine, autoflush=True))
Base = declarative_base()
# DB classes
class Subscription(Base):
__tablename__ = 'wp_edd_subscriptions'
id = Column(Integer, primary_key=True)
customer_id = Column(Integer)
period = Column(String)
initial_amount = Column(String)
recurring_amount = Column(String)
bill_times = Column(Integer)
transaction_id = Column(String)
parent_payment_id = Column(Integer)
product_id = Column(Integer)
created = Column(Date)
expiration = Column(Date)
trial_period = Column(String)
status = Column(String)
profile_id = Column(String)
notes = Column(String)
server = relationship("Server", uselist=False, backref="subscription")
class Server(Base):
__tablename__ = 'servers'
id = Column(Integer, primary_key=True)
customer_id = Column(Integer)
product_id = Column(Integer)
wp_edd_sub_id = Column(Integer, ForeignKey(Subscription.id))
server_ip = Column(String)
server_name = Column(String)
email = Column(String)
root_password = Column(String)
bandwidth_this_month = Column(Integer)
bandwidth_limit_this_month = Column(Integer)
current_location = Column(String)
rebuild_schedule = Column(String)
rebuild_schedule_location = Column(String)
rebuild_now_status = Column(Integer)
rebuild_now_location = Column(String)
ovpn_file = Column(String)
status = Column(String)
snapshot_id = Column(String)
username = Column(String)
def __init__(self, customer_id, product_id, wp_edd_sub_id, server_ip, server_name, email, root_password, ovpn_file, status, bandwidth_limit_this_month, vps_plan_id, username):
self.vps_plan_id = vps_plan_id
self.username = username
self.customer_id = customer_id
self.product_id = product_id
self.wp_edd_sub_id = wp_edd_sub_id
self.server_ip = server_ip
self.server_name = "g0" + str(self.wp_edd_sub_id) + ".ghostifi.net"
self.server_subdomain = "g0" + str(self.wp_edd_sub_id)
self.email = email
self.root_password = root_password
self.bandwidth_this_month = 0
self.bandwidth_limit_this_month = 0
self.current_location = "New Jersey"
self.rebuild_schedule = "None"
self.rebuild_schedule_location = "New Jersey"
self.rebuild_now_status = 0
self.rebuild_now_location = "New Jersey"
self.ovpn_file = 'GhostiFi_' + self.server_subdomain + '_UDP-443.ovpn'
self.status = status
self.vultr_api_key = "------"
self.ghostifi_ssh_key_id = "------"
self.rchase_ssh_key_id = "------"
# New Jersey
self.default_dc_id = '1'
# Debian
self.os_id = '244'
self.smtp_ssl_host = "------"
self.smtp_ssl_port = 465
self.smtp_username = "------"
self.smtp_password = "------"
self.smtp_sender = "support@ghostifi.net"
self.cf_zone = "------"
self.cf_api_key = "------"
self.cf_email ="------"
self.cf_url = 'https://api.cloudflare.com/client/v4/zones/' + self.cf_zone + '/dns_records'
self.cf_headers = {'Content-Type': 'application/json', 'X-Auth-Key': self.cf_api_key, 'X-Auth-Email': self.cf_email}
self.cf_create_payload = ''
self.ssh_password = "------"
self.sub_id = ''
self.snapshot_id = ''
def _create_vps(self):
# Create new Vultr VPS: Debian 9, 1024MB, $5/mo, add rchase and ghostifi SSH keys
print "Creating VPS..."
vps_plan_id = self.vps_plan_id
url = 'https://api.vultr.com/v1/server/create'
payload = {'hostname': self.server_name, 'label': self.server_name, 'DCID': self.default_dc_id,
'VPSPLANID': vps_plan_id, 'OSID': self.os_id,
'SSHKEYID': self.ghostifi_ssh_key_id + "," + self.rchase_ssh_key_id}
r = requests.post(url, data=payload, headers={"API-Key": self.vultr_api_key})
self.sub_id = json.loads(r.text)["SUBID"]
return self.sub_id
def _get_vps_status(self):
print "Waiting for VPS to finish setup..."
time_check = 0
while time_check < 500:
time_check += 1
url = 'https://api.vultr.com/v1/server/list'
r = requests.get(url, headers={"API-Key": self.vultr_api_key})
server_state = json.loads(r.text)[self.sub_id]["server_state"]
if server_state != "none":
self.server_ip = json.loads(r.text)[self.sub_id]["main_ip"]
self.cf_create_payload = {'type': 'A', 'name': self.server_name, 'content': self.server_ip}
result = 1
break
else:
result = 0
time.sleep(1)
return result
def _get_root_password(self):
print "Getting root password..."
url = 'https://api.vultr.com/v1/server/list'
r = requests.get(url, headers={"API-Key": self.vultr_api_key})
self.root_password = json.loads(r.text)[self.sub_id]["default_password"]
if self.root_password != None:
print "Root password saved!"
result = 1
else:
print "Error retrieving root password"
result = 0
time.sleep(1)
return result
def _create_cf_dns(self):
print "Creating CloudFlare DNS record..."
r = requests.post(self.cf_url, data=json.dumps(self.cf_create_payload), headers=self.cf_headers)
json_data = json.loads(r.text)
try:
if json_data["result"]["id"]:
return json_data["result"]["id"]
except:
return 0
def _install_openvpn(self):
print "Installing OpenVPN..."
k = paramiko.RSAKey.from_private_key_file("/home/ghostifi/ghostifi.pem", password=self.ssh_password)
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
times_tried = 0
while times_tried < 30:
try:
c.connect(hostname=self.server_ip, username="root", pkey=k)
ssh_connected = 1
break
except Exception as e:
print e
time.sleep(3)
times_tried += 1
print "Trying to connect to SSH again..."
ssh_connected = 0
if ssh_connected == 0:
return 0
commands = ['wget https://raw.githubusercontent.com/GhostiFi/openvpn-install.sh/master/openvpn-install.sh', 'chmod +x /root/openvpn-install.sh', '/bin/bash /root/openvpn-install.sh']
for command in commands:
time.sleep(1)
# print "-----"
print "Executing {}".format(command)
stdin, stdout, stderr = c.exec_command(command)
output = stdout.read()
# print output
# print "Errors:"
errors = stderr.read()
# print errors
# print "-----"
c.close()
def _get_ovpn_file(self):
print "Moving OVPN file to webserver..."
k = paramiko.RSAKey.from_private_key_file("/home/ghostifi/ghostifi.pem", password=self.ssh_password)
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect(hostname=self.server_ip, username="root", pkey=k)
sftp_client = c.open_sftp()
sftp_client.get('/root/GhostiFi.ovpn','/home/ghostifi/ovpn/GhostiFi_' + self.server_subdomain + '_UDP-443.ovpn')
sftp_client.close()
def _create_vps_snapshot(self):
vps_plan_id = self.vps_plan_id
url = 'https://api.vultr.com/v1/snapshot/create'
payload = {'SUBID': self.sub_id}
r = requests.post(url, data=payload, headers={"API-Key": self.vultr_api_key})
self.snapshot_id = json.loads(r.text)["SNAPSHOTID"]
return self.sub_id
def _send_email(self, targets, msg_txt, subject):
print "Sending email..."
msg = MIMEText(msg_txt)
msg['Subject'] = subject
msg['From'] = self.smtp_sender
msg['To'] = ', '.join(targets)
server = smtplib.SMTP_SSL(self.smtp_ssl_host, self.smtp_ssl_port)
server.login(self.smtp_username, self.smtp_password)
server.sendmail(self.smtp_sender, targets, msg.as_string())
server.quit()
print "Email sent!"
def create(self):
self.sub_id = self._create_vps()
vps_status = self._get_vps_status()
if vps_status == 1:
print "VPS setup complete!"
cf_create_status = self._create_cf_dns()
if cf_create_status != 0:
print "CF DNS record created!"
self._install_openvpn()
self._get_ovpn_file()
self._create_vps_snapshot()
self._get_root_password()
self.status = 'Running'
# Send setup complete notification email
targets = [self.smtp_sender, self.email]
msg_txt = 'Thanks for using GhostiFi!\nYour VPS VPN has finished installing. Login at https://ghostifi.net/user to find your OVPN file, as well as instructions on how to get started connecting devices.\n\nUsername: ' + self.username + '\n' + 'Server: ' + self.server_name + '\n\nIf you need any help just reply to this email, and we will get back to you shortly\n\n'
subject = 'VPS VPN server is ready'
self._send_email(targets, msg_txt, subject)
print "Your VPS VPN is ready!"
else:
print "CF DNS record create failed."
else:
print "VPS setup failed."
def destroy(self):
print "Destroyed!"
def rebuild_now(self):
print "Rebuilt!"
# Setup SQLAlchemy stuff
Base.metadata.create_all(engine)
session = Session()
# Create lists which will be populated by SQL queries
servers_to_create = []
servers_to_destroy = []
# Get all active subscriptions, joined to servers
active_subscriptions = session.query(Subscription, Server).outerjoin(Server, Subscription.id == Server.wp_edd_sub_id).filter(Subscription.status=="Active").all()
# Find active subscriptions which do not have existing servers (create these)
for subscription in active_subscriptions:
# If subscription exists for this server, skip, else append to the server create list
if subscription[1]:
pass
else:
servers_to_create.append(subscription[0])
# Get all existing servers, joined to subscriptions
active_servers = session.query(Server, Subscription).outerjoin(Subscription, Subscription.id == Server.wp_edd_sub_id).all()
# Find existing servers which do not have active subscriptions (destroy these)
for server in active_servers:
# If subscription exists for this server, skip, else append to the server destroy list
if server[1]:
pass
else:
servers_to_destroy.append(server[0])
# Get all servers which need to be rebuilt now (rebuild these)
servers_to_rebuild_now = session.query(Server).filter(Server.rebuild_now_status==1).all()
# Make the Pool of workers
pool = ThreadPool(4)
def thread_servers_to_create(self):
# Setup SQLAlchemy
session = Session()
# If product is The VPS VPN Monthly or Yearly, set the VPS plan_id to 201 (25GB SSD/1GB RAM)
if self.product_id == 5747 or self.product_id == 5745:
vps_plan_id = '201'
# Get username, email out of wp_users table. Using a raw query because this is a one-off
sql = text('Select wp_users.user_login, wp_users.user_email from wp_edd_subscriptions left join wp_edd_customers on wp_edd_subscriptions.customer_id = wp_edd_customers.id left join wp_users on wp_edd_customers.user_id = wp_users.id where wp_edd_subscriptions.id = :id')
query = session.execute(sql, {'id': self.id}).fetchone()
username = query[0]
email = query[1]
# Create a new server object
new_server = Server(self.customer_id, self.product_id, self.id, '0.0.0.0', '', email, '', '', 'Installing', 0, vps_plan_id, username)
# Save to database
session.add(new_server)
new_server.create()
session.commit()
# Close session
session.close()
def thread_servers_to_destroy(self):
self.destroy()
def thread_servers_to_rebuild_now(self):
self.rebuild_now()
# Start a thread for each servers_to_create
results = pool.map(thread_servers_to_create, servers_to_create)
# Start a thread for each servers_to_destroy
results = pool.map(thread_servers_to_destroy, servers_to_destroy)
# Start a thread for each servers_to_rebuild_now
results = pool.map(thread_servers_to_rebuild_now, servers_to_rebuild_now)
# close the pool and wait for the work to finish
pool.close()
pool.join()
session.close()
finally:
os.unlink(pidfile)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment