mirror of
https://github.com/nextcloud/nextcloudpi.git
synced 2025-08-20 14:31:23 +00:00
305 lines
8.6 KiB
Python
Executable File
305 lines
8.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
"""
|
|
Automatic system testing for NextCloudPi
|
|
|
|
Copyleft 2018 by Ignacio Nunez Hernanz <nacho _a_t_ ownyourbits _d_o_t_ com>
|
|
GPL licensed (see LICENSE file in repository root).
|
|
Use at your own risk!
|
|
|
|
./system_tests.py [user@ip]
|
|
|
|
More at https://ownyourbits.com
|
|
"""
|
|
|
|
pre_cmd = []
|
|
|
|
import sys
|
|
import getopt
|
|
import os
|
|
import signal
|
|
from subprocess import run, getstatusoutput, PIPE
|
|
|
|
processes_must_be_running = [
|
|
'apache2',
|
|
'cron',
|
|
'mariadb',
|
|
'php-fpm',
|
|
'postfix',
|
|
'redis-server',
|
|
]
|
|
|
|
binaries_must_be_installed = [
|
|
'jq',
|
|
'dialog',
|
|
'dnsmasq',
|
|
'git',
|
|
'letsencrypt',
|
|
'noip2',
|
|
'rsync',
|
|
'ssh',
|
|
]
|
|
|
|
binaries_no_docker = [
|
|
'btrfs',
|
|
'fail2ban-server',
|
|
'udiskie',
|
|
'ufw',
|
|
'samba',
|
|
]
|
|
|
|
files_must_exist = [
|
|
'/usr/local/etc/ncp-version',
|
|
]
|
|
|
|
files_must_not_exist = [
|
|
'/.ncp-image',
|
|
]
|
|
|
|
|
|
class tc:
|
|
"terminal colors"
|
|
brown='\033[33m'
|
|
yellow='\033[33;1m'
|
|
green='\033[32m'
|
|
red='\033[31m'
|
|
normal='\033[0m'
|
|
|
|
|
|
def usage():
|
|
"Print usage"
|
|
print("usage: system_tests.py [user@ip]")
|
|
|
|
|
|
def is_running(process):
|
|
"check that a process is running"
|
|
print("[running] " + tc.brown + "{:16}".format(process) + tc.normal, end=' ')
|
|
result = run(pre_cmd + ['pgrep', '-cf', process], stdout=PIPE, stderr=PIPE)
|
|
if result.returncode == 0:
|
|
print(tc.green + "ok" + tc.normal)
|
|
else:
|
|
print(tc.red + "error" + tc.normal)
|
|
return result.returncode == 0
|
|
|
|
|
|
def file_exists(file):
|
|
"check that a file exists"
|
|
print("[exists ] " + tc.brown + "{:16}".format(file) + tc.normal, end=' ')
|
|
result = run(pre_cmd + ['test', '-f', file], stdout=PIPE, stderr=PIPE)
|
|
if result.returncode == 0:
|
|
print(tc.green + "ok" + tc.normal)
|
|
else:
|
|
print(tc.red + "error" + tc.normal)
|
|
return result.returncode == 0
|
|
|
|
|
|
def file_not_exists(file):
|
|
"check that a file doesn't exist"
|
|
print("[nexists] " + tc.brown + "{:16}".format(file) + tc.normal, end=' ')
|
|
result = run(pre_cmd + ['test', '-f', file], stdout=PIPE, stderr=PIPE)
|
|
if result.returncode != 0:
|
|
print(tc.green + "ok" + tc.normal)
|
|
else:
|
|
print(tc.red + "error" + tc.normal)
|
|
return result.returncode == 0
|
|
|
|
|
|
def check_processes_running(processes):
|
|
"check that all processes are running"
|
|
ret = True
|
|
for process in processes:
|
|
if not is_running(process):
|
|
ret = False
|
|
return ret
|
|
|
|
|
|
def is_installed(binary):
|
|
"check that a binary is installed"
|
|
print("[install] " + tc.brown + "{:16}".format(binary) + tc.normal, end=' ')
|
|
result = run(pre_cmd + ['sudo', 'which', binary], stdout=PIPE, stderr=PIPE)
|
|
if result.returncode == 0:
|
|
print(tc.green + "ok" + tc.normal)
|
|
else:
|
|
print(tc.red + "error" + tc.normal)
|
|
return result.returncode == 0
|
|
|
|
|
|
def check_binaries_installed(binaries):
|
|
"check that all the binaries are installed"
|
|
ret = True
|
|
for binary in binaries:
|
|
if not is_installed(binary):
|
|
ret = False
|
|
return ret
|
|
|
|
|
|
def check_files_exist(files):
|
|
"check that all the files exist"
|
|
ret = True
|
|
for file in files:
|
|
if not file_exists(file):
|
|
ret = False
|
|
return ret
|
|
|
|
|
|
def check_files_dont_exist(files):
|
|
"check that all the files don't exist"
|
|
ret = True
|
|
for file in files:
|
|
if file_not_exists(file):
|
|
ret = False
|
|
return ret
|
|
|
|
|
|
def check_notify_push():
|
|
"check that notify_push is installed and set up"
|
|
result = run(pre_cmd + ['ncc', 'notify_push:self-test'], stdout=PIPE, stderr=PIPE)
|
|
|
|
print("[push ] " + tc.brown + "notify_push self-test" + tc.normal, end=' ')
|
|
if result.returncode == 0:
|
|
print(tc.green + "ok" + tc.normal)
|
|
return True
|
|
else:
|
|
print(tc.red + "error" + tc.normal)
|
|
print(result.stderr)
|
|
print(result.stdout)
|
|
return False
|
|
|
|
def is_lxc():
|
|
"check that we are running inside a LXC container"
|
|
(exitcode, output) = getstatusoutput('grep -q container=lxc /proc/1/environ')
|
|
return exitcode == 0
|
|
|
|
def signal_handler(sig, frame):
|
|
sys.exit(0)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
signal.signal(signal.SIGINT, signal_handler)
|
|
|
|
# parse options
|
|
try:
|
|
opts, args = getopt.getopt(sys.argv[1:], 'h', ['help', 'no-ping', 'non-interactive'])
|
|
except getopt.GetoptError:
|
|
usage()
|
|
sys.exit(2)
|
|
|
|
skip_ping = False
|
|
interactive = True
|
|
for opt, arg in opts:
|
|
if opt in ('-h', '--help'):
|
|
usage()
|
|
sys.exit(2)
|
|
elif opt == '--no-ping':
|
|
skip_ping = True
|
|
elif opt == '--non-interactive':
|
|
interactive = False
|
|
else:
|
|
usage()
|
|
sys.exit(2)
|
|
|
|
# parse arguments
|
|
ssh_cmd = "ssh root@nextcloudpi.local"
|
|
if len(args) > 0:
|
|
if '@' in args[0]:
|
|
ssh_cmd = "ssh " + args[0]
|
|
else:
|
|
print(tc.brown + "* Ignoring invalid SSH argument " + tc.yellow + args[0] + tc.normal)
|
|
args = []
|
|
|
|
# detect if we are running this in a NCP instance
|
|
try:
|
|
dockers_running = run(['docker', 'ps', '--format', '{{.Names}}'], stdout=PIPE).stdout.decode('utf-8')
|
|
except:
|
|
dockers_running = ''
|
|
|
|
# detect if we are running this in a LXC instance
|
|
try:
|
|
lxc_running = run(['lxc', 'info', 'ncp'], stdout=PIPE, check = True)
|
|
except:
|
|
lxc_running = False
|
|
|
|
try:
|
|
systemd_container_running = run(['machinectl', 'show', 'ncp'], stdout=PIPE, check = True)
|
|
except:
|
|
systemd_container_running = False
|
|
|
|
|
|
# local method
|
|
if os.path.exists('/usr/local/etc/ncp-baseimage'):
|
|
print(tc.brown + "* local NCP instance detected" + tc.normal)
|
|
if not is_lxc():
|
|
binaries_must_be_installed = binaries_must_be_installed + binaries_no_docker
|
|
pre_cmd = []
|
|
|
|
# docker method
|
|
elif 'nextcloudpi' in dockers_running:
|
|
print( tc.brown + "* local NCP docker instance detected" + tc.normal)
|
|
pre_cmd = ['docker', 'exec']
|
|
if interactive:
|
|
pre_cmd.append('-ti')
|
|
pre_cmd.append('nextcloudpi')
|
|
|
|
# LXC method
|
|
elif lxc_running:
|
|
print( tc.brown + "* local LXC instance detected" + tc.normal)
|
|
pre_cmd = ['lxc', 'exec', 'ncp', '--']
|
|
|
|
elif systemd_container_running:
|
|
pre_cmd = ['systemd-run', '--wait', '-P', '--machine=ncp']
|
|
|
|
# SSH method
|
|
else:
|
|
if len(args) == 0:
|
|
print( tc.brown + "* No local NCP instance detected, trying SSH with " +
|
|
tc.yellow + ssh_cmd + tc.normal + "...")
|
|
binaries_must_be_installed = binaries_must_be_installed + binaries_no_docker
|
|
pre_cmd = ['ssh', '-o UserKnownHostsFile=/dev/null' , '-o PasswordAuthentication=no',
|
|
'-o StrictHostKeyChecking=no', '-o ConnectTimeout=1', ssh_cmd[4:]]
|
|
|
|
if not skip_ping:
|
|
at_char = ssh_cmd.index('@')
|
|
ip = ssh_cmd[at_char+1:]
|
|
ping_cmd = run(['ping', '-c1', '-w10', ip], stdout=PIPE, stderr=PIPE)
|
|
if ping_cmd.returncode != 0:
|
|
print(tc.red + "No connectivity to " + tc.yellow + ip + tc.normal)
|
|
#sys.exit(1)
|
|
|
|
ssh_test = run(pre_cmd + [':'], stdout=PIPE, stderr=PIPE)
|
|
if ssh_test.returncode != 0:
|
|
ssh_copy = run(['ssh-copy-id', ssh_cmd[4:]], stderr=PIPE)
|
|
if ssh_copy.returncode != 0:
|
|
print(tc.red + "SSH connection failed" + tc.normal)
|
|
sys.exit(1)
|
|
|
|
print(pre_cmd)
|
|
# checks
|
|
print("\nNextCloudPi system checks")
|
|
print("-------------------------")
|
|
running_result = check_processes_running(processes_must_be_running)
|
|
install_result = check_binaries_installed(binaries_must_be_installed)
|
|
files1_result = check_files_exist(files_must_exist)
|
|
files2_result = check_files_dont_exist(files_must_not_exist)
|
|
notify_push_result = check_notify_push()
|
|
|
|
if running_result and install_result and files1_result and files2_result and notify_push_result:
|
|
sys.exit(0)
|
|
else:
|
|
sys.exit(1)
|
|
|
|
# License
|
|
#
|
|
# This script is free software; you can redistribute it and/or modify it
|
|
# under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This script is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this script; if not, write to the
|