Refactored

This commit is contained in:
jeroenops
2018-11-13 21:55:59 +01:00
commit b36df4384a
129 changed files with 14400 additions and 0 deletions

0
wo/core/__init__.py Normal file
View File

48
wo/core/addswap.py Normal file
View File

@@ -0,0 +1,48 @@
"""WordOps Swap Creation"""
from wo.core.variables import WOVariables
from wo.core.shellexec import WOShellExec
from wo.core.fileutils import WOFileUtils
from wo.core.aptget import WOAptGet
from wo.core.logging import Log
import os
class WOSwap():
"""Manage Swap"""
def __init__():
"""Initialize """
pass
def add(self):
"""Swap addition with WordOps"""
if WOVariables.wo_ram < 512:
if WOVariables.wo_swap < 1000:
Log.info(self, "Adding SWAP file, please wait...")
# Install dphys-swapfile
WOAptGet.update(self)
WOAptGet.install(self, ["dphys-swapfile"])
# Stop service
WOShellExec.cmd_exec(self, "service dphys-swapfile stop")
# Remove Default swap created
WOShellExec.cmd_exec(self, "/sbin/dphys-swapfile uninstall")
# Modify Swap configuration
if os.path.isfile("/etc/dphys-swapfile"):
WOFileUtils.searchreplace(self, "/etc/dphys-swapfile",
"#CONF_SWAPFILE=/var/swap",
"CONF_SWAPFILE=/wo-swapfile")
WOFileUtils.searchreplace(self, "/etc/dphys-swapfile",
"#CONF_MAXSWAP=2048",
"CONF_MAXSWAP=1024")
WOFileUtils.searchreplace(self, "/etc/dphys-swapfile",
"#CONF_SWAPSIZE=",
"CONF_SWAPSIZE=1024")
else:
with open("/etc/dphys-swapfile", 'w') as conffile:
conffile.write("CONF_SWAPFILE=/wo-swapfile\n"
"CONF_SWAPSIZE=1024\n"
"CONF_MAXSWAP=1024\n")
# Create swap file
WOShellExec.cmd_exec(self, "service dphys-swapfile start")

91
wo/core/apt_repo.py Normal file
View File

@@ -0,0 +1,91 @@
"""WordOps packages repository operations"""
from wo.core.shellexec import WOShellExec
from wo.core.variables import WOVariables
from wo.core.logging import Log
import os
class WORepo():
"""Manage Repositories"""
def __init__(self):
"""Initialize """
pass
def add(self, repo_url=None, ppa=None):
"""
This function used to add apt repositories and or ppa's
If repo_url is provided adds repo file to
/etc/apt/sources.list.d/
If ppa is provided add apt-repository using
add-apt-repository
command.
"""
if repo_url is not None:
repo_file_path = ("/etc/apt/sources.list.d/"
+ WOVariables().wo_repo_file)
try:
if not os.path.isfile(repo_file_path):
with open(repo_file_path,
encoding='utf-8', mode='a') as repofile:
repofile.write(repo_url)
repofile.write('\n')
repofile.close()
elif repo_url not in open(repo_file_path,
encoding='utf-8').read():
with open(repo_file_path,
encoding='utf-8', mode='a') as repofile:
repofile.write(repo_url)
repofile.write('\n')
repofile.close()
return True
except IOError as e:
Log.debug(self, "{0}".format(e))
Log.error(self, "File I/O error.")
except Exception as e:
Log.debug(self, "{0}".format(e))
Log.error(self, "Unable to add repo")
if ppa is not None:
WOShellExec.cmd_exec(self, "add-apt-repository -y '{ppa_name}'"
.format(ppa_name=ppa))
def remove(self, ppa=None, repo_url=None):
"""
This function used to remove ppa's
If ppa is provided adds repo file to
/etc/apt/sources.list.d/
command.
"""
if ppa:
WOShellExec.cmd_exec(self, "add-apt-repository -y "
"--remove '{ppa_name}'"
.format(ppa_name=ppa))
elif repo_url:
repo_file_path = ("/etc/apt/sources.list.d/"
+ WOVariables().wo_repo_file)
try:
repofile = open(repo_file_path, "w+")
repofile.write(repofile.read().replace(repo_url, ""))
repofile.close()
except IOError as e:
Log.debug(self, "{0}".format(e))
Log.error(self, "File I/O error.")
except Exception as e:
Log.debug(self, "{0}".format(e))
Log.error(self, "Unable to remove repo")
def add_key(self, keyids, keyserver=None):
"""
This function adds imports repository keys from keyserver.
default keyserver is hkp://keys.gnupg.net
user can provide other keyserver with keyserver="hkp://xyz"
"""
WOShellExec.cmd_exec(self, "gpg --keyserver {serv}"
.format(serv=(keyserver or
"hkp://keys.gnupg.net"))
+ " --recv-keys {key}".format(key=keyids))
WOShellExec.cmd_exec(self, "gpg -a --export --armor {0}"
.format(keyids)
+ " | apt-key add - ")

231
wo/core/aptget.py Normal file
View File

@@ -0,0 +1,231 @@
"""WordOps package installation using apt-get module."""
import apt
import apt_pkg
import sys
import subprocess
from wo.core.logging import Log
from wo.core.apt_repo import WORepo
from sh import apt_get
from sh import ErrorReturnCode
class WOAptGet():
"""Generic apt-get intialisation"""
def update(self):
"""
Similar to `apt-get update`
"""
try:
with open('/var/log/wo/wordops.log', 'a') as f:
proc = subprocess.Popen('apt-get update',
shell=True,
stdin=None, stdout=f,
stderr=subprocess.PIPE,
executable="/bin/bash")
proc.wait()
output, error_output = proc.communicate()
# Check what is error in error_output
if "NO_PUBKEY" in str(error_output):
# Split the output
Log.info(self, "Fixing missing GPG keys, please wait...")
error_list = str(error_output).split("\\n")
# Use a loop to add misising keys
for single_error in error_list:
if "NO_PUBKEY" in single_error:
key = single_error.rsplit(None, 1)[-1]
WORepo.add_key(self, key, keyserver="hkp://pgp.mit.edu")
proc = subprocess.Popen('apt-get update',
shell=True,
stdin=None, stdout=f, stderr=f,
executable="/bin/bash")
proc.wait()
if proc.returncode == 0:
return True
else:
Log.info(self, Log.FAIL + "Whoops, something went wrong...")
Log.error(self, "Check the WordOps log for more details "
"`tail /var/log/wo/wordops.log` and please try again...")
except Exception as e:
Log.error(self, "apt-get update exited with error")
def check_upgrade(self):
"""
Similar to `apt-get upgrade`
"""
try:
check_update = subprocess.Popen(['apt-get upgrade -s | grep '
'\"^Inst\" | wc -l'],
stdout=subprocess.PIPE,
shell=True).communicate()[0]
if check_update == b'0\n':
Log.error(self, "No package updates available")
Log.info(self, "Following package updates are available:")
subprocess.Popen("apt-get -s dist-upgrade | grep \"^Inst\"",
shell=True, executable="/bin/bash",
stdout=sys.stdout).communicate()
except Exception as e:
Log.error(self, "Unable to check for packages upgrades")
def dist_upgrade(self):
"""
Similar to `apt-get upgrade`
"""
try:
with open('/var/log/wo/wordops.log', 'a') as f:
proc = subprocess.Popen("DEBIAN_FRONTEND=noninteractive "
"apt-get dist-upgrade -o "
"Dpkg::Options::=\"--force-confdef\""
" -o "
"Dpkg::Options::=\"--force-confold\""
" -y ",
shell=True, stdin=None,
stdout=f, stderr=f,
executable="/bin/bash")
proc.wait()
if proc.returncode == 0:
return True
else:
Log.info(self, Log.FAIL + "Oops Something went "
"wrong!!")
Log.error(self, "Check the WordOps log for more details "
"`tail /var/log/wo/wordops.log` and please try again...")
except Exception as e:
Log.error(self, "Error while installing packages, "
"apt-get exited with error")
def install(self, packages):
all_packages = ' '.join(packages)
try:
with open('/var/log/wo/wordops.log', 'a') as f:
proc = subprocess.Popen("DEBIAN_FRONTEND=noninteractive "
"apt-get install -o "
"Dpkg::Options::=\"--force-confdef\""
" -o "
"Dpkg::Options::=\"--force-confold\""
" -y --allow-unauthenticated {0}"
.format(all_packages), shell=True,
stdin=None, stdout=f, stderr=f,
executable="/bin/bash")
proc.wait()
if proc.returncode == 0:
return True
else:
Log.info(self, Log.FAIL + "Oops Something went "
"wrong!!")
Log.error(self, "Check the WordOps log for more details "
"`tail /var/log/wo/wordops.log` and please try again...")
except Exception as e:
Log.info(self, Log.FAIL + "Oops Something went "
"wrong!!")
Log.error(self, "Check the WordOps log for more details "
"`tail /var/log/wo/wordops.log` and please try again...")
def remove(self, packages, auto=False, purge=False):
all_packages = ' '.join(packages)
try:
with open('/var/log/wo/wordops.log', 'a') as f:
if purge:
proc = subprocess.Popen('apt-get purge -y {0}'
.format(all_packages), shell=True,
stdin=None, stdout=f, stderr=f,
executable="/bin/bash")
else:
proc = subprocess.Popen('apt-get remove -y {0}'
.format(all_packages), shell=True,
stdin=None, stdout=f, stderr=f,
executable="/bin/bash")
proc.wait()
if proc.returncode == 0:
return True
else:
Log.info(self, Log.FAIL + "Oops Something went "
"wrong!!")
Log.error(self, "Check the WordOps log for more details "
"`tail /var/log/wo/wordops.log` and please try again...")
except Exception as e:
Log.error(self, "Error while installing packages, "
"apt-get exited with error")
def auto_clean(self):
"""
Similar to `apt-get autoclean`
"""
try:
orig_out = sys.stdout
sys.stdout = open(self.app.config.get('log.logging', 'file'),
encoding='utf-8', mode='a')
apt_get.autoclean("-y")
sys.stdout = orig_out
except ErrorReturnCode as e:
Log.debug(self, "{0}".format(e))
Log.error(self, "Unable to apt-get autoclean")
def auto_remove(self):
"""
Similar to `apt-get autoremove`
"""
try:
Log.debug(self, "Running apt-get autoremove")
apt_get.autoremove("-y")
except ErrorReturnCode as e:
Log.debug(self, "{0}".format(e))
Log.error(self, "Unable to apt-get autoremove")
def is_installed(self, package_name):
"""
Checks if package is available in cache and is installed or not
returns True if installed otherwise returns False
"""
apt_cache = apt.cache.Cache()
apt_cache.open()
if (package_name.strip() in apt_cache and
apt_cache[package_name.strip()].is_installed):
# apt_cache.close()
return True
# apt_cache.close()
return False
def download_only(self,package_name,repo_url=None,repo_key=None):
"""
Similar to `apt-get install --download-only PACKAGE_NAME`
"""
packages = ' '.join(package_name)
try:
with open('/var/log/wo/wordops.log', 'a') as f:
if repo_url is not None:
WORepo.add(self, repo_url=repo_url)
if repo_key is not None:
WORepo.add_key(self, repo_key)
proc = subprocess.Popen("apt-get update && DEBIAN_FRONTEND=noninteractive "
"apt-get install -o "
"Dpkg::Options::=\"--force-confdef\""
" -o "
"Dpkg::Options::=\"--force-confold\""
" -y --download-only {0}"
.format(packages), shell=True,
stdin=None, stdout=f, stderr=f,
executable="/bin/bash")
proc.wait()
if proc.returncode == 0:
return True
else:
Log.error(self,"Error in fetching dpkg package.\nReverting changes ..",False)
if repo_url is not None:
WORepo.remove(self, repo_url=repo_url)
return False
except Exception as e:
Log.error(self, "Error while downloading packages, "
"apt-get exited with error")

23
wo/core/checkfqdn.py Normal file
View File

@@ -0,0 +1,23 @@
from wo.core.shellexec import WOShellExec
from wo.core.variables import WOVariables
import os
def check_fqdn(self, wo_host):
"""FQDN check with WordOps, for mail server hostname must be FQDN"""
# wo_host=os.popen("hostname -f | tr -d '\n'").read()
if '.' in wo_host:
WOVariables.wo_fqdn = wo_host
with open('/etc/hostname', encoding='utf-8', mode='w') as hostfile:
hostfile.write(wo_host)
WOShellExec.cmd_exec(self, "sed -i \"1i\\127.0.0.1 {0}\" /etc/hosts"
.format(wo_host))
if WOVariables.wo_platform_distro == 'debian':
WOShellExec.cmd_exec(self, "/etc/init.d/hostname.sh start")
else:
WOShellExec.cmd_exec(self, "service hostname restart")
else:
wo_host = input("Enter hostname [fqdn]:")
check_fqdn(self, wo_host)

32
wo/core/cron.py Normal file
View File

@@ -0,0 +1,32 @@
from wo.core.shellexec import WOShellExec
from wo.core.logging import Log
"""
Set CRON on LINUX system.
"""
class WOCron():
def setcron_weekly(self,cmd,comment='Cron set by WordOps',user='root',min=0,hour=12):
if not WOShellExec.cmd_exec(self, "crontab -l | grep -q \'{0}\'".format(cmd)):
WOShellExec.cmd_exec(self, "/bin/bash -c \"crontab -l "
"2> /dev/null | {{ cat; echo -e"
" \\\""
"\\n0 0 * * 0 "
"{0}".format(cmd) +
" # {0}".format(comment)+
"\\\"; } | crontab -\"")
Log.debug(self, "Cron set")
def remove_cron(self,cmd):
if WOShellExec.cmd_exec(self, "crontab -l | grep -q \'{0}\'".format(cmd)):
if not WOShellExec.cmd_exec(self, "/bin/bash -c "
"\"crontab "
"-l | sed '/{0}/d'"
"| crontab -\""
.format(cmd)):
Log.error(self, "Failed to remove crontab entry",False)
else:
Log.debug(self, "Cron not found")

28
wo/core/database.py Normal file
View File

@@ -0,0 +1,28 @@
"""WordOps generic database creation module"""
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from wo.core.variables import WOVariables
# db_path = self.app.config.get('site', 'db_path')
engine = create_engine(WOVariables.wo_db_uri, convert_unicode=True)
db_session = scoped_session(sessionmaker(autocommit=False,
autoflush=False,
bind=engine))
Base = declarative_base()
Base.query = db_session.query_property()
def init_db(app):
"""
Initializes and creates all tables from models into the database
"""
# import all modules here that might define models so that
# they will be registered properly on the metadata. Otherwise
# # you will have to import them first before calling init_db()
# import wo.core.models
try:
app.log.info("Initializing WordOps Database")
Base.metadata.create_all(bind=engine)
except Exception as e:
app.log.debug("{0}".format(e))

24
wo/core/domainvalidate.py Normal file
View File

@@ -0,0 +1,24 @@
"""WordOps domain validation module."""
from urllib.parse import urlparse
def ValidateDomain(url):
"""
This function returns domain name removing http:// and https://
returns domain name only with or without www as user provided.
"""
# Check if http:// or https:// present remove it if present
domain_name = url.split('/')
if 'http:' in domain_name or 'https:' in domain_name:
domain_name = domain_name[2]
else:
domain_name = domain_name[0]
www_domain_name = domain_name.split('.')
final_domain = ''
if www_domain_name[0] == 'www':
final_domain = '.'.join(www_domain_name[1:])
else:
final_domain = domain_name
return (final_domain, domain_name)

45
wo/core/download.py Normal file
View File

@@ -0,0 +1,45 @@
"""WordOps download core classes."""
import urllib.request
import urllib.error
import os
from wo.core.logging import Log
class WODownload():
"""Method to download using urllib"""
def __init__():
pass
def download(self, packages):
"""Download packages, packges must be list in format of
[url, path, package name]"""
for package in packages:
url = package[0]
filename = package[1]
pkg_name = package[2]
try:
directory = os.path.dirname(filename)
if not os.path.exists(directory):
os.makedirs(directory)
Log.info(self, "Downloading {0:20}".format(pkg_name), end=' ')
req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
with urllib.request.urlopen(req) as response, open(filename, 'wb') as out_file:
out_file.write(response.read())
Log.info(self, "{0}".format("[" + Log.ENDC + "Done"
+ Log.OKBLUE + "]"))
except urllib.error.URLError as e:
Log.debug(self, "[{err}]".format(err=str(e.reason)))
Log.error(self, "Unable to download file, {0}"
.format(filename))
return False
except urllib.error.HTTPError as e:
Log.error(self, "Package download failed. {0}"
.format(pkg_name))
Log.debug(self, "[{err}]".format(err=str(e.reason)))
return False
except urllib.error.ContentTooShortError as e:
Log.debug(self, "{0}{1}".format(e.errno, e.strerror))
Log.error(self, "Package download failed. The amount of the"
" downloaded data is less than "
"the expected amount \{0} ".format(pkg_name))
return False

26
wo/core/exc.py Normal file
View File

@@ -0,0 +1,26 @@
"""WordOps exception classes."""
class WOError(Exception):
"""Generic errors."""
def __init__(self, msg):
Exception.__init__(self)
self.msg = msg
def __str__(self):
return self.msg
class WOConfigError(WOError):
"""Config related errors."""
pass
class WORuntimeError(WOError):
"""Generic runtime errors."""
pass
class WOArgumentError(WOError):
"""Argument related errors."""
pass

21
wo/core/extract.py Normal file
View File

@@ -0,0 +1,21 @@
"""WordOps Extract Core """
import tarfile
import os
from wo.core.logging import Log
class WOExtract():
"""Method to extract from tar.gz file"""
def extract(self, file, path):
"""Function to extract tar.gz file"""
try:
tar = tarfile.open(file)
tar.extractall(path=path)
tar.close()
os.remove(file)
return True
except tarfile.TarError as e:
Log.debug(self, "{0}".format(e))
Log.error(self, 'Unable to extract file \{0}'.format(file))
return False

268
wo/core/fileutils.py Normal file
View File

@@ -0,0 +1,268 @@
"""WordOps file utils core classes."""
import shutil
import os
import sys
import glob
import shutil
import pwd
import fileinput
from wo.core.logging import Log
class WOFileUtils():
"""Utilities to operate on files"""
def __init__():
pass
def remove(self, filelist):
"""remove files from given path"""
for file in filelist:
if os.path.isfile(file):
Log.info(self, "Removing {0:65}".format(file), end=' ')
os.remove(file)
Log.info(self, "{0}".format("[" + Log.ENDC + "Done" +
Log.OKBLUE + "]"))
Log.debug(self, 'file Removed')
if os.path.isdir(file):
try:
Log.info(self, "Removing {0:65}".format(file), end=' ')
shutil.rmtree(file)
Log.info(self, "{0}".format("[" + Log.ENDC + "Done" +
Log.OKBLUE + "]"))
except shutil.Error as e:
Log.debug(self, "{err}".format(err=str(e.reason)))
Log.error(self, 'Unable to Remove file ')
def create_symlink(self, paths, errormsg=''):
"""
Create symbolic links provided in list with first as source
and second as destination
"""
src = paths[0]
dst = paths[1]
if not os.path.islink(dst):
try:
Log.debug(self, "Creating Symbolic link, Source:{0}, Dest:{1}"
.format(src, dst))
os.symlink(src, dst)
except Exception as e:
Log.debug(self, "{0}{1}".format(e.errno, e.strerror))
Log.error(self, "Unable to create symbolic link ...\n ")
else:
Log.debug(self, "Destination: {0} exists".format(dst))
def remove_symlink(self, filepath):
"""
Removes symbolic link for the path provided with filepath
"""
try:
Log.debug(self, "Removing symbolic link: {0}".format(filepath))
os.unlink(filepath)
except Exception as e:
Log.debug(self, "{0}".format(e))
Log.error(self, "Unable to reomove symbolic link ...\n")
def copyfiles(self, src, dest):
"""
Copies files:
src : source path
dest : destination path
Recursively copy an entire directory tree rooted at src.
The destination directory, named by dst, must not already exist;
it will be created as well as missing parent directories.
"""
try:
Log.debug(self, "Copying files, Source:{0}, Dest:{1}"
.format(src, dest))
shutil.copytree(src, dest)
except shutil.Error as e:
Log.debug(self, "{0}".format(e))
Log.error(self, 'Unable to copy files from {0} to {1}'
.format(src, dest))
except IOError as e:
Log.debug(self, "{0}".format(e.strerror))
Log.error(self, "Unable to copy files from {0} to {1}"
.format(src, dest))
def copyfile(self, src, dest):
"""
Copy file:
src : source path
dest : destination path
"""
try:
Log.debug(self, "Copying file, Source:{0}, Dest:{1}"
.format(src, dest))
shutil.copy2(src, dest)
except shutil.Error as e:
Log.debug(self, "{0}".format(e))
Log.error(self, 'Unable to copy file from {0} to {1}'
.format(src, dest))
except IOError as e:
Log.debug(self, "{0}".format(e.strerror))
Log.error(self, "Unable to copy file from {0} to {1}"
.format(src, dest))
def searchreplace(self, fnm, sstr, rstr):
"""
Search replace strings in file
fnm : filename
sstr: search string
rstr: replace string
"""
try:
Log.debug(self, "Doning search and replace, File:{0},"
"Source string:{1}, Dest String:{2}"
.format(fnm, sstr, rstr))
for line in fileinput.input(fnm, inplace=True):
print(line.replace(sstr, rstr), end='')
fileinput.close()
except Exception as e:
Log.debug(self, "{0}".format(e))
Log.error(self, "Unable to search {0} and replace {1} {2}"
.format(fnm, sstr, rstr))
def mvfile(self, src, dst):
"""
Moves file from source path to destination path
src : source path
dst : Destination path
"""
try:
Log.debug(self, "Moving file from {0} to {1}".format(src, dst))
shutil.move(src, dst)
except Exception as e:
Log.debug(self, "{err}".format(err=e))
Log.error(self, 'Unable to move file from {0} to {1}'
.format(src, dst))
def chdir(self, path):
"""
Change Directory to path specified
Path : path for destination directory
"""
try:
Log.debug(self, "Changing directory to {0}"
.format(path))
os.chdir(path)
except OSError as e:
Log.debug(self, "{err}".format(err=e.strerror))
Log.error(self, 'Unable to Change Directory {0}'.format(path))
def chown(self, path, user, group, recursive=False):
"""
Change Owner for files
change owner for file with path specified
user: username of owner
group: group of owner
recursive: if recursive is True change owner for all
files in directory
"""
userid = pwd.getpwnam(user)[2]
groupid = pwd.getpwnam(user)[3]
try:
Log.debug(self, "Changing ownership of {0}, Userid:{1},Groupid:{2}"
.format(path, userid, groupid))
# Change inside files/directory permissions only if recursive flag
# is set
if recursive:
for root, dirs, files in os.walk(path):
for d in dirs:
os.chown(os.path.join(root, d), userid,
groupid)
for f in files:
os.chown(os.path.join(root, f), userid,
groupid)
os.chown(path, userid, groupid)
except shutil.Error as e:
Log.debug(self, "{0}".format(e))
Log.error(self, "Unable to change owner : {0}".format(path))
except Exception as e:
Log.debug(self, "{0}".format(e))
Log.error(self, "Unable to change owner : {0} ".format(path))
def chmod(self, path, perm, recursive=False):
"""
Changes Permission for files
path : file path permission to be changed
perm : permissions to be given
recursive: change permission recursively for all files
"""
try:
Log.debug(self, "Changing permission of {0}, Perm:{1}"
.format(path, perm))
if recursive:
for root, dirs, files in os.walk(path):
for d in dirs:
os.chmod(os.path.join(root, d), perm)
for f in files:
os.chmod(os.path.join(root, f), perm)
else:
os.chmod(path, perm)
except OSError as e:
Log.debug(self, "{0}".format(e.strerror))
Log.error(self, "Unable to change owner : {0}".format(path))
def mkdir(self, path):
"""
create directories.
path : path for directory to be created
Similar to `mkdir -p`
"""
try:
Log.debug(self, "Creating directories: {0}"
.format(path))
os.makedirs(path)
except OSError as e:
Log.debug(self, "{0}".format(e.strerror))
Log.error(self, "Unable to create directory {0} ".format(path))
def isexist(self, path):
"""
Check if file exist on given path
"""
try:
if os.path.exists(path):
return (True)
else:
return (False)
except OSError as e:
Log.debug(self, "{0}".format(e.strerror))
Log.error(self, "Unable to check path {0}".format(path))
def grep(self, fnm, sstr):
"""
Searches for string in file and returns the matched line.
"""
try:
Log.debug(self, "Finding string {0} to file {1}"
.format(sstr, fnm))
for line in open(fnm, encoding='utf-8'):
if sstr in line:
return line
return False
except OSError as e:
Log.debug(self, "{0}".format(e.strerror))
Log.error(self, "Unable to Search string {0} in {1}"
.format(sstr, fnm))
def rm(self, path):
"""
Remove files
"""
Log.debug(self, "Removing {0}".format(path))
if WOFileUtils.isexist(self, path):
try:
if os.path.isdir(path):
shutil.rmtree(path)
else:
os.remove(path)
except shutil.Error as e:
Log.debug(self, "{0}".format(e))
Log.error(self, "Unable to remove directory : {0} "
.format(path))
except OSError as e:
Log.debug(self, "{0}".format(e))
Log.error(self, "Unable to remove file : {0} "
.format(path))

57
wo/core/git.py Normal file
View File

@@ -0,0 +1,57 @@
"""WordOps GIT module"""
from sh import git, ErrorReturnCode
from wo.core.logging import Log
import os
class WOGit:
"""Intialization of core variables"""
def ___init__():
# TODO method for core variables
pass
def add(self, paths, msg="Intializating"):
"""
Initializes Directory as repository if not already git repo.
and adds uncommited changes automatically
"""
for path in paths:
global git
git = git.bake("--git-dir={0}/.git".format(path),
"--work-tree={0}".format(path))
if os.path.isdir(path):
if not os.path.isdir(path+"/.git"):
try:
Log.debug(self, "WOGit: git init at {0}"
.format(path))
git.init(path)
except ErrorReturnCode as e:
Log.debug(self, "{0}".format(e))
Log.error(self, "Unable to git init at {0}"
.format(path))
status = git.status("-s")
if len(status.splitlines()) > 0:
try:
Log.debug(self, "WOGit: git commit at {0}"
.format(path))
git.add("--all")
git.commit("-am {0}".format(msg))
except ErrorReturnCode as e:
Log.debug(self, "{0}".format(e))
Log.error(self, "Unable to git commit at {0} "
.format(path))
else:
Log.debug(self, "WOGit: Path {0} not present".format(path))
def checkfilestatus(self, repo, filepath):
"""
Checks status of file, If its tracked or untracked.
"""
global git
git = git.bake("--git-dir={0}/.git".format(repo),
"--work-tree={0}".format(repo))
status = git.status("-s", "{0}".format(filepath))
if len(status.splitlines()) > 0:
return True
else:
return False

47
wo/core/logging.py Normal file
View File

@@ -0,0 +1,47 @@
"""WordOps log module"""
class Log:
"""
Logs messages with colors for different messages
according to functions
"""
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
def error(self, msg, exit=True):
"""
Logs error into log file
"""
print(Log.FAIL + msg + Log.ENDC)
self.app.log.error(Log.FAIL + msg + Log.ENDC)
if exit:
self.app.close(1)
def info(self, msg, end='\n', log=True):
"""
Logs info messages into log file
"""
print(Log.OKBLUE + msg + Log.ENDC, end=end)
if log:
self.app.log.info(Log.OKBLUE + msg + Log.ENDC)
def warn(self, msg):
"""
Logs warning into log file
"""
print(Log.WARNING + msg + Log.ENDC)
self.app.log.warn(Log.BOLD + msg + Log.ENDC)
def debug(self, msg):
"""
Logs debug messages into log file
"""
self.app.log.debug(Log.HEADER + msg + Log.ENDC)

195
wo/core/logwatch.py Normal file
View File

@@ -0,0 +1,195 @@
"""
Real time log files watcher supporting log rotation.
"""
import os
import time
import errno
import stat
from wo.core.logging import Log
class LogWatcher(object):
"""Looks for changes in all files of a directory.
This is useful for watching log file changes in real-time.
It also supports files rotation.
Example:
>>> def callback(filename, lines):
... print filename, lines
...
>>> l = LogWatcher("/var/www/example.com/logs", callback)
>>> l.loop()
"""
def __init__(self, filelist, callback, extensions=["log"], tail_lines=0):
"""Arguments:
(str) @folder:
the folder to watch
(callable) @callback:
a function which is called every time a new line in a
file being watched is found;
this is called with "filename" and "lines" arguments.
(list) @extensions:
only watch files with these extensions
(int) @tail_lines:
read last N lines from files being watched before starting
"""
self.files_map = {}
self.filelist = filelist
self.callback = callback
# self.folder = os.path.realpath(folder)
self.extensions = extensions
# assert (os.path.isdir(self.folder), "%s does not exists"
# % self.folder)
for file in self.filelist:
assert (os.path.isfile(file))
assert callable(callback)
self.update_files()
# The first time we run the script we move all file markers at EOF.
# In case of files created afterwards we don't do this.
for id, file in list(iter(self.files_map.items())):
file.seek(os.path.getsize(file.name)) # EOF
if tail_lines:
lines = self.tail(file.name, tail_lines)
if lines:
self.callback(file.name, lines)
def __del__(self):
self.close()
def loop(self, interval=0.1, async=False):
"""Start the loop.
If async is True make one loop then return.
"""
while 1:
self.update_files()
for fid, file in list(iter(self.files_map.items())):
self.readfile(file)
if async:
return
time.sleep(interval)
def log(self, line):
"""Log when a file is un/watched"""
print(line)
# def listdir(self):
# """List directory and filter files by extension.
# You may want to override this to add extra logic or
# globbling support.
# """
# ls = os.listdir(self.folder)
# if self.extensions:
# return ([x for x in ls if os.path.splitext(x)[1][1:]
# in self.extensions])
# else:
# return ls
@staticmethod
def tail(fname, window):
"""Read last N lines from file fname."""
try:
f = open(fname, encoding='utf-8', mode='r')
except IOError as err:
if err.errno == errno.ENOENT:
return []
else:
raise
else:
BUFSIZ = 1024
f.seek(0, os.SEEK_END)
fsize = f.tell()
block = -1
data = ""
exit = False
while not exit:
step = (block * BUFSIZ)
if abs(step) >= fsize:
f.seek(0)
exit = True
else:
f.seek(step, os.SEEK_END)
data = f.read().strip()
if data.count('\n') >= window:
break
else:
block -= 1
return data.splitlines()[-window:]
def update_files(self):
ls = []
for name in self.filelist:
absname = os.path.realpath(os.path.join(name))
try:
st = os.stat(absname)
except EnvironmentError as err:
if err.errno != errno.ENOENT:
raise
else:
if not stat.S_ISREG(st.st_mode):
continue
fid = self.get_file_id(st)
ls.append((fid, absname))
# check existent files
for fid, file in list(iter(self.files_map.items())):
# next(iter(graph.items()))
try:
st = os.stat(file.name)
except EnvironmentError as err:
if err.errno == errno.ENOENT:
self.unwatch(file, fid)
else:
raise
else:
if fid != self.get_file_id(st):
# same name but different file (rotation); reload it.
self.unwatch(file, fid)
self.watch(file.name)
# add new ones
for fid, fname in ls:
if fid not in self.files_map:
self.watch(fname)
def readfile(self, file):
lines = file.readlines()
if lines:
self.callback(file.name, lines)
def watch(self, fname):
try:
file = open(fname, encoding='utf-8', mode='r')
fid = self.get_file_id(os.stat(fname))
except EnvironmentError as err:
if err.errno != errno.ENOENT:
raise
else:
self.log("watching logfile %s" % fname)
self.files_map[fid] = file
def unwatch(self, file, fid):
# file no longer exists; if it has been renamed
# try to read it for the last time in case the
# log rotator has written something in it.
lines = self.readfile(file)
self.log("un-watching logfile %s" % file.name)
del self.files_map[fid]
if lines:
self.callback(file.name, lines)
@staticmethod
def get_file_id(st):
return "%xg%x" % (st.st_dev, st.st_ino)
def close(self):
for id, file in list(iter(self.files_map.items())):
file.close()
self.files_map.clear()

136
wo/core/mysql.py Normal file
View File

@@ -0,0 +1,136 @@
"""WordOps MySQL core classes."""
import pymysql
from pymysql import connections, DatabaseError, Error
import configparser
from os.path import expanduser
import sys
import os
from wo.core.logging import Log
from wo.core.variables import WOVariables
class MySQLConnectionError(Exception):
"""Custom Exception when MySQL server Not Connected"""
pass
class StatementExcecutionError(Exception):
"""Custom Exception when any Query Fails to execute"""
pass
class DatabaseNotExistsError(Exception):
"""Custom Exception when Database not Exist"""
pass
class WOMysql():
"""Method for MySQL connection"""
def connect(self):
"""Makes connection with MySQL server"""
try:
if os.path.exists('/etc/mysql/conf.d/my.cnf'):
connection = pymysql.connect(read_default_file='/etc/mysql/conf.d/my.cnf')
else:
connection = pymysql.connect(read_default_file='~/.my.cnf')
return connection
except ValueError as e:
Log.debug(self, str(e))
raise MySQLConnectionError
except pymysql.err.InternalError as e:
Log.debug(self, str(e))
raise MySQLConnectionError
def dbConnection(self, db_name):
try:
if os.path.exists('/etc/mysql/conf.d/my.cnf'):
connection = pymysql.connect(db=db_name,read_default_file='/etc/mysql/conf.d/my.cnf')
else:
connection = pymysql.connect(db=db_name,read_default_file='~/.my.cnf')
return connection
except DatabaseError as e:
if e.args[1] == '#42000Unknown database \'{0}\''.format(db_name):
raise DatabaseNotExistsError
else:
raise MySQLConnectionError
except pymysql.err.InternalError as e:
Log.debug(self, str(e))
raise MySQLConnectionError
except Exception as e :
Log.debug(self, "[Error]Setting up database: \'" + str(e) + "\'")
raise MySQLConnectionError
def execute(self, statement, errormsg='', log=True):
"""Get login details from /etc/mysql/conf.d/my.cnf & Execute MySQL query"""
connection = WOMysql.connect(self)
log and Log.debug(self, "Exceuting MySQL Statement : {0}"
.format(statement))
try:
cursor = connection.cursor()
sql = statement
cursor.execute(sql)
# connection is not autocommit by default.
# So you must commit to save your changes.
connection.commit()
except AttributeError as e:
Log.debug(self, str(e))
raise StatementExcecutionError
except Error as e:
Log.debug(self, str(e))
raise StatementExcecutionError
finally:
connection.close()
def backupAll(self):
import subprocess
try:
Log.info(self, "Backing up database at location: "
"/var/wo-mysqlbackup")
# Setup Nginx common directory
if not os.path.exists('/var/wo-mysqlbackup'):
Log.debug(self, 'Creating directory'
'/var/wo-mysqlbackup')
os.makedirs('/var/wo-mysqlbackup')
db = subprocess.check_output(["mysql -Bse \'show databases\'"],
universal_newlines=True,
shell=True).split('\n')
for dbs in db:
if dbs == "":
continue
Log.info(self, "Backing up {0} database".format(dbs))
p1 = subprocess.Popen("mysqldump {0}"
" --max_allowed_packet=1024M"
" --single-transaction".format(dbs),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE, shell=True)
p2 = subprocess.Popen("gzip -c > /var/wo-mysqlbackup/{0}{1}.s"
"ql.gz".format(dbs, WOVariables.wo_date),
stdin=p1.stdout,
shell=True)
# Allow p1 to receive a SIGPIPE if p2 exits
p1.stdout.close()
output = p1.stderr.read()
p1.wait()
if p1.returncode == 0:
Log.debug(self, "done")
else:
Log.error(self, output.decode("utf-8"))
except Exception as e:
Log.error(self, "Error: process exited with status %s"
% e)
def check_db_exists(self, db_name):
try:
if WOMysql.dbConnection(self, db_name):
return True
except DatabaseNotExistsError as e:
Log.debug(self, str(e))
return False
except MySQLConnectionError as e:
Log.debug(self, str(e))
return False

View File

@@ -0,0 +1,44 @@
"""WordOps Hash Bucket Calculator"""
from wo.core.fileutils import WOFileUtils
import math
import os
import fileinput
import re
import subprocess
def hashbucket(self):
# Check Nginx Hashbucket error
sub = subprocess.Popen('nginx -t', stdout=subprocess.PIPE,
stderr=subprocess.PIPE, shell=True)
output, error_output = sub.communicate()
if 'server_names_hash_bucket_size' not in str(error_output):
return True
count = 0
# Get the list of sites-availble
sites_list = os.listdir("/etc/nginx/sites-enabled/")
# Count the number of characters in site names
for site in sites_list:
count = sum([count, len(site)])
# Calculate Nginx hash bucket size
ngx_calc = math.trunc(sum([math.log(count, 2), 2]))
ngx_hash = math.trunc(math.pow(2, ngx_calc))
# Replace hashbucket in Nginx.conf file
if WOFileUtils.grep(self, "/etc/nginx/nginx.conf",
"server_names_hash_bucket_size"):
for line in fileinput.FileInput("/etc/nginx/nginx.conf", inplace=1):
if "server_names_hash_bucket_size" in line:
print("\tserver_names_hash_bucket_size {0};".format(ngx_hash))
else:
print(line, end='')
else:
WOFileUtils.searchreplace(self, '/etc/nginx/nginx.conf',
"gzip_disable \"msie6\";",
"gzip_disable \"msie6\";\n"
"\tserver_names_hash_bucket_size {0};\n"
.format(ngx_hash))

33
wo/core/sendmail.py Normal file
View File

@@ -0,0 +1,33 @@
import smtplib
import os
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email.utils import COMMASPACE, formatdate
from email import encoders
def WOSendMail(send_from, send_to, subject, text, files, server="localhost",
port=587, username='', password='', isTls=True):
msg = MIMEMultipart()
msg['From'] = send_from
msg['To'] = send_to
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = subject
msg.attach(MIMEText(text))
for f in files:
part = MIMEBase('application', "octet-stream")
part.set_payload(open(f, "rb").read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="{0}"'
.format(os.path.basename(f)))
msg.attach(part)
smtp = smtplib.SMTP(server, port)
if isTls:
smtp.starttls()
smtp.sendmail(send_from, send_to, msg.as_string())
smtp.quit()

134
wo/core/services.py Normal file
View File

@@ -0,0 +1,134 @@
"""WordOps Service Manager"""
import os
import sys
import subprocess
from subprocess import Popen
from wo.core.logging import Log
import pystache
class WOService():
"""Intialization for service"""
def ___init__():
pass
def start_service(self, service_name):
"""
start service
Similar to `service xyz start`
"""
try:
if service_name in ['nginx', 'php5-fpm']:
service_cmd = ('{0} -t && service {0} start'
.format(service_name))
else:
service_cmd = ('service {0} start'.format(service_name))
Log.info(self, "Start : {0:10}" .format(service_name), end='')
retcode = subprocess.getstatusoutput(service_cmd)
if retcode[0] == 0:
Log.info(self, "[" + Log.ENDC + "OK" + Log.OKBLUE + "]")
return True
else:
Log.debug(self, "{0}".format(retcode[1]))
Log.info(self, "[" + Log.FAIL + "Failed" + Log.OKBLUE+"]")
return False
except OSError as e:
Log.debug(self, "{0}".format(e))
Log.error(self, "\nFailed to start service {0}"
.format(service_name))
def stop_service(self, service_name):
"""
Stop service
Similar to `service xyz stop`
"""
try:
Log.info(self, "Stop : {0:10}" .format(service_name), end='')
retcode = subprocess.getstatusoutput('service {0} stop'
.format(service_name))
if retcode[0] == 0:
Log.info(self, "[" + Log.ENDC + "OK" + Log.OKBLUE + "]")
return True
else:
Log.debug(self, "{0}".format(retcode[1]))
Log.info(self, "[" + Log.FAIL + "Failed" + Log.OKBLUE+"]")
return False
except OSError as e:
Log.debug(self, "{0}".format(e))
Log.error(self, "\nFailed to stop service : {0}"
.format(service_name))
def restart_service(self, service_name):
"""
Restart service
Similar to `service xyz restart`
"""
try:
if service_name in ['nginx', 'php5-fpm']:
service_cmd = ('{0} -t && service {0} restart'
.format(service_name))
else:
service_cmd = ('service {0} restart'.format(service_name))
Log.info(self, "Restart : {0:10}".format(service_name), end='')
retcode = subprocess.getstatusoutput(service_cmd)
if retcode[0] == 0:
Log.info(self, "[" + Log.ENDC + "OK" + Log.OKBLUE + "]")
return True
else:
Log.debug(self, "{0}".format(retcode[1]))
Log.info(self, "[" + Log.FAIL + "Failed" + Log.OKBLUE+"]")
return False
except OSError as e:
Log.debug(self, "{0} {1}".format(e.errno, e.strerror))
Log.error(self, "\nFailed to restart service : {0}"
.format(service_name))
def reload_service(self, service_name):
"""
Stop service
Similar to `service xyz stop`
"""
try:
if service_name in ['nginx', 'php5-fpm']:
service_cmd = ('{0} -t && service {0} reload'
.format(service_name))
else:
service_cmd = ('service {0} reload'.format(service_name))
Log.info(self, "Reload : {0:10}".format(service_name), end='')
retcode = subprocess.getstatusoutput(service_cmd)
if retcode[0] == 0:
Log.info(self, "[" + Log.ENDC + "OK" + Log.OKBLUE + "]")
return True
else:
Log.debug(self, "{0}".format(retcode[1]))
Log.info(self, "[" + Log.FAIL + "Failed" + Log.OKBLUE+"]")
return False
except OSError as e:
Log.debug(self, "{0}".format(e))
Log.error(self, "\nFailed to reload service {0}"
.format(service_name))
def get_service_status(self, service_name):
try:
is_exist = subprocess.getstatusoutput('which {0}'
.format(service_name))
if is_exist[0] == 0 or service_name in ['php7.0-fpm', 'php5.6-fpm']:
retcode = subprocess.getstatusoutput('service {0} status'
.format(service_name))
if retcode[0] == 0:
return True
else:
Log.debug(self, "{0}".format(retcode[1]))
return False
else:
return False
except OSError as e:
Log.debug(self, "{0}{1}".format(e.errno, e.strerror))
Log.error(self, "Unable to get services status of {0}"
.format(service_name))
return False

83
wo/core/shellexec.py Normal file
View File

@@ -0,0 +1,83 @@
"""WordOps Shell Functions"""
from wo.core.logging import Log
import os
import sys
import subprocess
class CommandExecutionError(Exception):
"""custom Exception for command execution"""
pass
class WOShellExec():
"""Method to run shell commands"""
def __init__():
pass
def cmd_exec(self, command, errormsg='', log=True):
"""Run shell command from Python"""
try:
log and Log.debug(self, "Running command: {0}".format(command))
with subprocess.Popen([command], stdout=subprocess.PIPE,
stderr=subprocess.PIPE, shell=True) as proc:
(cmd_stdout_bytes, cmd_stderr_bytes) = proc.communicate()
(cmd_stdout, cmd_stderr) = (cmd_stdout_bytes.decode('utf-8',
"replace"),
cmd_stderr_bytes.decode('utf-8',
"replace"))
if proc.returncode == 0:
Log.debug(self, "Command Output: {0}, \nCommand Error: {1}"
.format(cmd_stdout, cmd_stderr))
return True
else:
Log.debug(self, "Command Output: {0}, \nCommand Error: {1}"
.format(cmd_stdout, cmd_stderr))
return False
except OSError as e:
Log.debug(self, str(e))
raise CommandExecutionError
except Exception as e:
Log.debug(self, str(e))
raise CommandExecutionError
def invoke_editor(self, filepath, errormsg=''):
"""
Open files using sensible editor
"""
try:
subprocess.call(['sensible-editor', filepath])
except OSError as e:
Log.debug(self, "{0}{1}".format(e.errno, e.strerror))
raise CommandExecutionError
def cmd_exec_stdout(self, command, errormsg='', log=True):
"""Run shell command from Python"""
try:
log and Log.debug(self, "Running command: {0}".format(command))
with subprocess.Popen([command], stdout=subprocess.PIPE,
stderr=subprocess.PIPE, shell=True) as proc:
(cmd_stdout_bytes, cmd_stderr_bytes) = proc.communicate()
(cmd_stdout, cmd_stderr) = (cmd_stdout_bytes.decode('utf-8',
"replace"),
cmd_stderr_bytes.decode('utf-8',
"replace"))
if proc.returncode == 0:
Log.debug(self, "Command Output: {0}, \nCommand Error: {1}"
.format(cmd_stdout, cmd_stderr))
return cmd_stdout
else:
Log.debug(self, "Command Output: {0}, \nCommand Error: {1}"
.format(cmd_stdout, cmd_stderr))
return cmd_stdout
except OSError as e:
Log.debug(self, str(e))
raise CommandExecutionError
except Exception as e:
Log.debug(self, str(e))
raise CommandExecutionError

43
wo/core/sslutils.py Normal file
View File

@@ -0,0 +1,43 @@
import os
from wo.core.shellexec import WOShellExec
from wo.core.logging import Log
class SSL:
def getExpirationDays(self,domain,returnonerror=False):
# check if exist
if not os.path.isfile('/etc/letsencrypt/live/{0}/cert.pem'
.format(domain)):
Log.error(self,'File Not Found : /etc/letsencrypt/live/{0}/cert.pem'
.format(domain),False)
if returnonerror:
return -1
Log.error(self, "Check the WordOps log for more details "
"`tail /var/log/wo/wordops.log` and please try again...")
current_date = WOShellExec.cmd_exec_stdout(self, "date -d \"now\" +%s")
expiration_date = WOShellExec.cmd_exec_stdout(self, "date -d \"`openssl x509 -in /etc/letsencrypt/live/{0}/cert.pem"
" -text -noout|grep \"Not After\"|cut -c 25-`\" +%s".format(domain))
days_left = int((int(expiration_date) - int(current_date))/ 86400)
if (days_left > 0):
return days_left
else:
# return "Certificate Already Expired ! Please Renew soon."
return -1
def getExpirationDate(self,domain):
# check if exist
if not os.path.isfile('/etc/letsencrypt/live/{0}/cert.pem'
.format(domain)):
Log.error(self,'File Not Found : /etc/letsencrypt/live/{0}/cert.pem'
.format(domain),False)
Log.error(self, "Check the WordOps log for more details "
"`tail /var/log/wo/wordops.log` and please try again...")
expiration_date = WOShellExec.cmd_exec_stdout(self, "date -d \"`openssl x509 -in /etc/letsencrypt/live/{0}/cert.pem"
" -text -noout|grep \"Not After\"|cut -c 25-`\" ".format(domain))
return expiration_date

209
wo/core/variables.py Normal file
View File

@@ -0,0 +1,209 @@
"""WordOps core variable module"""
import platform
import socket
import configparser
import os
import sys
import psutil
import datetime
class WOVariables():
"""Intialization of core variables"""
# WordOps version
wo_version = "3.9.0"
# WordOps packages versions
wo_wp_cli = "2.0.1"
wo_adminer = "4.6.3"
# Get WPCLI path
wo_wpcli_path = os.popen('which wp | tr "\n" " "').read()
if wo_wpcli_path == '':
wo_wpcli_path = '/usr/bin/wp '
# Current date and time of System
wo_date = datetime.datetime.now().strftime('%d%b%Y%H%M%S')
# WordOps core variables
wo_platform_distro = platform.linux_distribution()[0].lower()
wo_platform_version = platform.linux_distribution()[1]
wo_platform_codename = os.popen("lsb_release -sc | tr -d \'\\n\'").read()
# Get timezone of system
if os.path.isfile('/etc/timezone'):
with open("/etc/timezone", "r") as tzfile:
wo_timezone = tzfile.read().replace('\n', '')
if wo_timezone == "Etc/UTC":
wo_timezone = "UTC"
else:
wo_timezone = "Europe/Amsterdam"
# Get FQDN of system
wo_fqdn = socket.getfqdn()
# WordOps default webroot path
wo_webroot = '/var/www/'
# PHP5 user
wo_php_user = 'www-data'
# Get git user name and EMail
config = configparser.ConfigParser()
config.read(os.path.expanduser("~")+'/.gitconfig')
try:
wo_user = config['user']['name']
wo_email = config['user']['email']
except Exception as e:
wo_user = input("Enter your name: ")
wo_email = input("Enter your email: ")
os.system("git config --global user.name {0}".format(wo_user))
os.system("git config --global user.email {0}".format(wo_email))
# Get System RAM and SWAP details
wo_ram = psutil.virtual_memory().total / (1024 * 1024)
wo_swap = psutil.swap_memory().total / (1024 * 1024)
# MySQL hostname
wo_mysql_host = ""
config = configparser.RawConfigParser()
if os.path.exists('/etc/mysql/conf.d/my.cnf'):
cnfpath = "/etc/mysql/conf.d/my.cnf"
else:
cnfpath = os.path.expanduser("~")+"/.my.cnf"
if [cnfpath] == config.read(cnfpath):
try:
wo_mysql_host = config.get('client', 'host')
except configparser.NoOptionError as e:
wo_mysql_host = "localhost"
else:
wo_mysql_host = "localhost"
# WordOps stack installation variables
# Nginx repo and packages
if wo_platform_codename == 'precise':
wo_nginx_repo = ("deb http://download.opensuse.org/repositories/home:"
"/rtCamp:/EasyEngine/xUbuntu_12.04/ /")
elif wo_platform_codename == 'trusty':
wo_nginx_repo = ("deb http://download.opensuse.org/repositories/home:"
"/rtCamp:/EasyEngine/xUbuntu_14.04/ /")
elif wo_platform_codename == 'xenial':
wo_nginx_repo = ("deb http://download.opensuse.org/repositories/home:"
"/rtCamp:/EasyEngine/xUbuntu_16.04/ /")
elif wo_platform_codename == 'bionic':
wo_nginx_repo = ("deb http://download.opensuse.org/repositories/home:"
"/rtCamp:/EasyEngine/xUbuntu_18.04/ /")
elif wo_platform_codename == 'whwozy':
wo_nginx_repo = ("deb http://download.opensuse.org/repositories/home:"
"/rtCamp:/EasyEngine/Debian_7.0/ /")
elif wo_platform_codename == 'jessie':
wo_nginx_repo = ("deb http://download.opensuse.org/repositories/home:"
"/rtCamp:/EasyEngine/Debian_8.0/ /")
wo_nginx = ["nginx-custom", "nginx-ee"]
wo_nginx_key = '3050AC3CD2AE6F03'
# PHP repo and packages
if wo_platform_distro == 'ubuntu':
if wo_platform_codename == 'precise':
wo_php_repo = "ppa:ondrej/php5-5.6"
wo_php = ["php5-fpm", "php5-curl", "php5-gd", "php5-imap",
"php5-mcrypt", "php5-common", "php5-readline",
"php5-mysql", "php5-cli", "php5-memcache", "php5-imagick",
"memcached", "graphviz", "php-pear"]
elif (wo_platform_codename == 'trusty' or wo_platform_codename == 'xenial' or wo_platform_codename == 'bionic'):
wo_php_repo = "ppa:ondrej/php"
# PHP5.6 for those who like to live on the dangerous side
wo_php5_6 = ["php5.6-fpm", "php5.6-curl", "php5.6-gd", "php5.6-imap",
"php5.6-mcrypt", "php5.6-readline", "php5.6-common", "php5.6-recode",
"php5.6-mysql", "php5.6-cli", "php5.6-curl", "php5.6-mbstring",
"php5.6-bcmath", "php5.6-mysql", "php5.6-opcache", "php5.6-zip", "php5.6-xml", "php5.6-soap"]
# PHP7.0 going EOL soon
wo_php7_0 = ["php7.0-fpm", "php7.0-curl", "php7.0-gd", "php7.0-imap",
"php7.0-mcrypt", "php7.0-readline", "php7.0-common", "php7.0-recode",
"php7.0-cli", "php7.0-mbstring",
"php7.0-bcmath", "php7.0-mysql", "php7.0-opcache", "php7.0-zip", "php7.0-xml", "php7.0-soap"]
wo_php7_2 = ["php7.2-fpm", "php7.2-curl", "php7.2-gd", "php7.2-imap",
"php7.2-readline", "php7.2-common", "php7.2-recode",
"php7.2-cli", "php7.2-mbstring",
"php7.2-bcmath", "php7.2-mysql", "php7.2-opcache", "php7.2-zip", "php7.2-xml", "php7.2-soap"]
wo_php_extra = ["php-memcached", "php-imagick", "php-memcache", "memcached",
"graphviz", "php-pear", "php-xdebug", "php-msgpack", "php-redis"]
elif wo_platform_distro == 'debian':
if wo_platform_codename == 'wheezy':
wo_php_repo = ("deb http://packages.dotdeb.org {codename}-php56 all"
.format(codename=wo_platform_codename))
else :
wo_php_repo = ("deb http://packages.dotdeb.org {codename} all".format(codename=wo_platform_codename))
wo_php = ["php5-fpm", "php5-curl", "php5-gd", "php5-imap",
"php5-mcrypt", "php5-common", "php5-readline",
"php5-mysqlnd", "php5-cli", "php5-memcache", "php5-imagick",
"memcached", "graphviz", "php-pear"]
wo_php7_0 = ["php7.0-fpm", "php7.0-curl", "php7.0-gd", "php7.0-imap",
"php7.0-mcrypt", "php7.0-common", "php7.0-readline", "php7.0-redis",
"php7.0-mysql", "php7.0-cli", "php7.0-memcache", "php7.0-imagick",
"php7.0-mbstring", "php7.0-recode", "php7.0-bcmath", "php7.0-opcache", "php7.0-zip", "php7.0-xml",
"php7.0-soap", "php7.0-msgpack",
"memcached", "graphviz", "php-pear", "php7.0-xdebug"]
wo_php_extra = []
if wo_platform_codename == 'wheezy':
wo_php = wo_php + ["php5-dev"]
if wo_platform_codename == 'precise' or wo_platform_codename == 'jessie':
wo_php = wo_php + ["php5-xdebug"]
# MySQL repo and packages
if wo_platform_distro == 'ubuntu':
wo_mysql_repo = ("deb [arch=amd64,i386,ppc64el] http://sfo1.mirrors.digitalocean.com/mariadb/repo/"
"10.1/ubuntu {codename} main"
.format(codename=wo_platform_codename))
elif wo_platform_distro == 'debian':
wo_mysql_repo = ("deb [arch=amd64,i386,ppc64el] http://sfo1.mirrors.digitalocean.com/mariadb/repo/"
"10.1/debian {codename} main"
.format(codename=wo_platform_codename))
wo_mysql = ["mariadb-server", "percona-toolkit"]
# HHVM repo details
# 12.04 requires boot repository
if wo_platform_distro == 'ubuntu':
if wo_platform_codename == "precise":
wo_boost_repo = ("ppa:mapnik/boost")
wo_hhvm_repo = ("deb http://dl.hhvm.com/ubuntu {codename} main"
.format(codename=wo_platform_codename))
elif wo_platform_codename == "trusty":
wo_hhvm_repo = ("deb http://dl.hhvm.com/ubuntu {codename} main"
.format(codename=wo_platform_codename))
else:
wo_hhvm_repo = ("deb http://dl.hhvm.com/debian {codename} main"
.format(codename=wo_platform_codename))
wo_hhvm = ["hhvm"]
# Redis repo details
if wo_platform_distro == 'ubuntu':
wo_redis_repo = ("ppa:chris-lea/redis-server")
else:
wo_redis_repo = ("deb http://packages.dotdeb.org {codename} all"
.format(codename=wo_platform_codename))
if (wo_platform_codename == 'trusty' or wo_platform_codename == 'xenial' or wo_platform_codename == 'bionic'):
wo_redis = ['redis-server', 'php-redis']
else:
wo_redis = ['redis-server', 'php5-redis']
# Repo path
wo_repo_file = "wo-repo.list"
wo_repo_file_path = ("/etc/apt/sources.list.d/" + wo_repo_file)
# Application dabase file path
basedir = os.path.abspath(os.path.dirname('/var/lib/wo/'))
wo_db_uri = 'sqlite:///' + os.path.join(basedir, 'dbase.db')
def __init__(self):
pass