From fc71f4f671c4c0cfdb54a1ba638f7ae2acfef13c Mon Sep 17 00:00:00 2001 From: Philip Guyton Date: Mon, 5 Nov 2018 19:53:59 +0000 Subject: [PATCH] fix non legacy build/docker issues plus dev to rpm install mechanism #1989 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a number of the remaining build and docker issues concerning the proposed move to openSUSE as an upstream linux ditro base. Includes a fix to facilitate moving from a developer (source) install to an rpm based one: currently this only supports our existing legacy CentOS base, pending the instantiation of an openSUSE rpm build backend along with related distro aware repository config code changes. Docker specific notes: Many more modern dockerd invocations require a number of command line arguments. Previously we passed, from the docker.service file, only one: our --data-root target. The included modifications allows for more custom or distro specific requirements to be met via accommodation of any number of arguments (unfiltered). All prior Rockstor specific dockerd arguments are preserved and applied as before. Summary: - Add dependency on python ‘distro’ library. - Store build system distro info in django settings, the assumption here is we build on our target distro: normally the case. - Add distro UI element, uses prior 2 items. - Normalise prior UI subheader linux info formatting. - Remove prior incorrect data_collector code comment. - Selectively run postgresql-setup (legacy) or initdb (non legacy) in initrock. - Update psycopg2 from 2.6 to 2.7.4. - Normalise on direct paths for commands: avoids redundant fs redirection ie in CentOS root we have “/bin -> /usr/bin" and "/sbin - > /usr/sbin"; as these dir links are not found in our non legacy base move all hard wired command paths using them to their canonical reference. - Use Django settings for a selection of variably located (distro specific) command paths: again with the assumption that we build on our target distro. - Fix version indicator and software update page display for dev (source) installs. - Fix dev (source install) to rpm install transition mechanism - necessarily considered as a re-install so db is wiped during the transition. Note that this, in part, involved the addition of an explicit 'yum install rockstor' command during update, along with ensuring that initrock is re-run on next rockstor.service start. - Add distro aware docker.service template file selection based on distro.id(); moving fully to a live edit (during Rock-on service enable) rather than build time customization: ie to accommodate for our docker wrapper redirect and it’s consequent requirement for NofityAccess=all for Type=notify docker configs. Both included openSUSE templates are taken from their respective distro default installs of docker-ce. - Establish docker-generic.service failover config for unknown distro ids taken from default upstream docker-ce 18.09 CentOS example. - Enhance docker wrapper to pass additional arguments to dockerd. - Minor additional rock-ons-root config exception logging. - Catch and log harmless reboot/shutdown command exceptions with rc=-15. The exception log reports from these Web-UI initiated events are misleading as they suggest malfunction where there is none as both commands execute as expected with: out='', err='', and rc=-15. --- base-buildout.cfg | 8 +-- conf/django-hack | 2 +- conf/docker-distroid-notes.service.txt | 13 +++++ conf/docker-generic.service | 46 +++++++++++++++++ conf/docker-opensuse-leap.service | 35 +++++++++++++ conf/docker-opensuse-tumbleweed.service | 34 +++++++++++++ conf/docker.service.in | 2 +- conf/settings.conf.in | 22 ++++++++- setup.py | 3 +- src/rockstor/fs/btrfs.py | 10 ++-- src/rockstor/scripts/docker_wrapper.py | 22 +++++++-- src/rockstor/scripts/initrock.py | 12 ++++- src/rockstor/smart_manager/data_collector.py | 12 ++++- .../smart_manager/views/docker_service.py | 49 ++++++++++++++----- .../static/storageadmin/css/style.css | 29 +++++++---- .../static/storageadmin/js/router.js | 6 +++ .../templates/storageadmin/base.html | 1 + src/rockstor/system/acl.py | 4 +- src/rockstor/system/iscsi.py | 2 +- src/rockstor/system/osi.py | 39 +++++++++++---- src/rockstor/system/pkg_mgmt.py | 26 ++++++++-- src/rockstor/system/samba.py | 2 +- src/rockstor/system/services.py | 3 +- src/rockstor/system/ssh.py | 4 +- src/rockstor/system/util.py | 2 +- 25 files changed, 326 insertions(+), 62 deletions(-) create mode 100644 conf/docker-distroid-notes.service.txt create mode 100644 conf/docker-generic.service create mode 100644 conf/docker-opensuse-leap.service create mode 100644 conf/docker-opensuse-tumbleweed.service diff --git a/base-buildout.cfg b/base-buildout.cfg index d23600abc..76a588bd5 100644 --- a/base-buildout.cfg +++ b/base-buildout.cfg @@ -61,7 +61,7 @@ command = postgresql-server postgresql-devel kernel-ml btrfs-progs rsync \ nfs-utils avahi netatalk smartmontools net-tools sos hdparm \ postfix cyrus-sasl-plain yum-cron nano usbutils pciutils shellinabox \ - epel-release cryptsetup docker-ce + epel-release cryptsetup docker-ce python-distro [rpm-deps-nut] recipe = plone.recipe.command @@ -99,7 +99,6 @@ gunicorn = 19.7.1 supervisor = 3.0b1 python = 2.7.3 djangorecipe = 1.9 -psycopg2 = 2.6 [django] recipe = djangorecipe @@ -145,9 +144,12 @@ on_update = true cmds = ${buildout:directory}/bin/django collectstatic --noinput -i admin -v 0 [docker-conf] +# Consider inline sed of system's /usr/lib/systemd/system/docker.service +# that way we pick up new versions on each build. +# Or depricate and rely on docker_service.py to inline edit and assert. recipe = collective.recipe.template input = ${buildout:directory}/conf/docker.service.in -output = ${buildout:directory}/conf/docker.service +output = ${buildout:directory}/conf/docker-rockstor.service [rockstor-systemd-conf] recipe = collective.recipe.template diff --git a/conf/django-hack b/conf/django-hack index c17ed4f24..4ba835b76 100755 --- a/conf/django-hack +++ b/conf/django-hack @@ -24,7 +24,7 @@ sys.path[0:0] = [ join(base, 'eggs/oauthlib-1.0.1-py2.7.egg'), join(base, 'eggs/psutil-3.3.0-py2.7-linux-x86_64.egg'), join(base, 'eggs/psycogreen-1.0-py2.7.egg'), - join(base, 'eggs/psycopg2-2.6-py2.7-linux-x86_64.egg'), + join(base, 'eggs/psycopg2-2.7.4-py2.7-linux-x86_64.egg'), join(base, 'eggs/python_engineio-1.0.3-py2.7.egg'), join(base, 'eggs/python_socketio-1.6.0-py2.7.egg'), join(base, 'eggs/pytz-2014.3-py2.7.egg'), diff --git a/conf/docker-distroid-notes.service.txt b/conf/docker-distroid-notes.service.txt new file mode 100644 index 000000000..42f3c1d00 --- /dev/null +++ b/conf/docker-distroid-notes.service.txt @@ -0,0 +1,13 @@ +Files with a name pattern of: docker-distroid.service + +where distroid = distro.id() + +are currently hand copied from their distro docker package origin, usually: +/usr/lib/systemd/system/docker.service + +src/rockstor/smart_manager/views/docker_service.py then stream edits them. + +See docker_service.py for pre-instantiation edit details. + +Auto edit is performed on Docker service enable event within Web-UI and +attempts to honour all existing dockerd arguments. \ No newline at end of file diff --git a/conf/docker-generic.service b/conf/docker-generic.service new file mode 100644 index 000000000..41b3849a0 --- /dev/null +++ b/conf/docker-generic.service @@ -0,0 +1,46 @@ +[Unit] +Description=Docker Application Container Engine +Documentation=https://docs.docker.com +BindsTo=containerd.service +After=network-online.target firewalld.service +Wants=network-online.target + +[Service] +Type=notify +# the default is not to use systemd for cgroups because the delegate issues still +# exists and systemd currently does not support the cgroup feature set required +# for containers run by docker +ExecStart=/usr/bin/dockerd -H unix:// +ExecReload=/bin/kill -s HUP $MAINPID +TimeoutSec=0 +RestartSec=2 +Restart=always + +# Note that StartLimit* options were moved from "Service" to "Unit" in systemd 229. +# Both the old, and new location are accepted by systemd 229 and up, so using the old location +# to make them work for either version of systemd. +StartLimitBurst=3 + +# Note that StartLimitInterval was renamed to StartLimitIntervalSec in systemd 230. +# Both the old, and new name are accepted by systemd 230 and up, so using the old name to make +# this option work for either version of systemd. +StartLimitInterval=60s + +# Having non-zero Limit*s causes performance problems due to accounting overhead +# in the kernel. We recommend using cgroups to do container-local accounting. +LimitNOFILE=infinity +LimitNPROC=infinity +LimitCORE=infinity + +# Comment TasksMax if your systemd version does not supports it. +# Only systemd 226 and above support this option. +TasksMax=infinity + +# set delegate yes so that systemd does not reset the cgroups of docker containers +Delegate=yes + +# kill only the docker process, not all processes in the cgroup +KillMode=process + +[Install] +WantedBy=multi-user.target diff --git a/conf/docker-opensuse-leap.service b/conf/docker-opensuse-leap.service new file mode 100644 index 000000000..1444d851b --- /dev/null +++ b/conf/docker-opensuse-leap.service @@ -0,0 +1,35 @@ +[Unit] +Description=Docker Application Container Engine +Documentation=http://docs.docker.com +After=network.target containerd.socket containerd.service lvm2-monitor.service SuSEfirewall2.service +Requires=containerd.socket containerd.service + +[Service] +EnvironmentFile=/etc/sysconfig/docker + +# While Docker has support for socket activation (-H fd://), this is not +# enabled by default because enabling socket activation means that on boot your +# containers won't start until someone tries to administer the Docker daemon. +Type=notify +ExecStart=/usr/bin/dockerd --containerd /run/containerd/containerd.sock --add-runtime oci=/usr/sbin/docker-runc $DOCKER_NETWORK_OPTIONS $DOCKER_OPTS +ExecReload=/bin/kill -s HUP $MAINPID + +# Having non-zero Limit*s causes performance problems due to accounting overhead +# in the kernel. We recommend using cgroups to do container-local accounting. +LimitNOFILE=infinity +LimitNPROC=infinity +LimitCORE=infinity + +# Uncomment TasksMax if your systemd version supports it. +# Only systemd 226 and above support this property. +TasksMax=infinity + +# Set delegate yes so that systemd does not reset the cgroups of docker containers +# Only systemd 218 and above support this property. +Delegate=yes + +# This is not necessary because of how we set up containerd. +#KillMode=process + +[Install] +WantedBy=multi-user.target diff --git a/conf/docker-opensuse-tumbleweed.service b/conf/docker-opensuse-tumbleweed.service new file mode 100644 index 000000000..986b97508 --- /dev/null +++ b/conf/docker-opensuse-tumbleweed.service @@ -0,0 +1,34 @@ +[Unit] +Description=Docker Application Container Engine +Documentation=http://docs.docker.com +After=network.target lvm2-monitor.service SuSEfirewall2.service + +[Service] +EnvironmentFile=/etc/sysconfig/docker + +# While Docker has support for socket activation (-H fd://), this is not +# enabled by default because enabling socket activation means that on boot your +# containers won't start until someone tries to administer the Docker daemon. +Type=notify +ExecStart=/usr/bin/dockerd --add-runtime oci=/usr/sbin/docker-runc $DOCKER_NETWORK_OPTIONS $DOCKER_OPTS +ExecReload=/bin/kill -s HUP $MAINPID + +# Having non-zero Limit*s causes performance problems due to accounting overhead +# in the kernel. We recommend using cgroups to do container-local accounting. +LimitNOFILE=infinity +LimitNPROC=infinity +LimitCORE=infinity + +# Uncomment TasksMax if your systemd version supports it. +# Only systemd 226 and above support this property. +TasksMax=infinity + +# Set delegate yes so that systemd does not reset the cgroups of docker containers +# Only systemd 218 and above support this property. +Delegate=yes + +# This is not necessary because of how we set up containerd. +#KillMode=process + +[Install] +WantedBy=multi-user.target diff --git a/conf/docker.service.in b/conf/docker.service.in index 5a21366f7..2817f412d 100644 --- a/conf/docker.service.in +++ b/conf/docker.service.in @@ -1,7 +1,7 @@ [Unit] Description=Docker Application Container Engine Documentation=http://docs.docker.com -After=network.target docker.socket rockstor-bootstrap.service +After=network.target docker.socket Requires=docker.socket [Service] diff --git a/conf/settings.conf.in b/conf/settings.conf.in index 60d03c29e..ba2f8668c 100644 --- a/conf/settings.conf.in +++ b/conf/settings.conf.in @@ -19,6 +19,7 @@ along with this program. If not, see . # Django settings for Rockstor project. import os +import subprocess, distro DEBUG = ${django-settings-conf:debug} TEMPLATE_DEBUG = DEBUG @@ -416,4 +417,23 @@ TASK_SCHEDULER = { 'max_log': 100 #max number of task log entries to keep } -OAUTH2_PROVIDER_APPLICATION_MODEL = 'oauth2_provider.Application' \ No newline at end of file +OAUTH2_PROVIDER_APPLICATION_MODEL = 'oauth2_provider.Application' + +# Setup OS specific command paths via 'which cmd' calls +# N.B. this method will not work with an alias, ie in CentOS +# which ls +# alias ls='ls --color=auto' +# /usr/bin/ls +# The following have been tested in CentOS, openSUSE Leap15, and Tumbleweed +UDEVADM = subprocess.check_output(["which", "udevadm"]).rstrip() +SHUTDOWN = subprocess.check_output(["which", "shutdown"]).rstrip() +CHKCONFIG_BIN = subprocess.check_output(["which", "chkconfig"]).rstrip() + +# Establish our OS base id, name, and version: +# Use id for code path decisions. Others are for Web-UI display purposes. +# Examples given are for CentOS Rockstor variant, Leap 15, and Tumblweed. +OS_DISTRO_ID = distro.id() # rockstor, opensuse-leap, opensuse-tumbleweed +OS_DISTRO_NAME = distro.name() # Rockstor, openSUSE Leap, openSUSE Tumbleweed +# Note that the following will capture the build os version. +# For live updates (running system) we call distro.version() directly in code. +OS_DISTRO_VERSION = distro.version() # 3, 15.0 ,20181107 diff --git a/setup.py b/setup.py index 665491411..8d5f54539 100644 --- a/setup.py +++ b/setup.py @@ -76,12 +76,13 @@ 'mock == 1.0.1', 'psutil == 3.3.0', 'psycogreen == 1.0', - 'psycopg2 == 2.6', + 'psycopg2 == 2.7.4', 'python-socketio == 1.6.0', 'pytz == 2014.3', 'pyzmq == 15.0.0', 'requests == 1.1.0', 'six == 1.10.0', + 'distro', ] ) diff --git a/src/rockstor/fs/btrfs.py b/src/rockstor/fs/btrfs.py index 829904ee7..0695fb769 100644 --- a/src/rockstor/fs/btrfs.py +++ b/src/rockstor/fs/btrfs.py @@ -34,12 +34,12 @@ logger = logging.getLogger(__name__) -MKFS_BTRFS = '/sbin/mkfs.btrfs' -BTRFS = '/sbin/btrfs' -MOUNT = '/bin/mount' -UMOUNT = '/bin/umount' +MKFS_BTRFS = '/usr/sbin/mkfs.btrfs' +BTRFS = '/usr/sbin/btrfs' +MOUNT = '/usr/bin/mount' +UMOUNT = '/usr/bin/umount' DEFAULT_MNT_DIR = '/mnt2/' -RMDIR = '/bin/rmdir' +RMDIR = '/usr/bin/rmdir' QID = '2015' # The following model/db default setting is also used when quotas are disabled. PQGROUP_DEFAULT = settings.MODEL_DEFS['pqgroup'] diff --git a/src/rockstor/scripts/docker_wrapper.py b/src/rockstor/scripts/docker_wrapper.py index 7c974c277..3456465e4 100644 --- a/src/rockstor/scripts/docker_wrapper.py +++ b/src/rockstor/scripts/docker_wrapper.py @@ -23,9 +23,23 @@ DOCKERD = '/usr/bin/dockerd' +ROCKSTOR_DOCKER_OPTS = [ + '--log-driver=journald', + '--storage-driver', 'btrfs', + '--storage-opt', 'btrfs.min_space=1G'] + def main(): - mnt_pt = sys.argv[1] + # We expect the last element of our argument list to be the mount point as + # docker_service.py formats it that way.: + mnt_pt = sys.argv[-1] + # N.B. sys.argv[0] is name of script itself and always present. + system_docker_opts = [] + if len(sys.argv) > 2: + # We have at least 1 additional argument passed so extract it/them ie: + # [script-name, additional-arg, mount-point] + # we extract additional-arg (or it's plural counterpart) as a list. + system_docker_opts = sys.argv[1:-1] sname = mnt_pt.split('/')[-1] try: so = Share.objects.get(name=sname) @@ -33,6 +47,6 @@ def main(): except Exception as e: sys.exit('Failed to mount Docker root(%s). Exception: %s' % (mnt_pt, e.__str__())) - run_command([DOCKERD, '--log-driver=journald', '--storage-driver', - 'btrfs', '--storage-opt', 'btrfs.min_space=1G', '--data-root', - mnt_pt]) + cmd = [DOCKERD] + ROCKSTOR_DOCKER_OPTS + system_docker_opts + \ + ['--data-root', mnt_pt] + run_command(cmd) diff --git a/src/rockstor/scripts/initrock.py b/src/rockstor/scripts/initrock.py index e155a0a4a..106b572bf 100644 --- a/src/rockstor/scripts/initrock.py +++ b/src/rockstor/scripts/initrock.py @@ -347,7 +347,17 @@ def main(): logger.debug('Deleting /var/lib/pgsql/data') shutil.rmtree('/var/lib/pgsql/data') logging.info('initializing Postgresql...') - run_command(['/usr/bin/postgresql-setup', 'initdb']) + # Conditionally run this only if found (CentOS/RedHat script) + if os.path.isfile('/usr/bin/postgresql-setup'): + logger.debug('running postgresql-setup initdb') + # Legacy (CentOS) db init command + run_command(['/usr/bin/postgresql-setup', 'initdb']) + else: + ## In eg openSUSE run the generic initdb from postgresql##-server + if os.path.isfile('/usr/bin/initdb'): + logger.debug('running generic initdb on {}'.format(pg_data)) + run_command( + ['su', '-', 'postgres', '-c', '/usr/bin/initdb', pg_data]) logging.info('Done.') run_command([SYSCTL, 'restart', 'postgresql']) run_command([SYSCTL, 'status', 'postgresql']) diff --git a/src/rockstor/smart_manager/data_collector.py b/src/rockstor/smart_manager/data_collector.py index c6feaedf6..5f4d6b58b 100644 --- a/src/rockstor/smart_manager/data_collector.py +++ b/src/rockstor/smart_manager/data_collector.py @@ -51,6 +51,7 @@ from system.services import service_status # noqa E402 from cli.api_wrapper import APIWrapper # noqa E402 from system.pkg_mgmt import (update_check, yum_check) # noqa E402 +import distro import logging # noqa E402 logger = logging.getLogger(__name__) @@ -838,6 +839,7 @@ class SysinfoNamespace(RockstorIO): start = False supported_kernel = settings.SUPPORTED_KERNEL_VERSION + os_distro_name = settings.OS_DISTRO_NAME # This function is run once on every connection def on_connect(self, sid, environ): @@ -857,6 +859,7 @@ def on_connect(self, sid, environ): self.spawn(self.prune_logs, sid) self.spawn(self.send_localtime, sid) self.spawn(self.send_uptime, sid) + self.spawn(self.send_distroinfo, sid) self.spawn(self.shutdown_status, sid) self.spawn(self.pool_degraded_status, sid) self.spawn(self.pool_dev_stats, sid) @@ -868,11 +871,18 @@ def on_disconnect(self, sid): self.start = False def send_uptime(self): - # Seems redundant + while self.start: self.emit('uptime', {'key': 'sysinfo:uptime', 'data': uptime()}) gevent.sleep(60) + def send_distroinfo(self): + while self.start: + data = {'distro': self.os_distro_name, 'version': distro.version()} + self.emit('distro_info', + {'key': 'sysinfo:distro_info', 'data': data}) + gevent.sleep(600) + def send_localtime(self): while self.start: diff --git a/src/rockstor/smart_manager/views/docker_service.py b/src/rockstor/smart_manager/views/docker_service.py index e9967c5e1..b3d4de39e 100644 --- a/src/rockstor/smart_manager/views/docker_service.py +++ b/src/rockstor/smart_manager/views/docker_service.py @@ -15,7 +15,6 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ - from rest_framework.response import Response from storageadmin.util import handle_exception from system.services import systemctl @@ -27,10 +26,16 @@ from fs.btrfs import mount_share import re import shutil +import distro import logging logger = logging.getLogger(__name__) +DOCKERD = '/usr/bin/dockerd' + +# Distro's for which we have known working conf/docker-distroid.service files. +KNOWN_DISTRO_IDS = ['rockstor', 'opensuse-leap', 'opensuse-tumbleweed'] + class DockerServiceView(BaseServiceDetailView): name = 'docker' @@ -40,45 +45,63 @@ def _validate_root(self, request, root): return Share.objects.get(name=root) except Exception as e: logger.exception(e) - e_msg = ('Share(%s) does not exist' % root) + e_msg = 'Share name ({}) does not exist.'.format(root) handle_exception(Exception(e_msg), request) @transaction.atomic def post(self, request, command): service = Service.objects.get(name=self.name) - if (command == 'config'): + if command == 'config': config = request.data.get('config', None) root_share = config['root_share'] self._validate_root(request, root_share) self._save_config(service, config) - elif (command == 'start'): + elif command == 'start': try: config = self._get_config(service) - except: + except Exception as e: + logger.exception(e) e_msg = ('Cannot start without configuration. ' - 'Please configure(System->Services) and try again.') + 'Please configure (System->Services) and try again.') handle_exception(Exception(e_msg), request) share = self._validate_root(request, config['root_share']) - mnt_pt = ('%s%s' % (settings.MNT_PT, share.name)) + mnt_pt = '{}{}'.format(settings.MNT_PT, share.name) if not share.is_mounted: mount_share(share, mnt_pt) - inf = ('%s/docker.service' % (settings.CONFROOT)) + docker_wrapper = '{}bin/docker-wrapper'.format(settings.ROOT_DIR) + distro_id = distro.id() # for Leap 15 <--> Tumbleweed moves. + if distro_id not in KNOWN_DISTRO_IDS: + distro_id = 'generic' + # TODO: Consider sourcing /usr/lib/systemd/system/docker.service + inf = '{}/docker-{}.service'.format(settings.CONFROOT, distro_id) outf = '/etc/systemd/system/docker.service' with open(inf) as ino, open(outf, 'w') as outo: for l in ino.readlines(): - if (re.match('ExecStart=', l) is not None): - outo.write('%s %s\n' % (l.strip(), mnt_pt)) + if re.match('ExecStart=', l) is not None: + outo.write('{} {}\n'.format( + l.strip().replace(DOCKERD, docker_wrapper, 1), + mnt_pt)) + elif re.match('Type=notify', l) is not None: + # Our docker wrapper use need NotifyAccess=all: avoids + # "Got notification message from PID ####1, but + # reception only permitted for main PID ####2" + outo.write(l) + outo.write('NotifyAccess=all\n') + elif re.match('After=', l) is not None: + outo.write('{} {}\n'.format( + l.strip(), 'rockstor-bootstrap.service')) else: outo.write(l) - socket_file = ('%s/docker.socket' % (settings.CONFROOT)) - shutil.copy(socket_file, '/etc/systemd/system/docker.socket') + if distro_id == 'rockstor': + socket_file = '{}/docker.socket'.format(settings.CONFROOT) + shutil.copy(socket_file, '/etc/systemd/system/docker.socket') systemctl(self.name, 'enable') systemctl(self.name, 'start') - elif (command == 'stop'): + elif command == 'stop': systemctl(self.name, 'stop') systemctl(self.name, 'disable') return Response() diff --git a/src/rockstor/storageadmin/static/storageadmin/css/style.css b/src/rockstor/storageadmin/static/storageadmin/css/style.css index 908c456df..731afa809 100644 --- a/src/rockstor/storageadmin/static/storageadmin/css/style.css +++ b/src/rockstor/storageadmin/static/storageadmin/css/style.css @@ -841,7 +841,10 @@ label.error { font-size: 12px; color: #b94a48; } #appliance-loadavg { float:right; - color: #fff; + padding-left: 5px; + color: #ffffff; + font-family: Roboto-Light; + font-size: 12px; } #yum-msg { @@ -865,6 +868,22 @@ label.error { font-size: 12px; color: #b94a48; } } } +#uptime { + float: right; + padding-left: 10px; + color: #FFFFFF; + font-family: Roboto-Light; + font-size: 12px; +} + +#distro-info { + float: right; + padding-left: 10px; + color: #FFFFFF; + font-family: Roboto-Light; + font-size: 12px; +} + hr.module-sep { margin: 0; border: 0; border-top: 1px solid #ddd; } @@ -2767,14 +2786,6 @@ This file is generated by `grunt build`, do not edit it by hand. font-weight:bold; } -#uptime { - float: right; - padding-left: 10px; - color: #FFFFFF; - font-family: Roboto-Light; - font-size: 12px; -} - .gentleselect-label { background-color: white; } diff --git a/src/rockstor/storageadmin/static/storageadmin/js/router.js b/src/rockstor/storageadmin/static/storageadmin/js/router.js index 71286ab51..ae47b0a38 100644 --- a/src/rockstor/storageadmin/static/storageadmin/js/router.js +++ b/src/rockstor/storageadmin/static/storageadmin/js/router.js @@ -1026,6 +1026,11 @@ $(document).ready(function() { $loadavg.text('Linux: ' + data); }; + var distroInfo = function(data) { + $('#distro-info').text(data.distro); + $('#distro-info').attr('title', data.version); + }; + var displayLocaleTime = function(data) { $('#local-time > span').text(data); @@ -1189,6 +1194,7 @@ $(document).ready(function() { RockStorSocket.addListener(kernelInfo, this, 'sysinfo:kernel_info'); + RockStorSocket.addListener(distroInfo, this, 'sysinfo:distro_info'); RockStorSocket.addListener(displayLoadAvg, this, 'sysinfo:uptime'); RockStorSocket.addListener(displayLocaleTime, this, 'sysinfo:localtime'); RockStorSocket.addListener(displayYumUpdates, this, 'sysinfo:yum_updates'); diff --git a/src/rockstor/storageadmin/templates/storageadmin/base.html b/src/rockstor/storageadmin/templates/storageadmin/base.html index 9b377290b..9e48c9274 100644 --- a/src/rockstor/storageadmin/templates/storageadmin/base.html +++ b/src/rockstor/storageadmin/templates/storageadmin/base.html @@ -171,6 +171,7 @@
+
diff --git a/src/rockstor/system/acl.py b/src/rockstor/system/acl.py index 837e0e7e8..a7e776565 100644 --- a/src/rockstor/system/acl.py +++ b/src/rockstor/system/acl.py @@ -18,8 +18,8 @@ from osi import run_command -CHOWN = '/bin/chown' -CHMOD = '/bin/chmod' +CHOWN = '/usr/bin/chown' +CHMOD = '/usr/bin/chmod' def chown(share, owner, group=None, recursive=False): diff --git a/src/rockstor/system/iscsi.py b/src/rockstor/system/iscsi.py index e9db3ad4a..45ce771cb 100644 --- a/src/rockstor/system/iscsi.py +++ b/src/rockstor/system/iscsi.py @@ -19,7 +19,7 @@ from osi import run_command TGTADM_BIN = '/usr/sbin/tgtadm' -DD_BIN = '/bin/dd' +DD_BIN = '/usr/bin/dd' def create_target_device(tid, tname): diff --git a/src/rockstor/system/osi.py b/src/rockstor/system/osi.py index 2025aca48..89c753ca8 100644 --- a/src/rockstor/system/osi.py +++ b/src/rockstor/system/osi.py @@ -41,7 +41,7 @@ CAT = '/usr/bin/cat' CHATTR = '/usr/bin/chattr' -DD = '/bin/dd' +DD = '/usr/bin/dd' DEFAULT_MNT_DIR = '/mnt2/' EXPORTFS = '/usr/sbin/exportfs' GRUBBY = '/usr/sbin/grubby' @@ -50,15 +50,15 @@ HOSTNAMECTL = '/usr/bin/hostnamectl' LS = '/usr/bin/ls' LSBLK = '/usr/bin/lsblk' -MKDIR = '/bin/mkdir' -MOUNT = '/bin/mount' +MKDIR = '/usr/bin/mkdir' +MOUNT = '/usr/bin/mount' NMCLI = '/usr/bin/nmcli' -RMDIR = '/bin/rmdir' -SHUTDOWN = '/usr/sbin/shutdown' +RMDIR = '/usr/bin/rmdir' +SHUTDOWN = settings.SHUTDOWN SYSTEMCTL_BIN = '/usr/bin/systemctl' SYSTEMD_ESCAPE = '/usr/bin/systemd-escape' -UDEVADM = '/usr/sbin/udevadm' -UMOUNT = '/bin/umount' +UDEVADM = settings.UDEVADM +UMOUNT = '/usr/bin/umount' WIPEFS = '/usr/sbin/wipefs' RTC_WAKE_FILE = '/sys/class/rtc/rtc0/wakealarm' RETURN_BOOLEAN = True @@ -1151,13 +1151,32 @@ def get_virtio_disk_serial(device_name): def system_shutdown(delta='now'): # New delta param default to now used to pass a 2 min delay # for scheduled tasks reboot/shutdown - return run_command([SHUTDOWN, '-h', delta]) - + try: + cmd = [SHUTDOWN, '-h', delta] + o, e, rc = run_command(cmd) + except CommandException as e: + # Catch / log harmless -15 return code - command executes as expected. + if e.rc == -15: + logger.info('Ignoring rc=-15 from command ({}).'.format(cmd)) + return e.out, e.err, e.rc + # otherwise we raise an exception as normal. + raise e + return o, e, rc def system_reboot(delta='now'): # New delta param default to now used to pass a 2 min delay # for scheduled tasks reboot/shutdown - return run_command([SHUTDOWN, '-r', delta]) + try: + cmd = [SHUTDOWN, '-r', delta] + o, e, rc = run_command(cmd) + except CommandException as e: + # Catch / log harmless -15 return code - command executes as expected. + if e.rc == -15: + logger.info('Ignoring rc=-15 from command ({}).'.format(cmd)) + return e.out, e.err, e.rc + # otherwise we raise an exception as normal. + raise e + return o, e, rc def system_suspend(): diff --git a/src/rockstor/system/pkg_mgmt.py b/src/rockstor/system/pkg_mgmt.py index c2c992df9..038215d69 100644 --- a/src/rockstor/system/pkg_mgmt.py +++ b/src/rockstor/system/pkg_mgmt.py @@ -26,6 +26,10 @@ from datetime import (datetime, timedelta) import requests from django.conf import settings +from system.exceptions import CommandException +import logging + +logger = logging.getLogger(__name__) YUM = '/usr/bin/yum' RPM = '/usr/bin/rpm' @@ -92,9 +96,20 @@ def current_version(): def rpm_build_info(pkg): - version = None + version = 'Unknown Version' date = None - o, e, rc = run_command([YUM, 'info', 'installed', '-v', pkg]) + try: + o, e, rc = run_command([YUM, 'info', 'installed', '-v', pkg]) + except CommandException as e: + # Catch "No matching Packages to list" so we can return None, None. + emsg = 'Error: No matching Packages to list' + # By checking both the first error element and the second to last we + # catch one yum waiting for another to release yum lock. + if e.err[0] == emsg or e.err[-2] == emsg: + logger.info('No "rockstor" package found: source install?') + return version, date + # otherwise we raise an exception as normal. + raise e for l in o: if (re.match('Buildtime', l) is not None): # eg: "Buildtime : Tue Dec 5 13:34:06 2017" @@ -109,7 +124,7 @@ def rpm_build_info(pkg): version = l.strip().split()[2] if (re.match('Release ', l) is not None): version = '%s-%s' % (version, l.strip().split()[2]) - return (version, date) + return version, date def switch_repo(subscription, on=True): @@ -207,9 +222,14 @@ def update_run(subscription=None, yum_update=False): with open(npath, 'w') as atfo: if not yum_update: atfo.write('%s stop rockstor\n' % SYSTEMCTL) + # rockstor-pre stop ensures initrock re-run on next rockstor start + atfo.write('%s stop rockstor-pre\n' % SYSTEMCTL) atfo.write('/usr/bin/find %s -name "*.pyc" -type f -delete\n' % settings.ROOT_DIR) atfo.write('%s --setopt=timeout=600 -y update\n' % YUM) + # account for moving from dev/source to package install: + atfo.write('%s --setopt=timeout=600 -y install rockstor\n' % YUM) + # the following rockstor start invokes rockstor-pre (initrock) also atfo.write('%s start rockstor\n' % SYSTEMCTL) else: atfo.write('%s --setopt=timeout=600 -y -x rock* update\n' % YUM) diff --git a/src/rockstor/system/samba.py b/src/rockstor/system/samba.py index ec3ad6d81..362901573 100644 --- a/src/rockstor/system/samba.py +++ b/src/rockstor/system/samba.py @@ -29,7 +29,7 @@ TESTPARM = '/usr/bin/testparm' SMB_CONFIG = '/etc/samba/smb.conf' SYSTEMCTL = '/usr/bin/systemctl' -CHMOD = '/bin/chmod' +CHMOD = '/usr/bin/chmod' RS_SHARES_HEADER = '####BEGIN: Rockstor SAMBA CONFIG####' RS_SHARES_FOOTER = '####END: Rockstor SAMBA CONFIG####' RS_AD_HEADER = '####BEGIN: Rockstor ACTIVE DIRECTORY CONFIG####' diff --git a/src/rockstor/system/services.py b/src/rockstor/system/services.py index 09ceba01a..eddc0ec69 100644 --- a/src/rockstor/system/services.py +++ b/src/rockstor/system/services.py @@ -24,8 +24,7 @@ import os from shutil import move - -CHKCONFIG_BIN = '/sbin/chkconfig' +CHKCONFIG_BIN = settings.CHKCONFIG_BIN AUTHCONFIG = '/usr/sbin/authconfig' SSHD_CONFIG = '/etc/ssh/sshd_config' SYSTEMCTL_BIN = '/usr/bin/systemctl' diff --git a/src/rockstor/system/ssh.py b/src/rockstor/system/ssh.py index 1e9479e8f..b3eb13a7a 100644 --- a/src/rockstor/system/ssh.py +++ b/src/rockstor/system/ssh.py @@ -26,8 +26,8 @@ SSHD_CONFIG = '/etc/ssh/sshd_config' -MKDIR = '/bin/mkdir' -MOUNT = '/bin/mount' +MKDIR = '/usr/bin/mkdir' +MOUNT = '/usr/bin/mount' USERMOD = '/usr/sbin/usermod' SFTP_REGEX = 'Subsystem\s+sftp' SFTP_STR = 'Subsystem\tsftp\tinternal-sftp' diff --git a/src/rockstor/system/util.py b/src/rockstor/system/util.py index fa9bc1b46..cc74dd917 100644 --- a/src/rockstor/system/util.py +++ b/src/rockstor/system/util.py @@ -18,7 +18,7 @@ from osi import run_command -TAR = '/bin/tar' +TAR = '/usr/bin/tar' def archive_logs(outfile, log_dir):