In commit ec66dd6562, in non-interactive
runs of scylla_setup all options were unintentionally set to "false",
regardless of the options passed on the scylla_setup command line. This
can lead to all sorts of wrong behaviors, and in particular one test
setup assumed it was enabling the Scylla service (which was previously
the default) but after this commit, it no longer did.
This patch restores the previous behavior: Non-interactive invocations
of scylla_setup adhere to the defaults and the command-line options,
rather than blindly choosing "false".
Signed-off-by: Nadav Har'El <nyh@scylladb.com>
Message-Id: <20190211214105.32613-1-nyh@scylladb.com>
409 lines
20 KiB
Python
Executable File
409 lines
20 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
#
|
|
# Copyright 2018 ScyllaDB
|
|
#
|
|
|
|
#
|
|
# This file is part of Scylla.
|
|
#
|
|
# Scylla is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Affero General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# Scylla 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 Scylla. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import os
|
|
import sys
|
|
import argparse
|
|
import glob
|
|
import shutil
|
|
import io
|
|
import stat
|
|
from scylla_util import *
|
|
|
|
interactive = False
|
|
|
|
def when_interactive_ask_service(interactive, msg1, msg2, default = None):
|
|
if not interactive:
|
|
return default
|
|
|
|
print(msg1)
|
|
print(msg2)
|
|
while True:
|
|
if default == True:
|
|
prompt = '[YES/no]'
|
|
elif default == False:
|
|
prompt = '[yes/NO]'
|
|
else:
|
|
prompt = '[yes/no]'
|
|
ans = input(prompt).lower()
|
|
if len(ans) == 0:
|
|
return default
|
|
if ans == 'yes' or ans == 'y':
|
|
return True
|
|
elif ans == 'no' or ans =='n':
|
|
return False
|
|
|
|
def print_non_interactive_suggestion_message(args):
|
|
colorprint('{green}scylla_setup accepts command line arguments as well!{nocolor} For easily provisioning in a similar environment than this, type:\n')
|
|
|
|
base_string = " scylla_setup"
|
|
outlines = [ base_string ]
|
|
for key,value in vars(args).items():
|
|
if not value:
|
|
continue
|
|
x = ' --{}'.format(key.replace('_','-'))
|
|
if type(value) != bool:
|
|
x += ' {}'.format(str(value))
|
|
|
|
if len(outlines[-1]) + len(x) > 72:
|
|
outlines.append(len(base_string) * ' ')
|
|
outlines[-1] += x
|
|
|
|
print(' \\\n'.join(outlines))
|
|
if args.no_io_setup == False:
|
|
print('\nAlso, to avoid the time-consuming I/O tuning you can add --no-io-setup and copy the contents of /etc/scylla.d/io*')
|
|
print('Only do that if you are moving the files into machines with the exact same hardware')
|
|
|
|
def interactive_choose_nic():
|
|
nics = [os.path.basename(n) for n in glob.glob('/sys/class/net/*') if n != '/sys/class/net/lo']
|
|
if len(nics) == 0:
|
|
print('A NIC was not found.')
|
|
sys.exit(1)
|
|
elif len(nics) == 1:
|
|
return nics[0]
|
|
else:
|
|
print('Please select a NIC from the following list:')
|
|
while True:
|
|
print(nics)
|
|
n = input('> ')
|
|
if is_valid_nic(n):
|
|
return n
|
|
|
|
def do_verify_package(pkg):
|
|
if is_debian_variant():
|
|
res = run('dpkg -s {}'.format(pkg), silent=True, exception=False)
|
|
elif is_redhat_variant():
|
|
res = run('rpm -q {}'.format(pkg), silent=True, exception=False)
|
|
elif is_gentoo_variant():
|
|
res = 0 if len(glob.glob('/var/db/pkg/*/{}-*'.format(pkg))) else 1
|
|
if res != 0:
|
|
print('{} package is not installed.'.format(pkg))
|
|
sys.exit(1)
|
|
|
|
def list_block_devices():
|
|
s = out('lsblk --help')
|
|
if re.match(r'\s*-p', s, flags=re.MULTILINE):
|
|
s = out('lsblk -pnr -e 7,252')
|
|
devices = re.findall(r'^(\S+) ', s, flags=re.MULTILINE)
|
|
else:
|
|
devices = []
|
|
for p in ['/dev/sd*', '/dev/hd*', '/dev/xvd*', '/dev/nvme*', '/dev/mapper/*']:
|
|
devices.extend([d for d in glob.glob(p) if d != '/dev/mapper/control'])
|
|
return devices
|
|
|
|
def get_unused_disks():
|
|
unused = []
|
|
for dev in list_block_devices():
|
|
# dev contains partitions
|
|
if len(glob.glob('/sys/class/block/{dev}/{dev}*'.format(dev=dev.replace('/dev/','')))) > 0:
|
|
continue
|
|
# dev is used
|
|
if not is_unused_disk(dev):
|
|
continue
|
|
unused.append(dev)
|
|
return unused
|
|
|
|
def run_setup_script(name, script):
|
|
global interactive
|
|
res = run(script, exception=False)
|
|
if res != 0:
|
|
if interactive:
|
|
colorprint('{red}{name} setup failed. Press any key to continue...{nocolor}', name=name)
|
|
input()
|
|
else:
|
|
print('{} setup failed.'.format(name))
|
|
sys.exit(1)
|
|
return res
|
|
|
|
if __name__ == '__main__':
|
|
if os.getuid() > 0:
|
|
print('Requires root permission.')
|
|
sys.exit(1)
|
|
parser = argparse.ArgumentParser(description='Configure environment for Scylla.')
|
|
group = parser.add_mutually_exclusive_group(required=True)
|
|
group.add_argument('--disks',
|
|
help='specify disks for RAID')
|
|
group.add_argument('--no-raid-setup', action='store_true', default=False,
|
|
help='skip raid setup')
|
|
parser.add_argument('--nic', default='eth0',
|
|
help='specify NIC')
|
|
parser.add_argument('--ntp-domain',
|
|
help='specify NTP domain')
|
|
parser.add_argument('--ami', action='store_true', default=False,
|
|
help='setup AMI instance')
|
|
parser.add_argument('--setup-nic-and-disks', action='store_true', default=False,
|
|
help='optimize NIC and disks')
|
|
parser.add_argument('--developer-mode', action='store_true', default=False,
|
|
help='enable developer mode')
|
|
parser.add_argument('--no-ec2-check', action='store_true', default=False,
|
|
help='skip EC2 configuration check')
|
|
parser.add_argument('--no-kernel-check', action='store_true', default=False,
|
|
help='skip kernel version check')
|
|
parser.add_argument('--no-verify-package', action='store_true', default=False,
|
|
help='skip verifying packages')
|
|
parser.add_argument('--no-enable-service', action='store_true', default=False,
|
|
help='skip enabling service')
|
|
if is_redhat_variant():
|
|
parser.add_argument('--no-selinux-setup', action='store_true', default=False,
|
|
help='skip selinux setup')
|
|
parser.add_argument('--no-bootparam-setup', action='store_true', default=False,
|
|
help='skip bootparam setup')
|
|
parser.add_argument('--no-ntp-setup', action='store_true', default=False,
|
|
help='skip ntp setup')
|
|
parser.add_argument('--no-coredump-setup', action='store_true', default=False,
|
|
help='skip coredump setup')
|
|
parser.add_argument('--no-sysconfig-setup', action='store_true', default=False,
|
|
help='skip sysconfig setup')
|
|
parser.add_argument('--no-io-setup', action='store_true', default=False,
|
|
help='skip IO configuration setup')
|
|
parser.add_argument('--no-version-check', action='store_true', default=False,
|
|
help='skip daily version check')
|
|
parser.add_argument('--no-node-exporter', action='store_true', default=False,
|
|
help='do not install the node exporter')
|
|
parser.add_argument('--no-cpuscaling-setup', action='store_true', default=False,
|
|
help='skip cpu scaling setup')
|
|
parser.add_argument('--no-fstrim-setup', action='store_true', default=False,
|
|
help='skip fstrim setup')
|
|
if len(sys.argv) == 1:
|
|
interactive = True
|
|
group.required = False # interactive mode: no option is required.
|
|
|
|
args = parser.parse_args()
|
|
|
|
if not interactive:
|
|
if not args.no_sysconfig_setup or (is_ec2() and not args.no_ec2_check):
|
|
if not is_valid_nic(args.nic):
|
|
print('NIC {} doesn\'t exist.'.format(args.nic))
|
|
sys.exit(1)
|
|
|
|
disks = args.disks
|
|
nic = args.nic
|
|
set_nic_and_disks = args.setup_nic_and_disks
|
|
ec2_check = not args.no_ec2_check
|
|
kernel_check = not args.no_kernel_check
|
|
verify_package = not args.no_verify_package
|
|
enable_service = not args.no_enable_service
|
|
if is_redhat_variant():
|
|
selinux_setup = not args.no_selinux_setup
|
|
bootparam_setup = not args.no_bootparam_setup
|
|
ntp_setup = not args.no_ntp_setup
|
|
raid_setup = not args.no_raid_setup
|
|
coredump_setup = not args.no_coredump_setup
|
|
sysconfig_setup = not args.no_sysconfig_setup
|
|
io_setup = not args.no_io_setup
|
|
version_check = not args.no_version_check
|
|
node_exporter = not args.no_node_exporter
|
|
cpuscaling_setup = not args.no_cpuscaling_setup
|
|
fstrim_setup = not args.no_fstrim_setup
|
|
selinux_reboot_required = False
|
|
|
|
if interactive:
|
|
colorprint('{green}Skip any of the following steps by answering \'no\'{nocolor}')
|
|
|
|
def interactive_ask_service(msg1, msg2, default = None):
|
|
return when_interactive_ask_service(interactive, msg1, msg2, default)
|
|
|
|
if is_ec2():
|
|
ec2_check = interactive_ask_service('Do you want to run Amazon EC2 configuration check?', 'Yes - runs a script to verify that this instance is optimized for running Scylla. No - skips the configuration check.', ec2_check)
|
|
args.no_ec2_check = not ec2_check
|
|
if ec2_check:
|
|
nic = interactive_choose_nic()
|
|
if ec2_check:
|
|
run('/usr/lib/scylla/scylla_ec2_check --nic {}'.format(nic))
|
|
|
|
kernel_check = interactive_ask_service('Do you want to run check your kernel version?', 'Yes - runs a script to verify that the kernel for this instance qualifies to run Scylla. No - skips the kernel check.', kernel_check)
|
|
args.no_kernel_check = not kernel_check
|
|
if kernel_check:
|
|
run('/usr/lib/scylla/scylla_kernel_check')
|
|
|
|
verify_package = interactive_ask_service('Do you want to verify the ScyllaDB packages are installed?', 'Yes - runs a script to confirm that ScyllaDB is installed. No - skips the installation check.', verify_package)
|
|
args.no_verify_package = not verify_package
|
|
if verify_package:
|
|
do_verify_package('scylla-jmx')
|
|
do_verify_package('scylla-tools')
|
|
|
|
enable_service = interactive_ask_service('Do you want the Scylla server service to automatically start when the Scylla node boots?', 'Yes - Scylla server service automatically starts on Scylla node boot. No - skips this step. Note you will have to start the Scylla Server service manually.', enable_service)
|
|
args.no_enable_service = not enable_service
|
|
if enable_service:
|
|
if is_systemd():
|
|
systemd_unit('scylla-server.service').enable()
|
|
elif is_gentoo_variant():
|
|
run('rc-update add scylla-server default')
|
|
|
|
if not os.path.exists('/etc/scylla.d/housekeeping.cfg'):
|
|
version_check = interactive_ask_service('Do you want to enable Scylla to check if there is a newer version of Scylla available?', 'Yes - start the Scylla-housekeeping service to check for a newer version. This check runs periodically. No - skips this step.', version_check)
|
|
args.no_version_check = not version_check
|
|
if version_check:
|
|
with open('/etc/scylla.d/housekeeping.cfg', 'w') as f:
|
|
f.write('[housekeeping]\ncheck-version: True\n')
|
|
if is_systemd():
|
|
systemd_unit('scylla-housekeeping-daily.timer').unmask()
|
|
systemd_unit('scylla-housekeeping-restart.timer').unmask()
|
|
else:
|
|
with open('/etc/scylla.d/housekeeping.cfg', 'w') as f:
|
|
f.write('[housekeeping]\ncheck-version: False\n')
|
|
if is_systemd():
|
|
hk_daily = systemd_unit('scylla-housekeeping-daily.timer')
|
|
hk_daily.mask()
|
|
hk_daily.stop()
|
|
hk_restart = systemd_unit('scylla-housekeeping-restart.timer')
|
|
hk_restart.mask()
|
|
hk_restart.stop()
|
|
|
|
cur_version=out('scylla --version', exception=False)
|
|
if len(cur_version) > 0:
|
|
if is_debian_variant():
|
|
new_version=out('sudo -u scylla /usr/lib/scylla/scylla-housekeeping --uuid-file /var/lib/scylla-housekeeping/housekeeping.uuid --repo-files \'/etc/apt/sources.list.d/scylla*.list\' version --version {} --mode i'.format(cur_version), shell=True, exception=False)
|
|
else:
|
|
new_version=out('sudo -u scylla /usr/lib/scylla/scylla-housekeeping --uuid-file /var/lib/scylla-housekeeping/housekeeping.uuid --repo-files \'/etc/yum.repos.d/scylla*.repo\' version --version {} --mode i'.format(cur_version), shell=True, exception=False)
|
|
if len(new_version) > 0:
|
|
print(new_version)
|
|
else:
|
|
if is_debian_variant():
|
|
new_version=out('sudo -u scylla /usr/lib/scylla/scylla-housekeeping --uuid-file /var/lib/scylla-housekeeping/housekeeping.uuid --repo-files \'/etc/apt/sources.list.d/scylla*.list\' version --version unknown --mode u', shell=True, exception=False)
|
|
else:
|
|
new_version=out('sudo -u scylla /usr/lib/scylla/scylla-housekeeping --uuid-file /var/lib/scylla-housekeeping/housekeeping.uuid --repo-files \'/etc/yum.repos.d/scylla*.repo\' version --version unknown --mode u', shell=True, exception=False)
|
|
print('A Scylla executable was not found, please check your installation {}'.format(new_version))
|
|
|
|
if is_redhat_variant():
|
|
selinux_setup = interactive_ask_service('Do you want to disable SELinux?', 'Yes - disables SELinux. Choosing Yes greatly improves performance. No - keeps SELinux activated.', selinux_setup)
|
|
args.no_selinux_setup = not selinux_setup
|
|
if selinux_setup:
|
|
res = run_setup_script('SELinux', '/usr/lib/scylla/scylla_selinux_setup')
|
|
if res != 0:
|
|
selinux_reboot_required=True
|
|
|
|
if args.ami:
|
|
bootparam_setup = interactive_ask_service('Do you want add hugepages capability to the bootloader options?', 'Yes - enable hugepages at boot time. Choosing yes greatly improves performance. No - skips this step.', bootparam_setup)
|
|
args.no_bootparam_setup = not bootparam_setup
|
|
if bootparam_setup:
|
|
run_setup_script('boot parameter', '/usr/lib/scylla/scylla_bootparam_setup --ami')
|
|
|
|
ntp_setup = interactive_ask_service('Do you want to setup Network Time Protocol(NTP) to auto-synchronize the current time on the node?', 'Yes - enables time-synchronization. This keeps the correct time on the node. No - skips this step.', ntp_setup)
|
|
args.no_ntp_setup = not ntp_setup
|
|
if ntp_setup:
|
|
if args.ntp_domain:
|
|
run_setup_script('NTP', '/usr/lib/scylla/scylla_ntp_setup --subdomain {}'.format(args.ntp_domain))
|
|
else:
|
|
run_setup_script('NTP', '/usr/lib/scylla/scylla_ntp_setup')
|
|
|
|
res = interactive_ask_service('Do you want to setup RAID0 and XFS?', 'It is recommended to use RAID0 and XFS for Scylla data. If you select yes, you will be prompted to choose the unmounted disks to use for Scylla data. Selected disks are formatted as part of the process.\nYes - choose a disk/disks to format and setup for RAID0 and XFS. No - skip this step.', raid_setup)
|
|
if res:
|
|
raid_setup = interactive_ask_service('Are you sure you want to setup RAID0 and XFS?', 'If you choose Yes, the selected drive will be reformated, erasing all existing data in the process.', raid_setup)
|
|
else:
|
|
raid_setup = False
|
|
if res and raid_setup:
|
|
devices = get_unused_disks()
|
|
if len(devices) == 0:
|
|
print('No free disks were detected, abort RAID/XFS setup. Disks must be unmounted before proceeding.\n')
|
|
raid_setup = False
|
|
else:
|
|
print('Please select unmounted disks from the following list: {}'.format(devices))
|
|
selected = []
|
|
dsklist = []
|
|
while True:
|
|
print('type \'cancel\' to cancel RAID/XFS setup.')
|
|
print('type \'done\' to finish selection. Selected: {}'.format(selected))
|
|
if len(dsklist) > 0:
|
|
dsk = dsklist.pop(0)
|
|
else:
|
|
dsk = input('> ')
|
|
if dsk == 'cancel':
|
|
raid_setup = 0
|
|
break
|
|
if dsk == 'done':
|
|
if len(selected) == 0:
|
|
continue
|
|
break
|
|
if dsk == '':
|
|
continue
|
|
if dsk.find(',') > 0:
|
|
dsklist = dsk.split(',')
|
|
continue
|
|
if not os.path.exists(dsk):
|
|
print('{} not found'.format(dsk))
|
|
continue
|
|
if not stat.S_ISBLK(os.stat(dsk).st_mode):
|
|
print('{} is not block device'.format(dsk))
|
|
continue
|
|
selected.append(dsk)
|
|
devices.remove(dsk)
|
|
disks = ','.join(selected)
|
|
args.disks = disks
|
|
|
|
args.no_raid_setup = not raid_setup
|
|
if raid_setup:
|
|
run_setup_script('RAID', '/usr/lib/scylla/scylla_raid_setup --disks {} --update-fstab'.format(disks))
|
|
|
|
coredump_setup = interactive_ask_service('Do you want to enable coredumps?', 'Yes - sets up coredump to allow a post-mortem analysis of the Scylla state just prior to a crash. No - skips this step.', coredump_setup)
|
|
args.no_coredump_setup = not coredump_setup
|
|
if coredump_setup:
|
|
if disks:
|
|
run_setup_script('coredump', '/usr/lib/scylla/scylla_coredump_setup --dump-to-raiddir')
|
|
else:
|
|
run_setup_script('coredump', '/usr/lib/scylla/scylla_coredump_setup')
|
|
|
|
sysconfig_setup = interactive_ask_service('Do you want to setup a system-wide customized configuration for Scylla?', 'Yes - setup the sysconfig file. No - skips this step.', sysconfig_setup)
|
|
args.no_sysconfig_setup = not sysconfig_setup
|
|
if sysconfig_setup:
|
|
nic = interactive_choose_nic()
|
|
set_nic_and_disks = interactive_ask_service('Do you want to enable Network Interface Card (NIC) and disk(s) optimization?', 'Yes - optimize the NIC queue and disks settings. Selecting Yes greatly improves performance. No - skip this step.', set_nic_and_disks)
|
|
if sysconfig_setup:
|
|
setup_args = '--setup-nic-and-disks' if set_nic_and_disks else ''
|
|
run_setup_script('NIC queue', '/usr/lib/scylla/scylla_sysconfig_setup --nic {nic} {setup_args}'.format(nic=nic, setup_args=setup_args))
|
|
|
|
io_setup = interactive_ask_service('Do you want IOTune to study your disks IO profile and adapt Scylla to it? (*WARNING* Saying NO here means the node will not boot in production mode unless you configure the I/O Subsystem manually!)', 'Yes - let iotune study my disk(s). Note that this action will take a few minutes. No - skip this step.', io_setup)
|
|
args.no_io_setup = not io_setup
|
|
if io_setup:
|
|
run_setup_script('IO configuration', '/usr/lib/scylla/scylla_io_setup')
|
|
|
|
node_exporter = interactive_ask_service('Do you want to install node exporter to export Prometheus data from the node? Note that the Scylla monitoring stack uses this data', 'Yes - install node exporter. No - skip this step.', node_exporter)
|
|
args.no_node_exporter = not node_exporter
|
|
if node_exporter:
|
|
run_setup_script('node exporter', '/usr/lib/scylla/node_exporter_install')
|
|
|
|
if args.developer_mode:
|
|
run_setup_script('developer mode', '/usr/lib/scylla/scylla_dev_mode_setup --developer-mode 1')
|
|
|
|
cpuscaling_setup = interactive_ask_service('Do you want to set the CPU scaling governor to Performance level on boot?', 'Yes - sets the CPU scaling governor to performance level. No - skip this step.', cpuscaling_setup)
|
|
args.no_cpuscaling_setup = not cpuscaling_setup
|
|
if cpuscaling_setup:
|
|
run_setup_script('CPU scaling', '/usr/lib/scylla/scylla_cpuscaling_setup')
|
|
|
|
fstrim_setup = interactive_ask_service('Do you want to enable fstrim service?', 'Yes - runs fstrim on your SSD. No - skip this step.', fstrim_setup)
|
|
args.no_fstrim_setup = not fstrim_setup
|
|
if fstrim_setup:
|
|
run_setup_script('fstrim', '/usr/lib/scylla/scylla_fstrim_setup')
|
|
|
|
print('ScyllaDB setup finished.')
|
|
|
|
if interactive:
|
|
print_non_interactive_suggestion_message(args)
|
|
|
|
if selinux_reboot_required:
|
|
print('Please restart your machine before using ScyllaDB, as you have disabled')
|
|
print(' SELinux.')
|
|
|
|
|
|
if dist_name() == 'Ubuntu':
|
|
run('apt-get install -y hugepages')
|