Browse Source
The original cgcs-patch is rpm based which requires a complete re-write to work on ostree/dpkg systems like Debian. The code has been forked, since the older Centos env and python2.7 are end-of-life. Forking the code allows all new development to not require re-testing on Centos. The debian folder under cgcs-patch has been moved under sw-patch Renaming and refactoring will be done in later commits. pylint is un-clamped in order to work on python3.9 Some minor pylint suppressions have been added. Test Plan: Verify that this builds on Debian Verify that the ISO installs the new content on Debian without breaking packages that import cgcs_patch. Verify patching service runs on Debian Co-Authored-By: Jessica Castelino <jessica.castelino@windriver.com> Story: 2009101 Task: 43076 Signed-off-by: Al Bailey <al.bailey@windriver.com> Change-Id: I3f1bca749404053bae63d4bcc9fb2477cf909fcdchanges/84/835484/18
107 changed files with 15094 additions and 15 deletions
@ -1,5 +1,7 @@
|
||||
# This is a cross-platform list tracking distribution packages needed for install and tests; |
||||
# see https://docs.openstack.org/infra/bindep/ for additional information. |
||||
# Do not install python2 rpms in a python3 only environment such as debian-bullseye |
||||
|
||||
python-rpm [platform:dpkg !platform:debian-bullseye] |
||||
python3-rpm [platform:dpkg] |
||||
rpm-python [platform:rpm] |
||||
|
@ -1,3 +1,3 @@
|
||||
cgcs-patch |
||||
enable-dev-patch |
||||
patch-alarm |
||||
sw-patch |
||||
|
@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env python |
||||
# |
||||
# Copyright (c) 2013-2014 Wind River Systems, Inc. |
||||
# |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
# |
||||
|
||||
|
||||
|
||||
import sys |
||||
|
||||
from cgcs_make_patch.make_patch_functions import make_patch |
||||
|
||||
if __name__ == "__main__": |
||||
sys.exit(make_patch()) |
@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env python |
||||
# |
||||
# Copyright (c) 2013-2014 Wind River Systems, Inc. |
||||
# |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
# |
||||
|
||||
|
||||
|
||||
import sys |
||||
|
||||
from cgcs_make_patch.make_patch_functions import modify_patch |
||||
|
||||
if __name__ == "__main__": |
||||
sys.exit(modify_patch()) |
@ -0,0 +1,52 @@
|
||||
# |
||||
# Copyright (c) 2016 Wind River Systems, Inc. |
||||
# |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
# |
||||
|
||||
# |
||||
# This bash source file provides variables and functions that |
||||
# may be used by in-service patching scripts. |
||||
# |
||||
|
||||
# Source platform.conf, for nodetype and subfunctions |
||||
. /etc/platform/platform.conf |
||||
|
||||
declare PATCH_SCRIPTDIR=/run/patching/patch-scripts |
||||
declare PATCH_FLAGDIR=/run/patching/patch-flags |
||||
declare -i PATCH_STATUS_OK=0 |
||||
declare -i PATCH_STATUS_FAILED=1 |
||||
|
||||
declare logfile=/var/log/patching.log |
||||
declare NAME=$(basename $0) |
||||
|
||||
function loginfo() |
||||
{ |
||||
echo "`date "+%FT%T.%3N"`: $NAME: $*" >> $logfile |
||||
} |
||||
|
||||
function is_controller() |
||||
{ |
||||
[[ $nodetype == "controller" ]] |
||||
} |
||||
|
||||
function is_worker() |
||||
{ |
||||
[[ $nodetype == "worker" ]] |
||||
} |
||||
|
||||
function is_storage() |
||||
{ |
||||
[[ $nodetype == "storage" ]] |
||||
} |
||||
|
||||
function is_cpe() |
||||
{ |
||||
[[ $nodetype == "controller" && $subfunction =~ worker ]] |
||||
} |
||||
|
||||
function is_locked() |
||||
{ |
||||
test -f /var/run/.node_locked |
||||
} |
||||
|
@ -0,0 +1,2 @@
|
||||
d /run/patching 0700 root root - |
||||
|
@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env python |
||||
|
||||
""" |
||||
Copyright (c) 2014 Wind River Systems, Inc. |
||||
|
||||
SPDX-License-Identifier: Apache-2.0 |
||||
|
||||
""" |
||||
|
||||
import sys |
||||
|
||||
from cgcs_patch.patch_functions import patch_build |
||||
|
||||
if __name__ == "__main__": |
||||
sys.exit(patch_build()) |
||||
|
@ -0,0 +1,27 @@
|
||||
#!/bin/bash |
||||
# |
||||
# Copyright (c) 2014 Wind River Systems, Inc. |
||||
# |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
# |
||||
|
||||
# Patching "goenabled" check. |
||||
# If a patch has been applied on this node, it is now out-of-date and should be rebooted. |
||||
|
||||
NAME=$(basename $0) |
||||
SYSTEM_CHANGED_FLAG=/var/run/node_is_patched |
||||
|
||||
logfile=/var/log/patching.log |
||||
|
||||
function LOG { |
||||
logger "$NAME: $*" |
||||
echo "`date "+%FT%T.%3N"`: $NAME: $*" >> $logfile |
||||
} |
||||
|
||||
if [ -f $SYSTEM_CHANGED_FLAG ]; then |
||||
LOG "Node has been patched. Failing goenabled check." |
||||
exit 1 |
||||
fi |
||||
|
||||
exit 0 |
||||
|
@ -0,0 +1,7 @@
|
||||
[runtime] |
||||
controller_multicast = 239.1.1.3 |
||||
agent_multicast = 239.1.1.4 |
||||
api_port = 5487 |
||||
controller_port = 5488 |
||||
agent_port = 5489 |
||||
|
@ -0,0 +1,15 @@
|
||||
/var/log/patching.log |
||||
/var/log/patching-api.log |
||||
/var/log/patching-insvc.log |
||||
{ |
||||
nodateext |
||||
size 10M |
||||
start 1 |
||||
rotate 10 |
||||
missingok |
||||
notifempty |
||||
compress |
||||
delaycompress |
||||
copytruncate |
||||
} |
||||
|
@ -0,0 +1,19 @@
|
||||
[process] |
||||
process = sw-patch-agent |
||||
pidfile = /var/run/sw-patch-agent.pid |
||||
script = /etc/init.d/sw-patch-agent |
||||
style = lsb ; ocf or lsb |
||||
severity = major ; Process failure severity |
||||
; critical : host is failed |
||||
; major : host is degraded |
||||
; minor : log is generated |
||||
restarts = 3 ; Number of back to back unsuccessful restarts before severity assertion |
||||
interval = 5 ; Number of seconds to wait between back-to-back unsuccessful restarts |
||||
debounce = 20 ; Number of seconds the process needs to run before declaring |
||||
; it as running O.K. after a restart. |
||||
; Time after which back-to-back restart count is cleared. |
||||
startuptime = 10 ; Seconds to wait after process start before starting the debounce monitor |
||||
mode = passive ; Monitoring mode: passive (default) or active |
||||
; passive: process death monitoring (default: always) |
||||
; active: heartbeat monitoring, i.e. request / response messaging |
||||
|
@ -0,0 +1,19 @@
|
||||
[process] |
||||
process = sw-patch-controller-daemon |
||||
pidfile = /var/run/sw-patch-controller-daemon.pid |
||||
script = /etc/init.d/sw-patch-controller-daemon |
||||
style = lsb ; ocf or lsb |
||||
severity = major ; Process failure severity |
||||
; critical : host is failed |
||||
; major : host is degraded |
||||
; minor : log is generated |
||||
restarts = 3 ; Number of back to back unsuccessful restarts before severity assertion |
||||
interval = 5 ; Number of seconds to wait between back-to-back unsuccessful restarts |
||||
debounce = 20 ; Number of seconds the process needs to run before declaring |
||||
; it as running O.K. after a restart. |
||||
; Time after which back-to-back restart count is cleared. |
||||
startuptime = 10 ; Seconds to wait after process start before starting the debounce monitor |
||||
mode = passive ; Monitoring mode: passive (default) or active |
||||
; passive: process death monitoring (default: always) |
||||
; active: heartbeat monitoring, i.e. request / response messaging |
||||
|
@ -0,0 +1,5 @@
|
||||
{ |
||||
"admin": "role:admin or role:administrator", |
||||
"admin_api": "is_admin:True", |
||||
"default": "rule:admin_api" |
||||
} |
@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env python |
||||
# |
||||
# Copyright (c) 2013-2014 Wind River Systems, Inc. |
||||
# |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
# |
||||
|
||||
|
||||
|
||||
import sys |
||||
|
||||
from cgcs_make_patch.make_patch_functions import query_patch |
||||
|
||||
if __name__ == "__main__": |
||||
sys.exit(query_patch()) |
@ -0,0 +1,183 @@
|
||||
#!/bin/bash |
||||
# |
||||
# Copyright (c) 2016 Wind River Systems, Inc. |
||||
# |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
# |
||||
|
||||
if [[ $EUID -ne 0 ]]; then |
||||
echo "This utility must be run as root." >&2 |
||||
exit 1 |
||||
fi |
||||
|
||||
function show_usage() |
||||
{ |
||||
cat <<EOF |
||||
Usage: [ --include-pyc ] [ --include-cfg ] --skip-multi [ pkg ... ] |
||||
|
||||
This utility scans the installed RPMs to compare checksums of files. |
||||
By default, files flagged as config are skipped, as are python pyc files. |
||||
|
||||
Optional arguments: |
||||
--include-pyc : Include pyc files in check |
||||
--include-cfg : Include config files in check |
||||
--skip-links : Skip symlink check |
||||
--skip-multi : Skip the search for files with multiple owners |
||||
pkg : Specify one or more packages to limit the scan |
||||
(implies --skip-multi) |
||||
|
||||
EOF |
||||
|
||||
exit 1 |
||||
} |
||||
|
||||
declare INCLUDE_PYTHON_FILES="no" |
||||
declare INCLUDE_CFG_FILES="no" |
||||
declare CHECK_FOR_MULTI="yes" |
||||
declare CHECK_LINKS="yes" |
||||
declare TIS_ONLY="yes" |
||||
|
||||
declare CHECK_RPM= |
||||
|
||||
for arg in "$@" |
||||
do |
||||
case $arg in |
||||
-h|--help) |
||||
show_usage |
||||
;; |
||||
--include-pyc) |
||||
INCLUDE_PYTHON_FILES="yes" |
||||
;; |
||||
--include-cfg) |
||||
INCLUDE_CFG_FILES="yes" |
||||
;; |
||||
--skip-links) |
||||
CHECK_LINKS="no" |
||||
;; |
||||
--skip-multi) |
||||
CHECK_FOR_MULTI="no" |
||||
;; |
||||
--all-rpms) |
||||
TIS_ONLY="no" |
||||
;; |
||||
*) |
||||
CHECK_RPM="$CHECK_RPM $arg" |
||||
CHECK_FOR_MULTI="no" |
||||
;; |
||||
esac |
||||
done |
||||
|
||||
function rpm_list() |
||||
{ |
||||
if [ -n "$CHECK_RPM" ] |
||||
then |
||||
for pkg in $CHECK_RPM |
||||
do |
||||
echo $pkg |
||||
done |
||||
elif [ "$TIS_ONLY" = "yes" ] |
||||
then |
||||
rpm -qa | grep '\.tis\.' | sort |
||||
else |
||||
rpm -qa | sort |
||||
fi |
||||
} |
||||
|
||||
rpm_list | while read pkg |
||||
do |
||||
# Get the --dump from the pkg |
||||
rpm -q --queryformat "[%{FILENAMES}|%{FILEMD5S}|%{FILEFLAGS:fflags}|%{FILELINKTOS}\n]" $pkg | \ |
||||
while IFS='|' read pname psum pflags plinkto |
||||
do |
||||
if [[ $pname == "(contains" ]] |
||||
then |
||||
# (contains no files) |
||||
continue |
||||
fi |
||||
|
||||
if [[ $INCLUDE_CFG_FILES == "no" && $pflags =~ c ]] |
||||
then |
||||
# Skip file already flagged as config |
||||
continue |
||||
fi |
||||
|
||||
if [[ $INCLUDE_PYTHON_FILES == "no" && $pname =~ \.py[co]$ ]] |
||||
then |
||||
# Skip python .pyo or .pyc file |
||||
continue |
||||
fi |
||||
|
||||
# Directories and symlinks will have no checksum |
||||
if [[ -z $psum ]] |
||||
then |
||||
if [[ -n $plinkto && $CHECK_LINKS == "yes" ]] |
||||
then |
||||
# Check the symlink pointer |
||||
flinkto=$(readlink $pname) |
||||
if [[ "$flinkto" != "$plinkto" ]] |
||||
then |
||||
echo "Link Mismatch: $pname ($pkg)" |
||||
fi |
||||
fi |
||||
continue |
||||
fi |
||||
|
||||
# Does the file exist? |
||||
if [ ! -e "$pname" ] |
||||
then |
||||
echo "Missing: $pname ($pkg)" |
||||
continue |
||||
fi |
||||
|
||||
# Has the file been replaced by a symlink? ie. update-alternatives |
||||
if [ -L "$pname" ] |
||||
then |
||||
continue |
||||
fi |
||||
|
||||
let -i sumlen=$(echo -n $psum | wc -c) |
||||
if [ $sumlen = 64 ] |
||||
then |
||||
sumcmd=sha256sum |
||||
else |
||||
sumcmd=md5sum |
||||
fi |
||||
|
||||
echo $psum $pname | $sumcmd --check --status |
||||
if [ $? -ne 0 ] |
||||
then |
||||
echo "Mismatch: $pname ($pkg)" |
||||
fi |
||||
done |
||||
done |
||||
|
||||
|
||||
function check_for_multi_master() |
||||
{ |
||||
# Search for files owned by multiple packages |
||||
prev= |
||||
rpm_list | xargs rpm -q --queryformat "[%{FILENAMES}|%{=NAME}\n]" | sort | while IFS='|' read f p |
||||
do |
||||
if [ "$f" = "$prev" ] |
||||
then |
||||
echo $f |
||||
fi |
||||
prev=$f |
||||
done | sort -u | while read f |
||||
do |
||||
if [ ! -d "$f" ] |
||||
then |
||||
echo $f |
||||
fi |
||||
done |
||||
} |
||||
|
||||
if [ $CHECK_FOR_MULTI = "yes" ] |
||||
then |
||||
echo |
||||
echo |
||||
echo "The following files belong to multiple packages:" |
||||
echo |
||||
check_for_multi_master |
||||
fi |
||||
|
@ -0,0 +1,60 @@
|
||||
#!/bin/bash |
||||
# |
||||
# Copyright (c) 2016 Wind River Systems, Inc. |
||||
# |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
# |
||||
|
||||
. /etc/patching/patch-functions |
||||
|
||||
declare SCRIPTS=$(find $PATCH_SCRIPTDIR -type f -executable | sort) |
||||
declare -i NUM_SCRIPTS=$(echo "$SCRIPTS" | wc -l) |
||||
|
||||
if [ $NUM_SCRIPTS -eq 0 ] |
||||
then |
||||
loginfo "No in-service patch scripts found." |
||||
exit 0 |
||||
fi |
||||
|
||||
loginfo "Running $NUM_SCRIPTS in-service patch scripts" |
||||
|
||||
declare SCRIPTLOG=/var/log/patching-insvc.log |
||||
cat <<EOF >>$SCRIPTLOG |
||||
############################################################ |
||||
`date "+%FT%T.%3N"`: Running $NUM_SCRIPTS in-service patch scripts: |
||||
|
||||
$SCRIPTS |
||||
|
||||
############################################################ |
||||
EOF |
||||
|
||||
declare -i FAILURES=0 |
||||
for cmd in $SCRIPTS |
||||
do |
||||
cat <<EOF >>$SCRIPTLOG |
||||
############################################################ |
||||
`date "+%FT%T.%3N"`: Running $cmd |
||||
|
||||
EOF |
||||
|
||||
bash -x $cmd >>$SCRIPTLOG 2>&1 |
||||
rc=$? |
||||
if [ $rc -ne $PATCH_STATUS_OK ] |
||||
then |
||||
let -i FAILURES++ |
||||
fi |
||||
cat <<EOF >>$SCRIPTLOG |
||||
`date "+%FT%T.%3N"`: Completed running $cmd (rc=$rc) |
||||
############################################################ |
||||
|
||||
EOF |
||||
done |
||||
|
||||
cat <<EOF >>$SCRIPTLOG |
||||
|
||||
`date "+%FT%T.%3N"`: Completed running scripts with $FAILURES failures |
||||
############################################################ |
||||
EOF |
||||
|
||||
exit $FAILURES |
||||
|
@ -0,0 +1,182 @@
|
||||
#!/usr/bin/env python |
||||
|
||||
""" |
||||
Copyright (c) 2018-2020 Wind River Systems, Inc. |
||||
|
||||
SPDX-License-Identifier: Apache-2.0 |
||||
|
||||
""" |
||||
|
||||
import getopt |
||||
import os |
||||
import platform |
||||
import rpm |
||||
import shutil |
||||
import subprocess |
||||
import sys |
||||
import tempfile |
||||
|
||||
import cgcs_patch.patch_functions as pf |
||||
import cgcs_patch.patch_verify as pv |
||||
import cgcs_patch.constants as constants |
||||
|
||||
import logging |
||||
logging.getLogger('main_logger') |
||||
logging.basicConfig(level=logging.INFO) |
||||
|
||||
# Override the pv.dev_certificate_marker so we can verify signatures off-box |
||||
cgcs_patch_bindir = os.path.dirname(os.path.abspath(sys.argv[0])) |
||||
dev_cert_path = os.path.abspath(os.path.join(cgcs_patch_bindir, '../../enable-dev-patch/enable-dev-patch/dev_certificate_enable.bin')) |
||||
|
||||
pv.dev_certificate_marker = dev_cert_path |
||||
|
||||
def usage(): |
||||
print "Usage: %s -o <repodir> <patch> ..." \ |
||||
% os.path.basename(sys.argv[0]) |
||||
exit(1) |
||||
|
||||
|
||||
def main(): |
||||
try: |
||||
opts, remainder = getopt.getopt(sys.argv[1:], |
||||
'o:', |
||||
['output=']) |
||||
except getopt.GetoptError: |
||||
usage() |
||||
|
||||
output = None |
||||
|
||||
for opt, arg in opts: |
||||
if opt == "--output" or opt == '-o': |
||||
output = arg |
||||
|
||||
if output is None: |
||||
usage() |
||||
|
||||
sw_version = os.environ['PLATFORM_RELEASE'] |
||||
|
||||
allpatches = pf.PatchData() |
||||
|
||||
output = os.path.abspath(output) |
||||
|
||||
pkgdir = os.path.join(output, 'Packages') |
||||
datadir = os.path.join(output, 'metadata') |
||||
committed_dir = os.path.join(datadir, 'committed') |
||||
|
||||
if os.path.exists(output): |
||||
# Check to see if the expected structure already exists, |
||||
# maybe we're appending a patch. |
||||
if not os.path.exists(committed_dir) or not os.path.exists(pkgdir): |
||||
print "Packages or metadata dir missing from existing %s. Aborting..." % output |
||||
exit(1) |
||||
|
||||
# Load the existing metadata |
||||
allpatches.load_all_metadata(committed_dir, constants.COMMITTED) |
||||
else: |
||||
os.mkdir(output, 0o755) |
||||
os.mkdir(datadir, 0o755) |
||||
os.mkdir(committed_dir, 0o755) |
||||
os.mkdir(pkgdir, 0o755) |
||||
|
||||
# Save the current directory, so we can chdir back after |
||||
orig_wd = os.getcwd() |
||||
|
||||
tmpdir = None |
||||
try: |
||||
for p in remainder: |
||||
fpath = os.path.abspath(p) |
||||
|
||||
# Create a temporary working directory |
||||
tmpdir = tempfile.mkdtemp(prefix="patchrepo_") |
||||
|
||||
# Change to the tmpdir |
||||
os.chdir(tmpdir) |
||||
|
||||
print "Parsing %s" % fpath |
||||
pf.PatchFile.read_patch(fpath) |
||||
|
||||
thispatch = pf.PatchData() |
||||
patch_id = thispatch.parse_metadata("metadata.xml", constants.COMMITTED) |
||||
|
||||
if patch_id in allpatches.metadata: |
||||
print "Skipping %s as it's already in the repo" % patch_id |
||||
# Change back to original working dir |
||||
os.chdir(orig_wd) |
||||
|
||||
shutil.rmtree(tmpdir) |
||||
tmpdir = None |
||||
|
||||
continue |
||||
|
||||
patch_sw_version = thispatch.query_line(patch_id, 'sw_version') |
||||
if patch_sw_version != sw_version: |
||||
raise Exception("%s is for release %s, not %s" % (patch_id, patch_sw_version, sw_version)) |
||||
|
||||
# Move the metadata to the "committed" dir, and the rpms to the Packages dir |
||||
shutil.move('metadata.xml', os.path.join(committed_dir, "%s-metadata.xml" % patch_id)) |
||||
for f in thispatch.query_line(patch_id, 'contents'): |
||||
shutil.move(f, pkgdir) |
||||
|
||||
allpatches.add_patch(patch_id, thispatch) |
||||
|
||||
# Change back to original working dir |
||||
os.chdir(orig_wd) |
||||
|
||||
shutil.rmtree(tmpdir) |
||||
tmpdir = None |
||||
except: |
||||
if tmpdir is not None: |
||||
# Change back to original working dir |
||||
os.chdir(orig_wd) |
||||
|
||||
shutil.rmtree(tmpdir) |
||||
tmpdir = None |
||||
raise |
||||
|
||||
allpatches.gen_release_groups_xml(sw_version, output) |
||||
|
||||
# Purge unneeded RPMs |
||||
keep = {} |
||||
for patch_id in allpatches.metadata.keys(): |
||||
for rpmname in allpatches.contents[patch_id]: |
||||
try: |
||||
pkgname, arch, pkgver = pf.parse_rpm_filename(rpmname) |
||||
except ValueError as e: |
||||
raise e |
||||
|
||||
if pkgname not in keep: |
||||
keep[pkgname] = { arch: pkgver } |
||||
continue |
||||
elif arch not in keep[pkgname]: |
||||
keep[pkgname][arch] = pkgver |
||||
continue |
||||
|
||||
# Compare versions |
||||
keep_pkgver = keep[pkgname][arch] |
||||
if pkgver > keep_pkgver: |
||||
# Find the rpmname |
||||
keep_rpmname = keep_pkgver.generate_rpm_filename(pkgname, arch) |
||||
|
||||
filename = os.path.join(pkgdir, keep_rpmname) |
||||
if os.path.exists(filename): |
||||
os.remove(filename) |
||||
|
||||
# Keep the new pkgver |
||||
keep[pkgname][arch] = pkgver |
||||
else: |
||||
filename = os.path.join(pkgdir, rpmname) |
||||
if os.path.exists(filename): |
||||
os.remove(filename) |
||||
|
||||
# Create the repo metadata |
||||
if os.path.exists('/usr/bin/createrepo_c'): |
||||
createrepo = '/usr/bin/createrepo_c' |
||||
else: |
||||
createrepo = 'createrepo' |
||||
|
||||
os.chdir(output) |
||||
subprocess.check_call([createrepo, '-g', 'comps.xml', '.']) |
||||
|
||||
if __name__ == "__main__": |
||||
sys.exit(main()) |
||||
|
@ -0,0 +1,16 @@
|
||||
#!/usr/bin/python |
||||
|
||||
""" |
||||
Copyright (c) 2014 Wind River Systems, Inc. |
||||
|
||||
SPDX-License-Identifier: Apache-2.0 |
||||
|
||||
""" |
||||
|
||||
import sys |
||||
|
||||
from cgcs_patch.patch_client import main |
||||
|
||||
if __name__ == "__main__": |
||||
main() |
||||
|
@ -0,0 +1,16 @@
|
||||
#!/usr/bin/python |
||||
|
||||
""" |
||||
Copyright (c) 2014 Wind River Systems, Inc. |
||||
|
||||
SPDX-License-Identifier: Apache-2.0 |
||||
|
||||
""" |
||||
|
||||
import sys |
||||
|
||||
from cgcs_patch.patch_agent import main |
||||
|
||||
if __name__ == "__main__": |
||||
main() |
||||
|
@ -0,0 +1,94 @@
|
||||
#!/bin/sh |
||||
# |
||||
# Copyright (c) 2014-2015 Wind River Systems, Inc. |
||||
# |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
# |
||||
|
||||
# chkconfig: 345 26 30 |
||||
|
||||
### BEGIN INIT INFO |
||||
# Provides: sw-patch-agent |
||||
# Required-Start: $syslog |
||||
# Required-Stop: $syslog |
||||
# Default-Start: 2 3 5 |
||||
# Default-Stop: 0 1 6 |
||||
# Short-Description: sw-patch-agent |
||||
# Description: Provides the CGCS Patch Agent Daemon |
||||
### END INIT INFO |
||||
|
||||
DESC="sw-patch-agent" |
||||
DAEMON="/usr/sbin/sw-patch-agent" |
||||
PIDFILE="/var/run/sw-patch-agent.pid" |
||||
PATCH_INSTALLING_FILE="/var/run/patch_installing" |
||||
|
||||
start() |
||||
{ |
||||
if [ -e $PIDFILE ]; then |
||||
PIDDIR=/proc/$(cat $PIDFILE) |
||||
if [ -d ${PIDDIR} ]; then |
||||
echo "$DESC already running." |
||||
exit 1 |
||||
else |
||||
echo "Removing stale PID file $PIDFILE" |
||||
rm -f $PIDFILE |
||||
fi |
||||
fi |
||||
|
||||
echo -n "Starting $DESC..." |
||||
|
||||
start-stop-daemon --start --quiet --background \ |
||||
--pidfile ${PIDFILE} --make-pidfile --exec ${DAEMON} |
||||
|
||||
if [ $? -eq 0 ]; then |
||||
echo "done." |
||||
else |
||||
echo "failed." |
||||
fi |
||||
} |
||||
|
||||
stop() |
||||
{ |
||||
if [ -f $PATCH_INSTALLING_FILE ]; then |
||||
echo "Patches are installing. Waiting for install to complete." |
||||
while [ -f $PATCH_INSTALLING_FILE ]; do |
||||
# Verify the agent is still running |
||||
pid=$(cat $PATCH_INSTALLING_FILE) |
||||
cat /proc/$pid/cmdline 2>/dev/null | grep -q $DAEMON |
||||
if [ $? -ne 0 ]; then |
||||
echo "Patch agent not running." |
||||
break |
||||
fi |
||||
sleep 1 |
||||
done |
||||
echo "Continuing with shutdown." |
||||
fi |
||||
|
||||
echo -n "Stopping $DESC..." |
||||
start-stop-daemon --stop --quiet --pidfile $PIDFILE |
||||
if [ $? -eq 0 ]; then |
||||
echo "done." |
||||
else |
||||
echo "failed." |
||||
fi |
||||
rm -f $PIDFILE |
||||
} |
||||
|
||||
case "$1" in |
||||
start) |
||||
start |
||||
;; |
||||
stop) |
||||
stop |
||||
;; |
||||
restart|force-reload) |
||||
stop |
||||
start |
||||
;; |
||||
*) |
||||
echo "Usage: $0 {start|stop|force-reload|restart}" |
||||
exit 1 |
||||
;; |
||||
esac |
||||
|
||||
exit 0 |
@ -0,0 +1,20 @@
|
||||
#!/bin/bash |
||||
# |
||||
# Copyright (c) 2016 Wind River Systems, Inc. |
||||
# |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
# |
||||
|
||||
. /etc/patching/patch-functions |
||||
|
||||
# |
||||
# Triggering a restart of the patching daemons is done by |
||||
# creating a flag file and letting the daemon handle the restart. |
||||
# |
||||
loginfo "Requesting restart of patch-agent" |
||||
|
||||
restart_patch_agent_flag="/run/patching/.restart.patch-agent" |
||||
touch $restart_patch_agent_flag |
||||
|
||||
exit 0 |
||||
|
@ -0,0 +1,16 @@
|
||||
[Unit] |
||||
Description=StarlingX Patching Agent |
||||
After=syslog.target network-online.target sw-patch.service |
||||
Before=pmon.service |
||||
|
||||
[Service] |
||||
Type=forking |
||||
User=root |
||||
ExecStart=/etc/init.d/sw-patch-agent start |
||||
ExecStop=/etc/init.d/sw-patch-agent stop |
||||
ExecReload=/etc/init.d/sw-patch-agent restart |
||||
PIDFile=/var/run/sw-patch-agent.pid |
||||
|
||||
[Install] |
||||
WantedBy=multi-user.target |
||||
|
@ -0,0 +1,16 @@
|
||||
#!/usr/bin/python |
||||
|
||||
""" |
||||
Copyright (c) 2014 Wind River Systems, Inc. |
||||
|
||||
SPDX-License-Identifier: Apache-2.0 |
||||
|
||||
""" |
||||
|
||||
import sys |
||||
|
||||
from cgcs_patch.patch_controller import main |
||||
|
||||
if __name__ == "__main__": |
||||
main() |
||||
|
@ -0,0 +1,78 @@
|
||||
#!/bin/sh |
||||
# |
||||
# Copyright (c) 2014-2015 Wind River Systems, Inc. |
||||
# |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
# |
||||
|
||||
# chkconfig: 345 25 30 |
||||
|
||||
### BEGIN INIT INFO |
||||
# Provides: sw-patch-controller-daemon |
||||
# Required-Start: $syslog |
||||
# Required-Stop: $syslog |
||||
# Default-Start: 2 3 5 |
||||
# Default-Stop: 0 1 6 |
||||
# Short-Description: sw-patch-controller-daemon |
||||
# Description: Provides the CGCS Patch Controller Daemon |
||||
### END INIT INFO |
||||
|
||||
DESC="sw-patch-controller-daemon" |
||||
DAEMON="/usr/sbin/sw-patch-controller-daemon" |
||||
PIDFILE="/var/run/sw-patch-controller-daemon.pid" |
||||
|
||||
start() |
||||
{ |
||||
if [ -e $PIDFILE ]; then |
||||
PIDDIR=/proc/$(cat $PIDFILE) |
||||
if [ -d ${PIDDIR} ]; then |
||||
echo "$DESC already running." |
||||
exit 1 |
||||
else |
||||
echo "Removing stale PID file $PIDFILE" |
||||
rm -f $PIDFILE |
||||
fi |
||||
fi |
||||
|
||||
echo -n "Starting $DESC..." |
||||
|
||||
start-stop-daemon --start --quiet --background \ |
||||
--pidfile ${PIDFILE} --make-pidfile --exec ${DAEMON} |
||||
|
||||
if [ $? -eq 0 ]; then |
||||
echo "done." |
||||
else |
||||
echo "failed." |
||||
fi |
||||
} |
||||
|
||||
stop() |
||||
{ |
||||
echo -n "Stopping $DESC..." |
||||
start-stop-daemon --stop --quiet --pidfile $PIDFILE |
||||
if [ $? -eq 0 ]; then |
||||
echo "done." |
||||
else |
||||
echo "failed." |
||||
fi |
||||
rm -f $PIDFILE |
||||
} |
||||
|
||||
case "$1" in |
||||
start) |
||||
start |
||||
;; |
||||
stop) |
||||
stop |
||||
;; |
||||
restart|force-reload) |
||||
stop |
||||
start |
||||
;; |
||||
*) |
||||
echo "Usage: $0 {start|stop|force-reload|restart}" |
||||
exit 1 |
||||
;; |
||||
esac |
||||
|
||||
exit 0 |
@ -0,0 +1,20 @@
|
||||
#!/bin/bash |
||||
# |
||||
# Copyright (c) 2016 Wind River Systems, Inc. |
||||
# |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
# |
||||
|
||||
. /etc/patching/patch-functions |
||||
|
||||
# |
||||
# Triggering a restart of the patching daemons is done by |
||||
# creating a flag file and letting the daemon handle the restart. |
||||
# |
||||
loginfo "Requesting restart of patch-controller" |
||||
|
||||
restart_patch_controller_flag="/run/patching/.restart.patch-controller" |
||||
touch $restart_patch_controller_flag |
||||
|
||||
exit 0 |
||||
|
@ -0,0 +1,16 @@
|
||||
[Unit] |
||||
Description=StarlingX Patching Controller Daemon |
||||
After=syslog.target network-online.target sw-patch.service sw-patch-controller.service |
||||
Before=pmon.service |
||||
|
||||
[Service] |
||||
Type=forking |
||||
User=root |
||||
ExecStart=/etc/init.d/sw-patch-controller-daemon start |
||||
ExecStop=/etc/init.d/sw-patch-controller-daemon stop |
||||
ExecReload=/etc/init.d/sw-patch-controller-daemon restart |
||||
PIDFile=/var/run/sw-patch-controller-daemon.pid |
||||
|
||||
[Install] |
||||
WantedBy=multi-user.target |
||||
|
@ -0,0 +1,106 @@
|
||||
#!/bin/bash |
||||
# |
||||
# Copyright (c) 2014 Wind River Systems, Inc. |
||||
# |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
# |
||||
# StarlingX Patching Controller setup |
||||
# chkconfig: 345 20 24 |
||||
# description: CGCS Patching Controller init script |
||||
|
||||
### BEGIN INIT INFO |
||||
# Provides: sw-patch-controller |
||||
# Required-Start: $syslog |
||||
# Required-Stop: $syslog |
||||
# Default-Start: 2 3 5 |
||||
# Default-Stop: 0 1 6 |
||||
# Short-Description: sw-patch-controller |
||||
# Description: Provides the StarlingX Patch Controller Daemon |
||||
### END INIT INFO |
||||
|
||||
. /usr/bin/tsconfig |
||||
|
||||
NAME=$(basename $0) |
||||
|
||||
REPO_ID=updates |
||||
REPO_ROOT=/var/www/pages/${REPO_ID} |
||||
REPO_DIR=${REPO_ROOT}/rel-${SW_VERSION} |
||||
GROUPS_FILE=$REPO_DIR/comps.xml |
||||
PATCHING_DIR=/opt/patching |
||||
|
||||
logfile=/var/log/patching.log |
||||
|
||||
function LOG { |
||||
logger "$NAME: $*" |
||||
echo "`date "+%FT%T.%3N"`: $NAME: $*" >> $logfile |
||||
} |
||||
|
||||
function LOG_TO_FILE { |
||||
echo "`date "+%FT%T.%3N"`: $NAME: $*" >> $logfile |
||||
} |
||||
|
||||
function create_groups { |
||||
if [ -f $GROUPS_FILE ]; then |
||||
return 0 |
||||
fi |
||||
|
||||
cat >$GROUPS_FILE <<EOF |
||||
<comps> |
||||
</comps> |
||||
|
||||
EOF |
||||
} |
||||
|
||||
function do_setup { |
||||
# Does the repo exist? |
||||
if [ ! -d $REPO_DIR ]; then |
||||
LOG "Creating repo" |
||||
mkdir -p $REPO_DIR |
||||
|
||||
# Setup the groups file |
||||
create_groups |
||||
|
||||
createrepo -g $GROUPS_FILE $REPO_DIR >> $logfile 2>&1 |
||||
fi |
||||
|
||||
if [ ! -d $PATCHING_DIR ]; then |
||||
LOG "Creating $PATCHING_DIR" |
||||
mkdir -p $PATCHING_DIR |
||||
fi |
||||
|
||||
# If we can ping the active controller, sync the repos |
||||
LOG_TO_FILE "ping -c 1 -w 1 controller" |
||||
ping -c 1 -w 1 controller >> $logfile 2>&1 || ping6 -c 1 -w 1 controller >> $logfile 2>&1 |
||||
if [ $? -ne 0 ]; then |
||||
LOG "Cannot ping controller. Nothing to do" |
||||
return 0 |
||||
fi |
||||
|
||||
# Sync the patching dir |
||||
LOG_TO_FILE "rsync -acv --delete rsync://controller/patching/ ${PATCHING_DIR}/" |
||||
rsync -acv --delete rsync://controller/patching/ ${PATCHING_DIR}/ >> $logfile 2>&1 |
||||
|
||||
# Sync the patching dir |
||||
LOG_TO_FILE "rsync -acv --delete rsync://controller/repo/ ${REPO_ROOT}/" |
||||
rsync -acv --delete rsync://controller/repo/ ${REPO_ROOT}/ >> $logfile 2>&1 |
||||
} |
||||
|
||||
case "$1" in |
||||
start) |
||||
do_setup |
||||
;; |
||||
status) |
||||
;; |
||||
stop) |
||||
# Nothing to do here |
||||
;; |
||||
restart) |
||||
do_setup |
||||
;; |
||||
*) |
||||
echo "Usage: $0 {status|start|stop|restart}" |
||||
exit 1 |
||||
esac |
||||
|
||||
exit 0 |
||||
|
@ -0,0 +1,14 @@
|
||||
[Unit] |
||||
Description=StarlingX Patching Controller |
||||
After=syslog.service network-online.target sw-patch.service |
||||
Before=sw-patch-agent.service sw-patch-controller-daemon.service |
||||
|
||||
[Service] |
||||
Type=oneshot |
||||
User=root |
||||
ExecStart=/etc/init.d/sw-patch-controller start |
||||
RemainAfterExit=yes |
||||
|
||||
[Install] |
||||
WantedBy=multi-user.target |
||||
|
@ -0,0 +1,178 @@
|
||||
#!/bin/bash |
||||
# |
||||
# Copyright (c) 2014-2020 Wind River Systems, Inc. |
||||
# |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
# |
||||
# StarlingX Patching |
||||
# chkconfig: 345 20 23 |
||||
# description: StarlingX Patching init script |
||||
|
||||
### BEGIN INIT INFO |
||||
# Provides: sw-patch |
||||
# Required-Start: $syslog |
||||
# Required-Stop: $syslog |
||||
# Default-Start: 2 3 5 |
||||
# Default-Stop: 0 1 6 |
||||
# Short-Description: sw-patch |
||||
# Description: Provides the StarlingX Patching |
||||
### END INIT INFO |
||||
|
||||
NAME=$(basename $0) |
||||
|
||||
. /usr/bin/tsconfig |
||||
. /etc/platform/platform.conf |
||||
|
||||
logfile=/var/log/patching.log |
||||
patch_failed_file=/var/run/patch_install_failed |
||||
patched_during_init=/etc/patching/.patched_during_init |
||||
|
||||
function LOG_TO_FILE { |
||||
echo "`date "+%FT%T.%3N"`: $NAME: $*" >> $logfile |
||||
} |
||||
|
||||
function check_for_rr_patch { |
||||
if [ -f /var/run/node_is_patched_rr ]; then |
||||
if [ ! -f ${patched_during_init} ]; then |
||||
echo |
||||
echo "Node has been patched and requires an immediate reboot." |
||||
echo |
||||
LOG_TO_FILE "Node has been patched, with reboot-required flag set. Rebooting" |
||||
touch ${patched_during_init} |
||||
/sbin/reboot |
||||
else |
||||
echo |
||||
echo "Node has been patched during init a second consecutive time. Skipping reboot due to possible error" |
||||
echo |
||||
LOG_TO_FILE "Node has been patched during init a second consecutive time. Skipping reboot due to possible error" |
||||
touch ${patch_failed_file} |
||||
rm -f ${patched_during_init} |
||||
exit 1 |
||||
fi |
||||
else |
||||
rm -f ${patched_during_init} |
||||
fi |
||||
} |
||||
|
||||
function check_install_uuid { |
||||
# Check whether our installed load matches the active controller |
||||
CONTROLLER_UUID=`curl -sf http://controller:${http_port}/feed/rel-${SW_VERSION}/install_uuid` |
||||
if [ $? -ne 0 ]; then |
||||
if [ "$HOSTNAME" = "controller-1" ]; then |
||||
# If we're on controller-1, controller-0 may not have the install_uuid |
||||
# matching this release, if we're in an upgrade. If the file doesn't exist, |
||||
# bypass this check |
||||
return 0 |
||||
fi |
||||
|
||||
LOG_TO_FILE "Unable to retrieve installation uuid from active controller" |
||||
echo "Unable to retrieve installation uuid from active controller" |
||||
return 1 |
||||
fi |
||||
|
||||
if [ "$INSTALL_UUID" != "$CONTROLLER_UUID" ]; then |
||||
LOG_TO_FILE "This node is running a different load than the active controller and must be reinstalled" |
||||
echo "This node is running a different load than the active controller and must be reinstalled" |
||||
return 1 |
||||
fi |
||||
|
||||
return 0 |
||||
} |
||||
|
||||
# Check for installation failure |
||||
if [ -f /etc/platform/installation_failed ] ; then |
||||
LOG_TO_FILE "/etc/platform/installation_failed flag is set. Aborting." |
||||
echo "$(basename $0): Detected installation failure. Aborting." |
||||
exit 1 |
||||
fi |
||||
|
||||
# Clean up the RPM DB |
||||
if [ ! -f /var/run/.rpmdb_cleaned ]; then |
||||
LOG_TO_FILE "Cleaning RPM DB" |
||||
rm -f /var/lib/rpm/__db* |
||||
touch /var/run/.rpmdb_cleaned |
||||
fi |
||||
|
||||
# For AIO-SX, abort if config is not yet applied and this is running in init |
||||
if [ "${system_mode}" = "simplex" -a ! -f ${INITIAL_CONTROLLER_CONFIG_COMPLETE} -a "$1" = "start" ]; then |
||||
LOG_TO_FILE "Config is not yet applied. Skipping init patching" |
||||
exit 0 |
||||
fi |
||||
|
||||
# If the management interface is bonded, it may take some time |
||||
# before communications can be properly setup. |
||||
# Allow up to $DELAY_SEC seconds to reach controller. |
||||
DELAY_SEC=120 |
||||
START=`date +%s` |
||||
FOUND=0 |
||||
while [ $(date +%s) -lt $(( ${START} + ${DELAY_SEC} )) ]; do |
||||
ping -c 1 controller > /dev/null 2>&1 || ping6 -c 1 controller > /dev/null 2>&1 |
||||
if [ $? -eq 0 ]; then |
||||
FOUND=1 |
||||
break |
||||
fi |
||||
sleep 1 |
||||
done |
||||
|
||||
if [ ${FOUND} -eq 0 ]; then |
||||
# 'controller' is not available, just exit |
||||
LOG_TO_FILE "Unable to contact active controller (controller). Boot will continue." |
||||
exit 1 |
||||
fi |
||||
|
||||
RC=0 |
||||
case "$1" in |
||||
start) |
||||
if [ "${system_mode}" = "simplex" ]; then |
||||
# On a simplex CPE, we need to launch the http server first, |
||||
# before we can do the patch installation |
||||
LOG_TO_FILE "***** Launching lighttpd *****" |
||||
/etc/init.d/lighttpd start |
||||
|
||||
LOG_TO_FILE "***** Starting patch operation *****" |
||||
/usr/sbin/sw-patch-agent --install 2>>$logfile |
||||
if [ -f ${patch_failed_file} ]; then |
||||
RC=1 |
||||
LOG_TO_FILE "***** Patch operation failed *****" |
||||
fi |
||||
LOG_TO_FILE "***** Finished patch operation *****" |
||||
|
||||
LOG_TO_FILE "***** Shutting down lighttpd *****" |
||||
/etc/init.d/lighttpd stop |
||||
else |
||||
check_install_uuid |
||||
if [ $? -ne 0 ]; then |
||||
# The INSTALL_UUID doesn't match the active controller, so exit |
||||
exit 1 |
||||
fi |
||||
|
||||
LOG_TO_FILE "***** Starting patch operation *****" |
||||
/usr/sbin/sw-patch-agent --install 2>>$logfile |
||||
if [ -f ${patch_failed_file} ]; then |
||||
RC=1 |
||||
LOG_TO_FILE "***** Patch operation failed *****" |
||||
fi |
||||
LOG_TO_FILE "***** Finished patch operation *****" |
||||
fi |
||||
|
||||
check_for_rr_patch |
||||
;; |
||||
stop) |
||||
# Nothing to do here |
||||
;; |
||||
restart) |
||||
LOG_TO_FILE "***** Starting patch operation *****" |
||||
/usr/sbin/sw-patch-agent --install 2>>$logfile |
||||
if [ -f ${patch_failed_file} ]; then |
||||
RC=1 |
||||
LOG_TO_FILE "***** Patch operation failed *****" |
||||
fi |
||||
LOG_TO_FILE "***** Finished patch operation *****" |
||||
;; |
||||
*) |
||||
echo "Usage: $0 {start|stop|restart}" |
||||
exit 1 |
||||
esac |
||||
|
||||
exit $RC |
||||
|
@ -0,0 +1,148 @@
|
||||
# |
||||
# Copyright (c) 2016 Wind River Systems, Inc. |
||||
# |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
# |
||||
|
||||
# |
||||
# This file provides bash-completion functionality for the sw-patch CLI |
||||
# |
||||
|
||||
function _swpatch() |
||||
{ |
||||
COMPREPLY=() |
||||
local cur="${COMP_WORDS[COMP_CWORD]}" |
||||
local prev="${COMP_WORDS[COMP_CWORD-1]}" |
||||
local subcommand=${COMP_WORDS[1]} |
||||
|
||||
# |
||||
# The available sw-patch subcommands |
||||
# |
||||
local subcommands=" |
||||
apply |
||||
commit |
||||
delete |
||||
query |
||||
query-dependencies |
||||
query-hosts |
||||
remove |
||||
show |
||||
upload |
||||
upload-dir |
||||
what-requires |
||||
drop-host |
||||
is-applied |
||||
is-available |
||||
report-app-dependencies |
||||
query-app-dependencies |
||||
" |
||||
if [ -f /etc/platform/.initial_config_complete ]; then |
||||
# Post-config, so the host-install commands are accessible |
||||
subcommands="${subcommands} host-install host-install-async" |
||||
else |
||||
# Pre-config, so the install-local command is accessible |
||||
subcommands="${subcommands} install-local" |
||||
fi |
||||
|
||||
# Appends the '/' when completing dir names |
||||
set mark-directories on |
||||
|
||||
if [ $COMP_CWORD -gt 1 ]; then |
||||
# |
||||
# Complete the arguments to the subcommands. |
||||
# |
||||
case "$subcommand" in |
||||
apply|delete|show|what-requires|is-applied|is-available) |
||||
# Query the list of known patches |
||||
local patches=$(sw-patch completion patches 2>/dev/null) |
||||
COMPREPLY=( $(compgen -W "${patches}" -- ${cur}) ) |
||||
return 0 |
||||
;; |
||||
remove) |
||||
# Query the list of known patches |
||||
local patches=$(sw-patch completion patches 2>/dev/null) |
||||
COMPREPLY=( $(compgen -W "--skipappcheck ${patches}" -- ${cur}) ) |
||||
return 0 |
||||
;; |
||||
host-install|host-install-async|drop-host) |
||||
if [ "${prev}" = "${subcommand}" -o "${prev}" = "--force" ]; then |
||||
# Query the list of known hosts |
||||
local names=$(sw-patch completion hosts 2>/dev/null) |
||||
COMPREPLY=( $(compgen -W "${names}" -- ${cur}) ) |
||||
else |
||||
# Only one host can be specified, so no more completion |
||||
COMPREPLY=( $(compgen -- ${cur}) ) |
||||
fi |
||||
return 0 |
||||
;; |
||||
upload) |
||||
# Allow dirs and files with .patch extension for completion |
||||
COMPREPLY=( $(compgen -f -o plusdirs -X '!*.patch' -- ${cur}) ) |
||||
return 0 |
||||
;; |
||||
upload-dir) |
||||
# Allow dirs only for completion |
||||
COMPREPLY=( $(compgen -d -- ${cur}) ) |
||||
return 0 |
||||
;; |
||||
query) |
||||
if [ "${prev}" = "--release" ]; then |
||||
# If --release has been specified, provide installed releases for completion |
||||
local releases=$(/bin/ls -d /var/www/pages/feed/rel-* 2>/dev/null | sed 's#/var/www/pages/feed/rel-##') |
||||
COMPREPLY=( $(compgen -W "${releases}" -- ${cur}) ) |
||||
else |
||||
# --release is only completion option for query |
||||
COMPREPLY=( $(compgen -W "--release" -- ${cur}) ) |
||||
fi |
||||
return 0 |
||||
;; |
||||
query-hosts|install-local) |
||||
# These subcommands have no options/arguments |
||||
COMPREPLY=( $(compgen -- ${cur}) ) |
||||
return 0 |
||||
;; |
||||
query-dependencies) |
||||
# Query the list of known patches |
||||
local patches=$(sw-patch completion patches 2>/dev/null) |
||||
COMPREPLY=( $(compgen -W "--recursive ${patches}" -- ${cur}) ) |
||||
return 0 |
||||
;; |
||||
commit) |
||||
if [ "${prev}" = "--release" ]; then |
||||
# If --release has been specified, provide installed releases for completion |
||||
local releases=$(/bin/ls -d /var/www/pages/feed/rel-* 2>/dev/null | sed 's#/var/www/pages/feed/rel-##') |
||||
COMPREPLY=( $(compgen -W "${releases}" -- ${cur}) ) |
||||
else |
||||
# Query the list of known patches |
||||
local patches=$(sw-patch completion patches 2>/dev/null) |
||||
COMPREPLY=( $(compgen -W "--all --dry-run --release ${patches}" -- ${cur}) ) |
||||
fi |
||||
return 0 |
||||
;; |
||||
report-app-dependencies) |
||||
if [ "${prev}" = "${subcommand}" ]; then |
||||