StarlingX open source release updates

Signed-off-by: Dean Troyer <dtroyer@gmail.com>
This commit is contained in:
Dean Troyer 2018-05-30 16:15:53 -07:00
parent 9e5dab7ca9
commit 527e098821
201 changed files with 18085 additions and 0 deletions

7
CONTRIBUTORS.wrs Normal file
View File

@ -0,0 +1,7 @@
The following contributors from Wind River have developed the seed code in this
repository. We look forward to community collaboration and contributions for
additional features, enhancements and refactoring.
Contributors:
=============
Don Penney <Don.Penney@windriver.com>

202
LICENSE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

5
README.rst Normal file
View File

@ -0,0 +1,5 @@
==========
stx-update
==========
StarlingX Software Management

202
cgcs-patch/LICENSE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

13
cgcs-patch/PKG-INFO Normal file
View File

@ -0,0 +1,13 @@
Metadata-Version: 1.1
Name: cgcs-patch
Version: 1.0
Summary: TIS Platform Patching
Home-page:
Author: Windriver
Author-email: info@windriver.com
License: Apache-2.0
Description: TIS Platform Patching
Platform: UNKNOWN

15
cgcs-patch/bin/make_patch Executable file
View File

@ -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())

15
cgcs-patch/bin/modify_patch Executable file
View File

@ -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())

View File

@ -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_compute()
{
[[ $nodetype == "compute" ]]
}
function is_storage()
{
[[ $nodetype == "storage" ]]
}
function is_cpe()
{
[[ $nodetype == "controller" && $subfunction =~ compute ]]
}
function is_locked()
{
test -f /var/run/.node_locked
}

View File

@ -0,0 +1,2 @@
d /run/patching 0700 root root -

16
cgcs-patch/bin/patch_build Executable file
View File

@ -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())

View File

@ -0,0 +1,29 @@
#!/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

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,5 @@
{
"admin": "role:admin or role:administrator",
"admin_api": "is_admin:True",
"default": "rule:admin_api"
}

15
cgcs-patch/bin/query_patch Executable file
View File

@ -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())

175
cgcs-patch/bin/rpm-audit Executable file
View File

@ -0,0 +1,175 @@
#!/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 .py 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
echo $psum $pname | sha256sum --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

View File

@ -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

182
cgcs-patch/bin/setup_patch_repo Executable file
View File

@ -0,0 +1,182 @@
#!/usr/bin/env python
"""
Copyright (c) 2018 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, 0755)
os.mkdir(datadir, 0755)
os.mkdir(committed_dir, 0755)
os.mkdir(pkgdir, 0755)
# 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:
print "%s is for release %s, not %s" % (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())

16
cgcs-patch/bin/sw-patch Executable file
View File

@ -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_client import main
if __name__ == "__main__":
main()

16
cgcs-patch/bin/sw-patch-agent Executable file
View File

@ -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_agent import main
if __name__ == "__main__":
main()

View File

@ -0,0 +1,97 @@
#!/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

View File

@ -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

View File

@ -0,0 +1,16 @@
[Unit]
Description=TIS Patching Agent
After=syslog.target network.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

View File

@ -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_controller import main
if __name__ == "__main__":
main()

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,16 @@
[Unit]
Description=TIS Patching Controller Daemon
After=syslog.target network.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

View File

@ -0,0 +1,104 @@
#!/bin/bash
#
# Copyright (c) 2014 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
# CGCS Patching Controller setup
# chkconfig: 345 20 24
# description: CGCS Patching Controller init script
. /usr/bin/tsconfig
NAME=$(basename $0)
REPO_ID=updates
REPO_ROOT=/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

View File

@ -0,0 +1,14 @@
[Unit]
Description=TIS Patching Controller
After=syslog.service network.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

View File

@ -0,0 +1,147 @@
#!/bin/bash
#
# Copyright (c) 2014-2015 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
# CGCS Patching
# chkconfig: 345 20 23
# description: CGCS Patching init script
NAME=$(basename $0)
. /usr/bin/tsconfig
. /etc/platform/platform.conf
logfile=/var/log/patching.log
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
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"
/sbin/reboot
fi
}
function check_install_uuid()
{
# Check whether our installed load matches the active controller
CONTROLLER_UUID=`curl -sf http://controller/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
# 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
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
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
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
LOG_TO_FILE "***** Finished patch operation *****"
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
esac
exit 0

View File

@ -0,0 +1,124 @@
#
# 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
"
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|remove|delete|show|what-requires)
# Query the list of known patches
local patches=$(sw-patch completion patches 2>/dev/null)
COMPREPLY=( $(compgen -W "${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 /www/pages/feed/rel-* 2>/dev/null | sed 's#/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 /www/pages/feed/rel-* 2>/dev/null | sed 's#/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
;;
*)
;;
esac
fi
# Provide subcommands for completion
COMPREPLY=($(compgen -W "${subcommands}" -- ${cur}))
return 0
}
# Bind the above function to the sw-patch CLI
complete -F _swpatch -o filenames sw-patch

View File

@ -0,0 +1,16 @@
[Unit]
Description=TIS Patching
After=syslog.target network.target
Before=sw-patch-agent.service
[Service]
Type=oneshot
User=root
ExecStart=/etc/init.d/sw-patch start
RemainAfterExit=yes
StandardOutput=syslog+console
StandardError=syslog+console
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,135 @@
#!/bin/bash
#
# Copyright (c) 2018 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
function show_usage()
{
cat >&2 <<EOF
$(basename $0): -r <release>
This tool will extract required packages to support upgrade-start
Options:
-r <release> : Release ID for target release.
EOF
exit 1
}
. /etc/build.info
if [ -z "${SW_VERSION}" ]; then
logger -t $0 "Unable to identify running release ID"
exit 1
fi
declare TGT_RELEASE=
while getopts "r:h" opt; do
case $opt in
r)
TGT_RELEASE=$OPTARG
;;
h)
show_usage
;;
*)
logger -t $0 "Unsupported option"
show_usage
;;
esac
done
if [ -z "${TGT_RELEASE}" ]; then
logger -t $0 "You must specify the target release."
exit 1
fi
if [ "${TGT_RELEASE}" = "${SW_VERSION}" ]; then
logger -t $0 "Target release cannot be running release."
exit 1
fi
declare TGT_BASE_REPO=/www/pages/feed/rel-${TGT_RELEASE}
declare TGT_PATCHES_REPO=/www/pages/updates/rel-${TGT_RELEASE}
if [ ! -d ${TGT_BASE_REPO} ]; then
logger -t $0 "Target release ${TGT_RELEASE} is not installed"
exit 1
fi
declare TGT_PATCHES_REPO_OPT=""
if [ -d ${TGT_PATCHES_REPO} ]; then
TGT_PATCHES_REPO_OPT="--repofrompath updates,${TGT_PATCHES_REPO}"
fi
declare WORKDIR=
function cleanup() {
if [ -n "${WORKDIR}" -a -d "${WORKDIR}" ]; then
rm -rf ${WORKDIR}
fi
}
trap cleanup EXIT
function extract_pkg() {
local pkgname=$1
ORIG_PWD=$PWD
cd $WORKDIR
# Find the RPM
local pkgfile=$(repoquery --repofrompath base,${TGT_BASE_REPO} ${TGT_PATCHES_REPO_OPT} --location -q ${pkgname})
if [ -z "${pkgfile}" ]; then
logger -t $0 "Could not find ${pkgname}"
exit 1
fi
# Chop off the file: from the start of the file location
local rpmfile=${pkgfile/file://}
rpm2cpio ${rpmfile} | cpio -idm
if [ $? -ne 0 ]; then
logger -t $0 "Failed to extract $pkgname files from ${pkgfile/file://}"
exit 1
fi
cd ${ORIG_PWD}
}
# Extract files from pxe-network-installer
WORKDIR=$(mktemp -d --tmpdir=/scratch pkgextract_XXXX)
if [ -z "${WORKDIR}" -o ! -d "${WORKDIR}" ]; then
logger -t $0 "Failed to create workdir"
exit 1
fi
extract_pkg pxe-network-installer
rsync -ac ${WORKDIR}/usr/ /usr/ &&
rsync -ac ${WORKDIR}/pxeboot/rel-${TGT_RELEASE}/ /pxeboot/rel-${TGT_RELEASE}/ &&
rsync -c ${WORKDIR}/pxeboot/pxelinux.cfg.files/*-${TGT_RELEASE} /pxeboot/pxelinux.cfg.files/ &&
rsync -ac ${WORKDIR}/www/pages/feed/rel-${TGT_RELEASE}/ /www/pages/feed/rel-${TGT_RELEASE}/
if [ $? -ne 0 ]; then
logger -t $0 "rsync command failed, extracting pxe-network-installer"
exit 1
fi
rm -rf ${WORKDIR}
# Extract files from platform-kickstarts
WORKDIR=$(mktemp -d --tmpdir=/scratch pkgextract_XXXX)
if [ -z "${WORKDIR}" -o ! -d "${WORKDIR}" ]; then
logger -t $0 "Failed to create workdir"
exit 1
fi
extract_pkg platform-kickstarts
rsync -ac ${WORKDIR}/www/pages/feed/rel-${TGT_RELEASE}/ /www/pages/feed/rel-${TGT_RELEASE}/
if [ $? -ne 0 ]; then
logger -t $0 "rsync command failed, extracting platform-kickstarts"
exit 1
fi
rm -rf ${WORKDIR}
exit 0

101
cgcs-patch/centos/build_srpm Executable file
View File

@ -0,0 +1,101 @@
source "$SRC_BASE/build-tools/spec-utils"
if [ "x$DATA" == "x" ]; then
echo "ERROR: Environment variable 'DATA' not defined."
exit 1
fi
if [ ! -f "$DATA" ]; then
echo "ERROR: Couldn't find '$PWD/$DATA'"
exit 1
fi
unset TIS_PATCH_VER # Ensure there's nothing in the env already
source $DATA
if [ -z "$TIS_PATCH_VER" ]; then
echo "ERROR: TIS_PATCH_VER must be defined"
exit 1
fi
SRC_DIR="cgcs-patch"
EXTRA_DIR="bin"
VERSION=$(grep '^Version:' PKG-INFO | awk -F ': ' '{print $2}' | sed -e 's/^[[:space:]]*//')
TAR_NAME=$(grep '^Name:' PKG-INFO | awk -F ': ' '{print $2}' | sed -e 's/^[[:space:]]*//')
CUR_DIR=`pwd`
BUILD_DIR="$RPMBUILD_BASE"
mkdir -p $BUILD_DIR/SRPMS
TAR="$TAR_NAME-$VERSION.tar.gz"
TAR_PATH="$BUILD_DIR/SOURCES/$TAR"
# copy the LICENSE for rpm spec %license directive
cp $SRC_DIR/LICENSE $BUILD_DIR/SOURCES/
TAR_NEEDED=0
if [ -f $TAR_PATH ]; then
n=`find . -cnewer $TAR_PATH -and ! -path './.git*' \
-and ! -path './build/*' \
-and ! -path './.pc/*' \
-and ! -path './patches/*' \
-and ! -path "./$DISTRO/*" \
-and ! -path './pbr-*.egg/*' \
| wc -l`
if [ $n -gt 0 ]; then
TAR_NEEDED=1
fi
else
TAR_NEEDED=1
fi
if [ $TAR_NEEDED -gt 0 ]; then
tar czvf $TAR_PATH $SRC_DIR $EXTRA_DIR \
--exclude='cgcs-patch/cgcs_patch_id' \
--exclude='cgcs-patch/cgcs_make_patch' \
--exclude='.git*' \
--exclude='build' \
--exclude='.pc' \
--exclude='patches' \
--exclude="$DISTRO" \
--exclude='pbr-*.egg' \
--transform "s,^$SRC_DIR/LICENSE,LICENSE," \
--transform "s,^$SRC_DIR,$TAR_NAME-$VERSION,"
fi
for SPEC in `ls $BUILD_DIR/SPECS`; do
SPEC_PATH="$BUILD_DIR/SPECS/$SPEC"
RELEASE=`spec_find_tag Release "$SPEC_PATH" 2>> /dev/null`
if [ $? -ne 0 ]; then
echo "ERROR: 'Release' not found in '$SPEC_PATH'"
fi
NAME=`spec_find_tag Name "$SPEC_PATH" 2>> /dev/null`
if [ $? -ne 0 ]; then
echo "ERROR: 'Name' not found in '$SPEC_PATH'"
fi
SRPM="$NAME-$VERSION-$RELEASE.src.rpm"
SRPM_PATH="$BUILD_DIR/SRPMS/$SRPM"
BUILD_NEEDED=0
if [ -f $SRPM_PATH ]; then
n=`find . -cnewer $SRPM_PATH | wc -l`
if [ $n -gt 0 ]; then
BUILD_NEEDED=1
fi
else
BUILD_NEEDED=1
fi
if [ $BUILD_NEEDED -gt 0 ]; then
echo "SPEC file: $SPEC_PATH"
echo "SRPM build directory: $BUILD_DIR"
echo "TIS_PATCH_VER: $TIS_PATCH_VER"
sed -i -e "1 i%define tis_patch_ver $TIS_PATCH_VER" $SPEC_PATH
rpmbuild -bs $SPEC_PATH --define="%_topdir $BUILD_DIR" --define="_tis_dist .tis"
fi
done

View File

@ -0,0 +1 @@
TIS_PATCH_VER=25

View File

@ -0,0 +1,180 @@
Summary: TIS Platform Patching
Name: cgcs-patch
Version: 1.0
Release: %{tis_patch_ver}%{?_tis_dist}
License: Apache-2.0
Group: base
Packager: Wind River <info@windriver.com>
URL: unknown
Source0: %{name}-%{version}.tar.gz
Source1: LICENSE
BuildRequires: python-setuptools
BuildRequires: systemd-units
BuildRequires: systemd-devel
Requires: python-devel
Requires: /bin/bash
%description
TIS Platform Patching
%define pythonroot /usr/lib64/python2.7/site-packages
%define debug_package %{nil}
%prep
%setup
%build
%{__python} setup.py build
%install
%{__python} setup.py install --root=$RPM_BUILD_ROOT \
--install-lib=%{pythonroot} \
--prefix=/usr \
--install-data=/usr/share \
--single-version-externally-managed
install -m 755 -d %{buildroot}%{_sbindir}
install -m 755 -d %{buildroot}%{_sysconfdir}/bash_completion.d
install -m 755 -d %{buildroot}%{_sysconfdir}/goenabled.d
install -m 755 -d %{buildroot}%{_sysconfdir}/init.d
install -m 755 -d %{buildroot}%{_sysconfdir}/logrotate.d
install -m 755 -d %{buildroot}%{_sysconfdir}/patching
install -m 700 -d %{buildroot}%{_sysconfdir}/patching/patch-scripts
install -m 755 -d %{buildroot}%{_sysconfdir}/pmon.d
install -m 755 -d %{buildroot}%{_unitdir}
install -m 500 ${RPM_BUILD_DIR}/bin/sw-patch-agent \
%{buildroot}%{_sbindir}/sw-patch-agent
install -m 500 ${RPM_BUILD_DIR}/bin/sw-patch-controller-daemon \
%{buildroot}%{_sbindir}/sw-patch-controller-daemon
install -m 555 ${RPM_BUILD_DIR}/bin/sw-patch \
%{buildroot}%{_sbindir}/sw-patch
install -m 555 ${RPM_BUILD_DIR}/bin/rpm-audit \
%{buildroot}%{_sbindir}/rpm-audit
install -m 500 ${RPM_BUILD_DIR}/bin/sw-patch-controller-daemon-init.sh \
%{buildroot}%{_sysconfdir}/init.d/sw-patch-controller-daemon
install -m 500 ${RPM_BUILD_DIR}/bin/sw-patch-agent-init.sh \
%{buildroot}%{_sysconfdir}/init.d/sw-patch-agent
install -m 600 ${RPM_BUILD_DIR}/bin/patching.conf \
%{buildroot}%{_sysconfdir}/patching/patching.conf
install -m 644 ${RPM_BUILD_DIR}/bin/policy.json \
%{buildroot}%{_sysconfdir}/patching/policy.json
install -m 444 ${RPM_BUILD_DIR}/bin/pmon-sw-patch-controller-daemon.conf \
%{buildroot}%{_sysconfdir}/pmon.d/sw-patch-controller-daemon.conf
install -m 444 ${RPM_BUILD_DIR}/bin/pmon-sw-patch-agent.conf \
%{buildroot}%{_sysconfdir}/pmon.d/sw-patch-agent.conf
install -m 444 ${RPM_BUILD_DIR}/bin/*.service %{buildroot}%{_unitdir}
install -m 444 ${RPM_BUILD_DIR}/bin/sw-patch.completion %{buildroot}%{_sysconfdir}/bash_completion.d/sw-patch
install -m 400 ${RPM_BUILD_DIR}/bin/patch-functions \
%{buildroot}%{_sysconfdir}/patching/patch-functions
install -D -m 444 ${RPM_BUILD_DIR}/bin/patch-tmpdirs.conf \
%{buildroot}%{_tmpfilesdir}/patch-tmpdirs.conf
install -m 500 ${RPM_BUILD_DIR}/bin/run-patch-scripts \
%{buildroot}%{_sbindir}/run-patch-scripts
install -m 500 ${RPM_BUILD_DIR}/bin/sw-patch-controller-daemon-restart \
%{buildroot}%{_sbindir}/sw-patch-controller-daemon-restart
install -m 500 ${RPM_BUILD_DIR}/bin/sw-patch-agent-restart \
%{buildroot}%{_sbindir}/sw-patch-agent-restart
install -m 500 ${RPM_BUILD_DIR}/bin/sw-patch-init.sh \
%{buildroot}%{_sysconfdir}/init.d/sw-patch
install -m 500 ${RPM_BUILD_DIR}/bin/sw-patch-controller-init.sh \
%{buildroot}%{_sysconfdir}/init.d/sw-patch-controller
install -m 555 ${RPM_BUILD_DIR}/bin/patch_check_goenabled.sh \
%{buildroot}%{_sysconfdir}/goenabled.d/patch_check_goenabled.sh
install -m 444 ${RPM_BUILD_DIR}/bin/patching.logrotate \
%{buildroot}%{_sysconfdir}/logrotate.d/patching
install -m 500 ${RPM_BUILD_DIR}/bin/upgrade-start-pkg-extract \
%{buildroot}%{_sbindir}/upgrade-start-pkg-extract
%clean
rm -rf $RPM_BUILD_ROOT
%package -n cgcs-patch-controller
Summary: TIS Platform Patching
Group: base
Requires: /usr/bin/env
Requires: /bin/sh
Requires: requests-toolbelt
Requires: createrepo
Requires(post): /usr/bin/env
Requires(post): /bin/sh
%description -n cgcs-patch-controller
TIS Platform Patching
%post -n cgcs-patch-controller
/usr/bin/systemctl enable sw-patch-controller.service
/usr/bin/systemctl enable sw-patch-controller-daemon.service
%package -n cgcs-patch-agent
Summary: TIS Platform Patching
Group: base
Requires: /usr/bin/env
Requires: /bin/sh
Requires(post): /usr/bin/env
Requires(post): /bin/sh
%description -n cgcs-patch-agent
TIS Platform Patching
%post -n cgcs-patch-agent
/usr/bin/systemctl enable sw-patch-agent.service
%post
/usr/bin/systemctl enable sw-patch.service
%files
%license ../LICENSE
%defattr(-,root,root,-)
%{pythonroot}/cgcs_patch
%{pythonroot}/cgcs_patch-*.egg-info
%{_sbindir}/rpm-audit
%config(noreplace) %{_sysconfdir}/patching/policy.json
%config(noreplace) %{_sysconfdir}/patching/patching.conf
%dir %{_sysconfdir}/patching/patch-scripts
%{_sysconfdir}/patching/patch-functions
%{_tmpfilesdir}/patch-tmpdirs.conf
%{_sbindir}/run-patch-scripts
%{_sysconfdir}/init.d/sw-patch
%{_unitdir}/sw-patch.service
%{_sysconfdir}/goenabled.d/patch_check_goenabled.sh
%{_sysconfdir}/logrotate.d/patching
%files -n cgcs-patch-controller
%defattr(-,root,root,-)
%{_sbindir}/sw-patch
%{_sbindir}/sw-patch-controller-daemon
%{_sbindir}/sw-patch-controller-daemon-restart
%{_sbindir}/upgrade-start-pkg-extract
%{_sysconfdir}/pmon.d/sw-patch-controller-daemon.conf
%{_sysconfdir}/init.d/sw-patch-controller-daemon
%{_unitdir}/sw-patch-controller-daemon.service
%{_sysconfdir}/bash_completion.d/sw-patch
%{_sysconfdir}/init.d/sw-patch-controller
%{_unitdir}/sw-patch-controller.service
%files -n cgcs-patch-agent
%defattr(-,root,root,-)
%{_sbindir}/sw-patch-agent
%{_sbindir}/sw-patch-agent-restart
%{_sysconfdir}/pmon.d/sw-patch-agent.conf
%{_sysconfdir}/init.d/sw-patch-agent
%{_unitdir}/sw-patch-agent.service

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,226 @@
#!/bin/bash
# repos
UPSTREAM_CLONE="git://vxgit.wrs.com/git"
# UPSTREAM_PUSH="ssh://vxgit.wrs.com/git"
ROOT_REPO=cgcs-root
JENKINS_HOST=yow-cgts3-lx.wrs.com
# SOURCE_CONTEXT=TC_17.06
# SOURCE_JENKINS_BUILD=TC_17.06_Pull
# PREFIX=TC
# SW_VERSION=18.03
# JOB=patching
USAGE=0
if [ x"$1" = x ] ; then
echo "ERROR: You must specify a source context"
USAGE=1
fi
SOURCE_CONTEXT=$1
if [ x"$2" = x ] ; then
echo "ERROR: You must specify a source context"
USAGE=1
fi
SOURCE_JENKINS_BUILD=$2
if [ x"$3" = x ] ; then
echo "ERROR: You must specify a prefix for patch and patch branch names"
USAGE=1
fi
PREFIX=$3
if [ x"$4" = x ] ; then
echo "ERROR: You must specify a sw_version"
USAGE=1
fi
SW_VERSION=$4
if [ x"$5" = x ] ; then
echo "ERROR: You must specify a job directory"
USAGE=1
fi
JOB=$5
if [ $USAGE -ne 0 ] ; then
echo "USAGE: make_patching_branch <source_context> <jenkins_src_job> <prefix> <sw_version> <repo_dir>"
echo " e.g. make_patching_branch CGCS_DEV_0007 Secure_Src_Pull_CGCS_DEV_0007 CGCS 14.10 testpatch"
exit 1
fi
PATCH_BRANCH=$PREFIX"_"$SW_VERSION"_PATCHING"
PATCH_TAG0=v$PREFIX"_"$SW_VERSION"_PATCH_0000"
MY_LOCAL_DISK=/localdisk/designer/$USER/$JOB
MY_REPO=$MY_LOCAL_DISK
if [[ "$JOB" = /* ]]
then
MY_LOCAL_DISK=$JOB
MY_REPO=$JOB
fi
echo "PREFIX=$PREFIX"
echo "SW_VERSION=$SW_VERSION"
echo "JOB=$JOB"
echo "SOURCE_CONTEXT=$SOURCE_CONTEXT"
echo "MY_LOCAL_DISK=$MY_LOCAL_DISK"
echo "MY_REPO=$MY_REPO"
echo "SOURCE_JENKINS_BUILD=$SOURCE_JENKINS_BUILD"
echo "PATCH_BRANCH=$PATCH_BRANCH"
echo "UPSTREAM_CLONE=$UPSTREAM_CLONE"
mkdir -p $MY_LOCAL_DISK
if [ $? -ne 0 ] ; then
echo "ERROR: failed to create directory '$MY_LOCAL_DISK'"
exit 1
fi
cd $MY_LOCAL_DISK
if [ $? -ne 0 ] ; then
echo "ERROR: failed to change to directory '$MY_LOCAL_DISK'"
exit 1
fi
# Get latest tools
if [ ! -d bin ] ; then
echo "clone bin"
git clone git://git.wrs.com/git/bin
else
echo "pull bin"
cd bin
git pull
cd $MY_LOCAL_DISK
fi
export PATH=$MY_REPO/build-tools/branching:$PATH
# Create repo
mkdir -p $MY_REPO
if [ $? -ne 0 ] ; then
echo "ERROR: failed to create directory '$MY_REPO'"
exit 1
fi
chgrp cgts $MY_REPO
if [ $? -ne 0 ] ; then
echo "ERROR: failed chgrp '$MY_REPO'"
exit 1
fi
chmod 750 $MY_REPO
if [ $? -ne 0 ] ; then
echo "ERROR: failed to chmod '$MY_REPO'"
exit 1
fi
cd $MY_REPO
if [ $? -ne 0 ] ; then
echo "ERROR: failed to change to directory '$MY_LOCAL_DISK'"
exit 1
fi
if [ ! -d $ROOT_REPO ] ; then
WRGIT_ALL_ADDONS=1 wrgit clone $UPSTREAM_CLONE/$ROOT_REPO $ROOT_REPO $SOURCE_CONTEXT
if [ $? -ne 0 ] ; then
echo "ERROR: failed to clone from repo '$UPSTREAM_CLONE' with context '$SOURCE_CONTEXT'"
exit 1
fi
MY_REPO=$MY_REPO/$ROOT_REPO
else
MY_REPO=$MY_REPO/$ROOT_REPO
cd $MY_REPO
if [ $? -ne 0 ] ; then
echo "ERROR: failed to change to directory '$MY_REPO'"
exit 1
fi
wrgit checkout $SOURCE_CONTEXT
if [ $? -ne 0 ] ; then
echo "ERROR: wrgit checkout '$SOURCE_CONTEXT' failed"
exit 1
fi
wrgit pull
if [ $? -ne 0 ] ; then
echo "ERROR: wrgit pull failed"
exit 1
fi
fi
cd $MY_REPO
if [ $? -ne 0 ] ; then
echo "ERROR: failed to change to directory '$MY_REPO'"
exit 1
fi
git branch | grep $PATCH_BRANCH
if [ $? -ne 0 ] ; then
# Set context
CONTEXT_PATH="/localdisk/designer/jenkins/$SOURCE_JENKINS_BUILD/$ROOT_REPO/CONTEXT"
if [ -f $CONTEXT_PATH ]; then
cp $CONTEXT_PATH ../
else
CONTEXT_PROVIDER=$JENKINS_HOST:$CONTEXT_PATH
scp $CONTEXT_PROVIDER ../
if [ $? -ne 0 ] ; then
echo "ERROR: failed to obtain context from '$CONTEXT_PROVIDER'"
exit 1
fi
fi
source ../CONTEXT
if [ $? -ne 0 ] ; then
echo "ERROR: failed to set context"
exit 1
fi
git branch | grep $PATCH_BRANCH
if [ $? -ne 0 ] ; then
# create patching branch and tag
create_branches_and_tags.sh $PATCH_BRANCH .
if [ $? -ne 0 ] ; then
echo "ERROR: failed to create branch '$PATCH_BRANCH'"
exit 1
fi
push_branches_tags.sh $PATCH_BRANCH
if [ $? -ne 0 ] ; then
echo "ERROR: failed to push branch '$PATCH_BRANCH' to '$UPSTREAM_PUSH'"
exit 1
fi
fi
else
wrgit checkout $PATCH_BRANCH
if [ $? -ne 0 ] ; then
echo "ERROR: wrgit checkout '$PATCH_BRANCH' failed"
exit 1
fi
fi
git tag | grep $PATCH_TAG0
if [ $? -ne 0 ] ; then
create_tags.sh $PATCH_TAG0
if [ $? -ne 0 ] ; then
echo "ERROR: failed to create tag '$PATCH_TAG0'"
exit 1
fi
push_tags.sh $PATCH_TAG0
if [ $? -ne 0 ] ; then
echo "ERROR: failed to push branch '$PATCH_BRANCH' to '$UPSTREAM_PUSH'"
exit 1
fi
fi

View File

@ -0,0 +1,98 @@
#!/bin/bash
# Real upstream repo
# UPSTREAM="git://vxgit.wrs.com/git/cgcs-root"
# Testing upstream repo
UPSTREAM="git://vxgit.wrs.com/git/users/cgcs/cgcs-root"
JENKINS_HOST=yow-cgts4-lx.wrs.com
# TAG=GCGS_14.10_PATCH_0001
# JOB=playground
USAGE=0
if [ x"$1" = x ] ; then
echo "ERROR: You must specify a PATCH_ID"
USAGE=1
fi
PATCH_ID=$1
if [ x"$2" = x ] ; then
echo "ERROR: You must specify a job directory"
USAGE=1
fi
JOB=$2
if [ $USAGE -ne 0 ] ; then
echo "USAGE: make_patching_tag <patch_id> <repo_dir>"
echo "USAGE: make_patching_tag CGCS_14.10_PATCH_0001 testpatch"
exit 1
fi
TAG="v$PATCH_ID"
MY_LOCAL_DISK=/localdisk/designer/$USER/$JOB
MY_REPO=$MY_LOCAL_DISK
if [[ "$JOB" = /* ]]
then
MY_LOCAL_DISK=$JOB
MY_REPO=$JOB
fi
echo "TAG=$TAG"
echo "JOB=$JOB"
echo "MY_LOCAL_DISK=$MY_LOCAL_DISK"
echo "MY_REPO=$MY_REPO"
# Get latest tools
if [ ! -d bin ] ; then
echo "clone bin"
git clone git://git.wrs.com/git/bin
else
echo "pull bin"
cd bin
git pull
cd $MY_LOCAL_DISK
fi
export PATH=$MY_REPO/build-tools/branching:$PATH
MY_REPO=$MY_REPO/cgcs-root
cd $MY_REPO
if [ $? -ne 0 ] ; then
echo "ERROR: failed to change to directory '$MY_REPO'"
exit 1
fi
### wrgit pull
if [ $? -ne 0 ] ; then
echo "ERROR: wrgit pull failed"
exit 1
fi
# create patching tag
create_tags.sh $TAG .
if [ $? -ne 0 ] ; then
echo "ERROR: failed to create tag '$TAG'"
exit 1
fi
if [ "$USER" = "jenkins" ] ; then
sh /localdisk/designer/slittle1/proxy/push_tags.sh $TAG
if [ $? -ne 0 ] ; then
echo "ERROR: failed to push branch '$TAG' to '$UPSTREAM'"
exit 1
fi
else
push_tags.sh $TAG
if [ $? -ne 0 ] ; then
echo "ERROR: failed to push branch '$TAG' to '$UPSTREAM'"
exit 1
fi
fi

View File

@ -0,0 +1,181 @@
#!/bin/bash
USAGE=0
if [ x"$1" = x ] ; then
echo "ERROR: You must specify a prefix for patch and patch branch names"
USAGE=1
fi
PREFIX=$1
if [ x"$2" = x ] ; then
echo "ERROR: You must specify a sw_version"
USAGE=1
fi
SW_VERSION=$2
if [ x"$3" = x ] ; then
echo "ERROR: You must specify a job directory"
USAGE=1
fi
JOB=$3
PATCH_BRANCH=$PREFIX"_"$SW_VERSION"_PATCHING"
PATCH_ID0=$PREFIX"_"$SW_VERSION"_PATCH_0000"
PATCH_TAG0=v$PATCH_ID0
MY_LOCAL_DISK=/localdisk/designer/$USER/$JOB
MY_REPO=$MY_LOCAL_DISK/cgcs-root
MY_WORKSPACE=/localdisk/loadbuild/$USER/$JOB
if [[ "$JOB" = /* ]]
then
MY_LOCAL_DISK=$JOB
MY_REPO=$JOB/cgcs-root
MY_WORKSPACE=$JOB
fi
if [ x"$4" != x ] ; then
MY_WORKSPACE=$4
fi
if [ x"$5" != x ] ; then
MY_LOCAL_DISK=$(realpath $5/..)
fi
MY_REPO=$MY_LOCAL_DISK/cgcs-root
if [ $USAGE -ne 0 ] ; then
echo "USAGE: make_patching_workspace <prefix> <sw_version> <repo_dir> [<workspace> [<sscache> [<repo>]]]"
echo " e.g. make_patching_workspace TC 18.03 testpatch"
echo " e.g. make_patching_workspace TC 18.03 mypatch $MY_WORKSPACE $MY_REPO"
exit 1
fi
RPM_DB_DIR=$MY_WORKSPACE/export/patch_data
RPM_DB=$RPM_DB_DIR/$PATCH_ID0.rpm_db
echo "PREFIX=$PREFIX"
echo "SW_VERSION=$SW_VERSION"
echo "JOB=$JOB"
echo "MY_LOCAL_DISK=$MY_LOCAL_DISK"
echo "MY_REPO=$MY_REPO"
echo "MY_WORKSPACE=$MY_WORKSPACE"
echo "PATCH_BRANCH=$PATCH_BRANCH"
echo "PATCH_TAG0=$PATCH_TAG0"
echo "RPM_DB_DIR=$RPM_DB_DIR"
echo "RPM_DB=$RPM_DB"
cd $MY_LOCAL_DISK/bin
if [ $? -ne 0 ] ; then
echo "ERROR: failed to change to directory '$MY_LOCAL_DISK/bin'"
exit 1
fi
export PATH=`pwd`:$PATH
cd $MY_REPO
if [ $? -ne 0 ] ; then
echo "ERROR: failed to change to directory '$MY_REPO'"
exit 1
fi
# Make sure gits are free of changes that would prevent checkout or pull
for d in $(find . -type d -name .git | xargs --max-args=1 dirname)
do
(cd $d
echo $d
git clean -df
git reset --hard
git ls-files --others --exclude-standard | xargs --no-run-if-empty rm
if [ ! -f .subgits ]; then
if [ -f .gitignore ]; then
git ls-files --others --ignored --exclude-from=.gitignore | xargs --no-run-if-empty rm
fi
fi
)
done
wrgit checkout $PATCH_TAG0
if [ $? -ne 0 ] ; then
echo "ERROR: wrgit checkout '$PATCH_TAG0' failed"
exit 1
fi
# Create workspace
echo "01: mkdir $MY_WORKSPACE"
if [ ! -d $MY_WORKSPACE ] ; then
mkdir -p $MY_WORKSPACE
if [ $? -ne 0 ] ; then
echo "ERROR: failed to create directory '$MY_WORKSPACE'"
exit 1
fi
fi
echo "02: cd $MY_WORKSPACE"
cd $MY_WORKSPACE
if [ $? -ne 0 ] ; then
echo "ERROR: failed to change to directory '$MY_WORKSPACE'"
exit 1
fi
echo "03: build"
if [ ! -f export/bootimage.iso ] ; then
echo "04: build pkgs"
nice -n 20 ionice -c Idle build-pkgs
if [ $? -ne 0 ] ; then
echo "ERROR: build-pkgs failed"
exit 1
fi
echo "05: build iso"
nice -n 20 ionice -c Idle build-iso
if [ $? -ne 0 ] ; then
echo "ERROR: build-iso failed "
exit 1
fi
fi
echo "06: rpm db"
mkdir -p $RPM_DB_DIR
if [ $? -ne 0 ] ; then
echo "ERROR: failed to make directory '$RPM_DB_DIR'"
exit 1
fi
echo "" > $RPM_DB
if [ $? -ne 0 ] ; then
echo "ERROR: failed to write file '$RPM_DB'"
exit 1
fi
for build_type in std rt; do
RPM_ROOT_DIR=$MY_WORKSPACE/$build_type/rpmbuild/RPMS
echo "RPM_ROOT_DIR=$RPM_ROOT_DIR"
cd $RPM_ROOT_DIR
if [ $? -ne 0 ] ; then
echo "ERROR: failed to change to directory '$RPM_ROOT_DIR'"
exit 1
fi
for d in `find * -type d`; do
pushd $d > /dev/null
if [ $? -ne 0 ] ; then
echo "ERROR: failed to change to directory '$d'"
exit 1
fi
rpm -qp --queryformat "$d %{NAME} %{RELEASE}\n" *rpm >> $RPM_DB 2> /dev/null
if [ $? -ne 0 ] ; then
echo "ERROR: rpm query failed in directory '$d'"
exit 1
fi
popd > /dev/null
done
done
echo 'Build is complete'

View File

@ -0,0 +1,7 @@
"""
Copyright (c) 2014-2017 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""

View File

@ -0,0 +1,30 @@
"""
Copyright (c) 2014-2017 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
from oslo_config import cfg
API_SERVICE_OPTS = [
cfg.StrOpt('api_bind_ip',
default='127.0.0.1',
help='IP for the Patching controller API server to bind to',
),
cfg.IntOpt('api_port',
default=5487,
help='The port for the Patching controller API server',
),
cfg.IntOpt('api_limit_max',
default=1000,
help='the maximum number of items returned in a single '
'response from a collection resource'),
]
CONF = cfg.CONF
opt_group = cfg.OptGroup(name='api',
title='Options for the Patching controller api service')
CONF.register_group(opt_group)
CONF.register_opts(API_SERVICE_OPTS)

View File

@ -0,0 +1,45 @@
"""
Copyright (c) 2014-2017 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
#from oslo.config import cfg
import pecan
from cgcs_patch.api import config
#CONF = cfg.CONF
def get_pecan_config():
# Set up the pecan configuration
filename = config.__file__.replace('.pyc', '.py')
return pecan.configuration.conf_from_file(filename)
def setup_app(pecan_config=None):
if not pecan_config:
pecan_config = get_pecan_config()
pecan.configuration.set_config(dict(pecan_config), overwrite=True)
app = pecan.make_app(
pecan_config.app.root,
static_root=pecan_config.app.static_root,
template_path=pecan_config.app.template_path,
debug=False,
force_canonical=getattr(pecan_config.app, 'force_canonical', True),
guess_content_type_from_ext=False, # Avoid mime-type lookup
)
return app
class VersionSelectorApplication(object):
def __init__(self):
pc = get_pecan_config()
self.v1 = setup_app(pecan_config=pc)
def __call__(self, environ, start_response):
return self.v1(environ, start_response)

View File

@ -0,0 +1,23 @@
"""
Copyright (c) 2014-2017 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
# Server Specific Configurations
server = {
'port': '5487',
'host': '127.0.0.1'
}
# Pecan Application Configurations
app = {
'root': 'cgcs_patch.api.controllers.root.RootController',
'modules': ['cgcs_patch.authapi'],
'static_root': '%(confdir)s/public',
'template_path': '%(confdir)s/../templates',
'debug': False,
'enable_acl': True,
'acl_public_routes': [],
}

View File

@ -0,0 +1,7 @@
"""
Copyright (c) 2014-2017 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""

View File

@ -0,0 +1,266 @@
"""
Copyright (c) 2014-2017 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
import os
from pecan import expose, request
import cgi
import glob
from cgcs_patch.exceptions import PatchError
from cgcs_patch.patch_controller import pc
import logging
from cgcs_patch.patch_functions import LOG
class PatchAPIController(object):
@expose('json')
@expose('query.xml', content_type='application/xml')
def index(self):
return self.query()
@expose('json')
@expose('query.xml', content_type='application/xml')
def query(self, **kwargs):
try:
pd = pc.patch_query_cached(**kwargs)
except PatchError as e:
return dict(error="Error: %s" % e.message)
return dict(pd=pd)
@expose('json')
@expose('show.xml', content_type='application/xml')
def show(self, *args):
try:
result = pc.patch_query_specific_cached(list(args))
except PatchError as e:
return dict(error="Error: %s" % e.message)
return result
@expose('json')
@expose('query.xml', content_type='application/xml')
def apply(self, *args):
if pc.any_patch_host_installing():
return dict(error="Rejected: One or more nodes are installing patches.")
try:
result = pc.patch_apply_api(list(args))
except PatchError as e:
return dict(error="Error: %s" % e.message)
pc.patch_sync()
return result
@expose('json')
@expose('query.xml', content_type='application/xml')
def remove(self, *args, **kwargs):
if pc.any_patch_host_installing():
return dict(error="Rejected: One or more nodes are installing patches.")
try:
result = pc.patch_remove_api(list(args), **kwargs)
except PatchError as e:
return dict(error="Error: %s" % e.message)
pc.patch_sync()
return result
@expose('json')
@expose('query.xml', content_type='application/xml')
def delete(self, *args):
try:
result = pc.patch_delete_api(list(args))
except PatchError as e:
return dict(error="Error: %s" % e.message)
pc.patch_sync()
return result
@expose('json')
@expose('query.xml', content_type='application/xml')
def upload(self):
assert isinstance(request.POST['file'], cgi.FieldStorage)
fileitem = request.POST['file']
if not fileitem.filename:
return dict(error="Error: No file uploaded")
fn = '/scratch/' + os.path.basename(fileitem.filename)
# This technique cannot copy a very large file. It
# requires a lot of memory as all data from the
# source file is read into memory then written to
# the destination file one chunk
# open(fn, 'wb').write(fileitem.file.read())
# Copying file by chunks using OS system calls
# requires much less memory. A larger chunk
# size can be used to improve the copy speed;
# currently 64K chunk size is selected
dst = os.open(fn, os.O_WRONLY | os.O_CREAT)
src = fileitem.file.fileno()
size = 64*1024
n = size
while n >= size:
s = os.read(src, size)
n = os.write(dst, s)
os.close(dst)
try:
result = pc.patch_import_api([fn])
except PatchError as e:
os.remove(fn)
return dict(error=e.message)
os.remove(fn)
pc.patch_sync()
return result
@expose('json')
def upload_dir(self, **kwargs):
files = []
for key, path in kwargs.iteritems():
LOG.info("upload-dir: Retrieving patches from %s" % path)
for f in glob.glob(path + '/*.patch'):
if os.path.isfile(f):
files.append(f)
if len(files) == 0:
return dict(error="No patches found")
try:
result = pc.patch_import_api(sorted(files))
except PatchError as e:
return dict(error=e.message)
pc.patch_sync()
return result
@expose('json')
def init_release(self, *args):
if len(list(args)) == 0:
return dict(error="Release must be specified")
try:
result = pc.patch_init_release_api(list(args)[0])
except PatchError as e:
return dict(error=e.message)
pc.patch_sync()
return result
@expose('json')
def del_release(self, *args):
if len(list(args)) == 0:
return dict(error="Release must be specified")
try:
result = pc.patch_del_release_api(list(args)[0])
except PatchError as e:
return dict(error=e.message)
pc.patch_sync()
return result
@expose('json')
@expose('query_hosts.xml', content_type='application/xml')
def query_hosts(self, *args):
return dict(data=pc.query_host_cache())
@expose('json')
@expose('query.xml', content_type='application/xml')
def what_requires(self, *args):
try:
result = pc.patch_query_what_requires(list(args))
except PatchError as e:
return dict(error="Error: %s" % e.message)
return result
@expose('json')
@expose('query.xml', content_type='application/xml')
def host_install(self, *args):
return dict(error="Deprecated: Use host_install_async")
@expose('json')
@expose('query.xml', content_type='application/xml')
def host_install_async(self, *args):
if len(list(args)) == 0:
return dict(error="Host must be specified for install")
force = False
if len(list(args)) > 1 and 'force' in list(args)[1:]:
force = True
try:
result = pc.patch_host_install(list(args)[0], force, async=True)
except PatchError as e:
return dict(error="Error: %s" % e.message)
return result
@expose('json')
@expose('query.xml', content_type='application/xml')
def drop_host(self, *args):
if len(list(args)) == 0:
return dict(error="Host must be specified")
try:
result = pc.drop_host(list(args)[0])
except PatchError as e:
return dict(error="Error: %s" % e.message)
return result
@expose('json')
def query_dependencies(self, *args, **kwargs):
try:
result = pc.patch_query_dependencies(list(args), **kwargs)
except PatchError as e:
return dict(error=e.message)
return result
@expose('json')
def commit(self, *args):
try:
result = pc.patch_commit(list(args))
except PatchError as e:
return dict(error=e.message)
pc.patch_sync()
return result
@expose('json')
def commit_dry_run(self, *args):
try:
result = pc.patch_commit(list(args), dry_run=True)
except PatchError as e:
return dict(error=e.message)
return result
class RootController(object):
@expose()
@expose('json')
def index(self):
return "Titanium Cloud Patching API, Available versions: /v1"
patch = PatchAPIController()
v1 = PatchAPIController()

View File

@ -0,0 +1,27 @@
"""
Copyright (c) 2014-2017 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
from pecan import make_app
from cgcs_patch import model
def setup_app(config):
model.init_model()
return make_app(
config.app.root,
static_root=config.app.static_root,
template_path=config.app.template_path,
logging=getattr(config, 'logging', {}),
debug=getattr(config.app, 'debug', False),
force_canonical=getattr(config.app, 'force_canonical', True),
guess_content_type_from_ext=getattr(
config.app,
'guess_content_type_from_ext',
True),
)

View File

@ -0,0 +1,25 @@
# Copyright (c) 2013-2017 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from oslo_config import cfg
API_SERVICE_OPTS = [
cfg.StrOpt('auth_api_bind_ip',
default=None,
help='IP for the authenticated Patching API server to bind to'),
cfg.IntOpt('auth_api_port',
default=5491,
help='The port for the authenticated Patching API server'),
cfg.IntOpt('api_limit_max',
default=1000,
help='the maximum number of items returned in a single '
'response from a collection resource')
]
CONF = cfg.CONF
opt_group = cfg.OptGroup(name='api',
title='Options for the patch-api service')
CONF.register_group(opt_group)
CONF.register_opts(API_SERVICE_OPTS)

View File

@ -0,0 +1,28 @@
"""
Copyright (c) 2014-2017 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
"""Access Control Lists (ACL's) control access the API server."""
from cgcs_patch.authapi import auth_token
OPT_GROUP_NAME = 'keystone_authtoken'
def install(app, conf, public_routes):
"""Install ACL check on application.
:param app: A WSGI application.
:param conf: Settings. Must include OPT_GROUP_NAME section.
:param public_routes: The list of the routes which will be allowed
access without authentication.
:return: The same WSGI application with ACL installed.
"""
keystone_config = dict(conf.items(OPT_GROUP_NAME))
return auth_token.AuthTokenMiddleware(app,
conf=keystone_config,
public_api_routes=public_routes)

View File

@ -0,0 +1,77 @@
"""
Copyright (c) 2014-2017 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
from oslo_config import cfg
import pecan
from cgcs_patch.authapi import acl
from cgcs_patch.authapi import config
from cgcs_patch.authapi import hooks
from cgcs_patch.authapi import policy
import ConfigParser
auth_opts = [
cfg.StrOpt('auth_strategy',
default='keystone',
help='Method to use for auth: noauth or keystone.'),
]
CONF = cfg.CONF
CONF.register_opts(auth_opts)
def get_pecan_config():
# Set up the pecan configuration
filename = config.__file__.replace('.pyc', '.py')
return pecan.configuration.conf_from_file(filename)
def setup_app(pecan_config=None, extra_hooks=None):
config = ConfigParser.RawConfigParser()
config.read('/etc/patching/patching.conf')
policy.init()
app_hooks = [hooks.ConfigHook(),
hooks.ContextHook(pecan_config.app.acl_public_routes),
]
if extra_hooks:
app_hooks.extend(extra_hooks)
if not pecan_config:
pecan_config = get_pecan_config()
if pecan_config.app.enable_acl:
app_hooks.append(hooks.AdminAuthHook())
pecan.configuration.set_config(dict(pecan_config), overwrite=True)
app = pecan.make_app(
pecan_config.app.root,
static_root=pecan_config.app.static_root,
template_path=pecan_config.app.template_path,
debug=False,
force_canonical=getattr(pecan_config.app, 'force_canonical', True),
hooks=app_hooks,
guess_content_type_from_ext=False, # Avoid mime-type lookup
)
if pecan_config.app.enable_acl:
return acl.install(app, config, pecan_config.app.acl_public_routes)
return app
class VersionSelectorApplication(object):
def __init__(self):
pc = get_pecan_config()
pc.app.enable_acl = (CONF.auth_strategy == 'keystone')
self.v1 = setup_app(pecan_config=pc)
def __call__(self, environ, start_response):
return self.v1(environ, start_response)

View File

@ -0,0 +1,38 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# -*- encoding: utf-8 -*-
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from keystonemiddleware import auth_token
from sysinv.common import utils
class AuthTokenMiddleware(auth_token.AuthProtocol):
"""A wrapper on Keystone auth_token middleware.
Does not perform verification of authentication tokens
for public routes in the API.
"""
def __init__(self, app, conf, public_api_routes=[]):
self.public_api_routes = set(public_api_routes)
super(AuthTokenMiddleware, self).__init__(app, conf)
def __call__(self, env, start_response):
path = utils.safe_rstrip(env.get('PATH_INFO'), '/')
if path in self.public_api_routes:
return self.app(env, start_response)
return super(AuthTokenMiddleware, self).__call__(env, start_response)

View File

@ -0,0 +1,23 @@
"""
Copyright (c) 2014-2017 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
# Server Specific Configurations
server = {
'port': '5491',
'host': '0.0.0.0'
}
# Pecan Application Configurations
app = {
'root': 'cgcs_patch.api.controllers.root.RootController',
'modules': ['cgcs_patch.api'],
'static_root': '%(confdir)s/public',
'template_path': '%(confdir)s/../templates',
'debug': False,
'enable_acl': True,
'acl_public_routes': [],
}

View File

@ -0,0 +1,100 @@
# -*- encoding: utf-8 -*-
#
# Copyright © 2012 New Dream Network, LLC (DreamHost)
#
# Author: Doug Hellmann <doug.hellmann@dreamhost.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# Copyright (c) 2013-2017 Wind River Systems, Inc.
#
from oslo_config import cfg
from pecan import hooks
from sysinv.common import context
from sysinv.common import utils
from sysinv.openstack.common import policy
from webob import exc
class ConfigHook(hooks.PecanHook):
"""Attach the config object to the request so controllers can get to it."""
def before(self, state):
state.request.cfg = cfg.CONF
class ContextHook(hooks.PecanHook):
"""Configures a request context and attaches it to the request.
The following HTTP request headers are used:
X-User-Id or X-User:
Used for context.user_id.
X-Tenant-Id or X-Tenant:
Used for context.tenant.
X-Auth-Token:
Used for context.auth_token.
X-Roles:
Used for setting context.is_admin flag to either True or False.
The flag is set to True, if X-Roles contains either an administrator
or admin substring. Otherwise it is set to False.
"""
def __init__(self, public_api_routes):
self.public_api_routes = public_api_routes
super(ContextHook, self).__init__()
def before(self, state):
user_id = state.request.headers.get('X-User-Id')
user_id = state.request.headers.get('X-User', user_id)
tenant = state.request.headers.get('X-Tenant-Id')
tenant = state.request.headers.get('X-Tenant', tenant)
domain_id = state.request.headers.get('X-User-Domain-Id')
domain_name = state.request.headers.get('X-User-Domain-Name')
auth_token = state.request.headers.get('X-Auth-Token', None)
creds = {'roles': state.request.headers.get('X-Roles', '').split(',')}
is_admin = policy.check('admin', state.request.headers, creds)
path = utils.safe_rstrip(state.request.path, '/')
is_public_api = path in self.public_api_routes
state.request.context = context.RequestContext(
auth_token=auth_token,
user=user_id,
tenant=tenant,
domain_id=domain_id,
domain_name=domain_name,
is_admin=is_admin,
is_public_api=is_public_api)
class AdminAuthHook(hooks.PecanHook):
"""Verify that the user has admin rights.
Checks whether the request context is an admin context and
rejects the request otherwise.
"""
def before(self, state):
ctx = state.request.context
is_admin_api = policy.check('admin_api', {}, ctx.to_dict())
if not is_admin_api and not ctx.is_public_api:
raise exc.HTTPForbidden()

View File

@ -0,0 +1,117 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2011 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# Copyright (c) 2014-2017 Wind River Systems, Inc.
#
"""Policy Engine For Patching."""
import os.path
from sysinv.common import exception
from sysinv.common import utils
from sysinv.openstack.common import policy
_POLICY_PATH = None
_POLICY_CACHE = {}
def reset():
global _POLICY_PATH
global _POLICY_CACHE
_POLICY_PATH = None
_POLICY_CACHE = {}
policy.reset()
def init():
global _POLICY_PATH
global _POLICY_CACHE
if not _POLICY_PATH:
_POLICY_PATH = '/etc/patching/policy.json'
if not os.path.exists(_POLICY_PATH):
raise exception.ConfigNotFound(message='/etc/patching/policy.json')
utils.read_cached_file(_POLICY_PATH, _POLICY_CACHE,
reload_func=_set_rules)
def _set_rules(data):
default_rule = "rule:admin_api"
policy.set_rules(policy.Rules.load_json(data, default_rule))
def enforce(context, action, target, do_raise=True):
"""Verifies that the action is valid on the target in this context.
:param context: sysinv context
:param action: string representing the action to be checked
this should be colon separated for clarity.
i.e. ``compute:create_instance``,
``compute:attach_volume``,
``volume:attach_volume``
:param target: dictionary representing the object of the action
for object creation this should be a dictionary representing the
location of the object e.g. ``{'project_id': context.project_id}``
:param do_raise: if True (the default), raises PolicyNotAuthorized;
if False, returns False
:raises sysinv.exception.PolicyNotAuthorized: if verification fails
and do_raise is True.
:return: returns a non-False value (not necessarily "True") if
authorized, and the exact value False if not authorized and
do_raise is False.
"""
init()
credentials = context.to_dict()
# Add the exception arguments if asked to do a raise
extra = {}
if do_raise:
extra.update(exc=exception.PolicyNotAuthorized, action=action)
return policy.check(action, target, credentials, **extra)
def check_is_admin(context):
"""Whether or not role contains 'admin' role according to policy setting.
"""
init()
credentials = context.to_dict()
target = credentials
return policy.check('context_is_admin', target, credentials)
@policy.register('context_is_admin')
class IsAdminCheck(policy.Check):
"""An explicit check for is_admin."""
def __init__(self, kind, match):
"""Initialize the check."""
self.expected = (match.lower() == 'true')
super(IsAdminCheck, self).__init__(kind, str(self.expected))
def __call__(self, target, creds):
"""Determine whether is_admin matches the requested value."""
return creds['is_admin'] == self.expected

View File

@ -0,0 +1,166 @@
"""
Copyright (c) 2017-2017 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
import socket
import struct
import subprocess
import time
import cgcs_patch.utils as utils
import cgcs_patch.config as cfg
import cgcs_patch.constants as constants
from cgcs_patch.patch_functions import LOG
class PatchService:
def __init__(self):
self.sock_out = None
self.sock_in = None
self.service_type = None
self.port = None
self.mcast_addr = None
self.socket_lock = None
def update_config(self):
# Implemented in subclass
pass
def socket_lock_acquire(self):
pass
def socket_lock_release(self):
pass
def setup_socket_ipv4(self):
mgmt_ip = cfg.get_mgmt_ip()
if mgmt_ip is None:
# Don't setup socket unless we have a mgmt ip
return None
self.update_config()
interface_addr = socket.inet_pton(socket.AF_INET, mgmt_ip)
# Close sockets, if necessary
for s in [self.sock_out, self.sock_in]:
if s is not None:
s.close()
self.sock_out = socket.socket(socket.AF_INET,
socket.SOCK_DGRAM)
self.sock_in = socket.socket(socket.AF_INET,
socket.SOCK_DGRAM)
self.sock_out.setblocking(0)
self.sock_in.setblocking(0)
self.sock_out.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sock_in.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sock_out.bind((mgmt_ip, 0))
self.sock_in.bind(('', self.port))
# These options are for outgoing multicast messages
self.sock_out.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, interface_addr)
self.sock_out.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 1)
# Since only the controllers are sending to this address,
# we want the loopback so the local agent can receive it
self.sock_out.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 1)
# Register the multicast group
group = socket.inet_pton(socket.AF_INET, self.mcast_addr)
mreq = struct.pack('=4s4s', group, interface_addr)
self.sock_in.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
return self.sock_in
def setup_socket_ipv6(self):
mgmt_ip = cfg.get_mgmt_ip()
if mgmt_ip is None:
# Don't setup socket unless we have a mgmt ip
return None
self.update_config()
# Close sockets, if necessary
for s in [self.sock_out, self.sock_in]:
if s is not None:
s.close()
self.sock_out = socket.socket(socket.AF_INET6,
socket.SOCK_DGRAM)
self.sock_in = socket.socket(socket.AF_INET6,
socket.SOCK_DGRAM)
self.sock_out.setblocking(0)
self.sock_in.setblocking(0)
self.sock_out.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sock_in.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sock_out.bind((mgmt_ip, 0))
self.sock_in.bind(('', self.port))
# These options are for outgoing multicast messages
mgmt_ifindex = utils.if_nametoindex(cfg.get_mgmt_iface())
self.sock_out.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, mgmt_ifindex)
self.sock_out.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, 1)
# Since only the controllers are sending to this address,
# we want the loopback so the local agent can receive it
self.sock_out.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_LOOP, 1)
# Register the multicast group
if_index_packed = struct.pack('I', mgmt_ifindex)
group = socket.inet_pton(socket.AF_INET6, self.mcast_addr) + if_index_packed
self.sock_in.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, group)
return self.sock_in
def setup_socket(self):
self.socket_lock_acquire()
try:
sock_in = None
if utils.get_management_version() == constants.ADDRESS_VERSION_IPV6:
sock_in = self.setup_socket_ipv6()
else:
sock_in = self.setup_socket_ipv4()
self.socket_lock_release()
return sock_in
except:
LOG.exception("Failed to setup socket")
# Close sockets, if necessary
for s in [self.sock_out, self.sock_in]:
if s is not None:
s.close()
self.socket_lock_release()
return None
def audit_socket(self):
# Ensure multicast address is still allocated
cmd = "ip maddr show %s | awk 'BEGIN { ORS=\"\" }; {if ($2 == \"%s\") print $2}'" % \
(cfg.get_mgmt_iface(), self.mcast_addr)
try:
result = subprocess.check_output(cmd, shell=True)
if result == self.mcast_addr:
return
except subprocess.CalledProcessError as e:
LOG.error("Command output: %s" % e.output)
return
# Close the socket and set it up again
LOG.info("Detected missing multicast addr (%s). Reconfiguring" % self.mcast_addr)
while self.setup_socket() is None:
LOG.info("Unable to setup sockets. Waiting to retry")
time.sleep(5)
LOG.info("Multicast address reconfigured")

View File

@ -0,0 +1,51 @@
"""
Copyright (c) 2017 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
dev_certificate = b"""-----BEGIN CERTIFICATE-----
MIIDejCCAmKgAwIBAgICEAQwDQYJKoZIhvcNAQELBQAwQjELMAkGA1UEBhMCQ0Ex
EDAOBgNVBAgMB09udGFyaW8xITAfBgNVBAoMGFdpbmQgUml2ZXIgU3lzdGVtcywg
SW5jLjAeFw0xNzA4MTgxNDM3MjlaFw0yNzA4MTYxNDM3MjlaMEExCzAJBgNVBAYT
AkNBMRAwDgYDVQQIDAdPbnRhcmlvMSAwHgYDVQQKDBdXaW5kIFJpdmVyIFN5c3Rl
bXMsIEluYzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALcs0/Te6x69
lxQOxudrF+uSC5F9r5bKUnZNWUKHyXKlN4SzZgWGs+fb/DqXIm7piuoQ6GH7GEQd
BEN1j/bwp30LZlv0Ur+8jhCvEdqsIP3vUXfv7pv0bomVs0Q8ZRI/FYZhjxYlyFKr
gZFV9WPP8S9SwfClHjaYRUudvwvjHHnnnkZ9blVFbXU0Xe83A8fWd0HNqAU1TlmK
4CeSi4FI4aRKiXJnOvgv2UoJMI57rBIVKYRUH8uuFpPofOwjOM/Rd6r3Ir+4/CX6
+/NALOBIEN6M05ZzoiyiH8NHELknQBqzNs0cXObJWpaSinAOcBnPCc7DNRwgQzjR
SdcE9FG1+LcCAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3Bl
blNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFDRbal2KxU0hQyv4
MVnWrW96+aWoMB8GA1UdIwQYMBaAFJaLO1x8+jti7V6pLGbUyqpy0M36MA0GCSqG
SIb3DQEBCwUAA4IBAQBmcPFZzEoPtuMPCFvJ/0cmngp8yvCGxWz3JEDkdGYSCVGs
TG5e9DeltaHOk6yLvZSRY1so30GQnyB9q8v4DwEGVslKg8u9w/WEU81wl6Q2FZ5s
XRP6TASQ0Lbg9e4b3bnTITJJ8jT/zF29NaohgC2fg0UwVuldZLfa7FihJB4//OC1
UdNEcmdqTVRqN2oco1n3ZUWKXvG2AvGsoiqu+lsWX1MXacoFvJexSACLrUvOoXMW
i38Ofp7XMCAm3rM0cXv7Uc9WCrgnTWbEvDgjGfRAmcM9moWGoWX6E46Xkojpkfle
Ss6CHAMK42aZ/+MWQlZEzNK49PtomGMjn5SuoK8u
-----END CERTIFICATE-----"""
formal_certificate=b"""-----BEGIN CERTIFICATE-----
MIIDezCCAmOgAwIBAgICEAMwDQYJKoZIhvcNAQELBQAwQjELMAkGA1UEBhMCQ0Ex
EDAOBgNVBAgMB09udGFyaW8xITAfBgNVBAoMGFdpbmQgUml2ZXIgU3lzdGVtcywg
SW5jLjAeFw0xNzA4MTgxNDM1MTJaFw0yNzA4MTYxNDM1MTJaMEIxCzAJBgNVBAYT
AkNBMRAwDgYDVQQIDAdPbnRhcmlvMSEwHwYDVQQKDBhXaW5kIFJpdmVyIFN5c3Rl
bXMsIEluYy4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+0fS8ybg8
M37lW+lcR9LmQAR2zUJdbnl2L0fj3W/7W+PMm3mJWeQDTf19wf+qHHrgEkjxGp10
BSXWZYdPyCdOjAay/Ew1s/waFeAQZpf4vv/9D1Y/4sVkqct9ibo5NVgvVsjqKVnX
IVhyzHlhBSUqYhZlS/SOx8JcLQWSUMJoP2XR4Tv28xIXi0Fuyp8QBwUmSwmvfPy4
0yxzfON/b8kHld5aTY353KLXh/5YWsn1zRlOYfS1OuJk4LGjm6HvmZtxPNUZk4vI
NA24rH4FKkuxyM3x8aPi3LE4G6GSrJDuNi28xzOj864rlFoyLODy/mov1YMR/g4k
d3mG6UbRckPxAgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9w
ZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBTjyMN/AX07rEmB
6sz6pnyt/m+eSzAfBgNVHSMEGDAWgBSWiztcfPo7Yu1eqSxm1MqqctDN+jANBgkq
hkiG9w0BAQsFAAOCAQEASpyCu/adGTvNjyy/tV+sL/kaVEKLA7q36HUrzQkTjMPX
y8L8PVZoeWprkz7cvYTyHmVTPLBvFkGEFVn8LWi9fTTp/UrHnxw6fvb+V78mOypi
4A1aU9+dh3L6arpd4jZ4hDiLhEClesGCYVTVBdsrh3zSOc51nT4hosyBVpRd/VgQ
jhGJBBMEXASZceady4ajK5jnR3wF8oW/he4NYF97qh8WWKVsIYbwgLS0rT58q7qq
vpjPxMOahUdACkyPyt/XJICTlkanVD7KgG3oLWpc+3FWPHGr+F7mspPLZqUcEFDV
bGF+oDJ7p/tqHsNvPlRDVGqh0QdiAkKeS/SJC9jmAw==
-----END CERTIFICATE-----
"""

View File

@ -0,0 +1,126 @@
"""
Copyright (c) 2014-2017 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
import os
import ConfigParser
import StringIO
import subprocess
import logging
import socket
import cgcs_patch.utils as utils
import cgcs_patch.constants as constants
import tsconfig.tsconfig as tsc
controller_mcast_group = None
agent_mcast_group = None
controller_port = 0
agent_port = 0
api_port = 0
mgmt_if = None
nodetype = None
platform_conf_mtime = 0
patching_conf_mtime = 0
patching_conf = '/etc/patching/patching.conf'
def read_config():
global patching_conf_mtime
global patching_conf
if patching_conf_mtime == os.stat(patching_conf).st_mtime:
# The file has not changed since it was last read
return
defaults = {
'controller_mcast_group': "239.1.1.3",
'agent_mcast_group': "239.1.1.4",
'api_port': "5487",
'controller_port': "5488",
'agent_port': "5489",
}
global controller_mcast_group
global agent_mcast_group
global api_port
global controller_port
global agent_port
config = ConfigParser.SafeConfigParser(defaults)
config.read(patching_conf)
patching_conf_mtime = os.stat(patching_conf).st_mtime
controller_mcast_group = config.get('runtime',
'controller_multicast')
agent_mcast_group = config.get('runtime', 'agent_multicast')
api_port = config.getint('runtime', 'api_port')
controller_port = config.getint('runtime', 'controller_port')
agent_port = config.getint('runtime', 'agent_port')
# The platform.conf file has no section headers, which causes problems
# for ConfigParser. So we'll fake it out.
ini_str = '[platform_conf]\n' + open(tsc.PLATFORM_CONF_FILE, 'r').read()
ini_fp = StringIO.StringIO(ini_str)
config.readfp(ini_fp)
try:
value = config.get('platform_conf', 'nodetype')
global nodetype
nodetype = value
except ConfigParser.Error:
logging.exception("Failed to read nodetype from config")
return False
def get_mgmt_ip():
# Check if initial config is complete
if not os.path.exists('/etc/platform/.initial_config_complete'):
return None
mgmt_hostname = socket.gethostname()
return utils.gethostbyname(mgmt_hostname)
# Because the patching daemons are launched before manifests are
# applied, the content of some settings in platform.conf can change,
# such as the management interface. As such, we can't just directly
# use tsc.management_interface
#
def get_mgmt_iface():
# Check if initial config is complete
if not os.path.exists(constants.INITIAL_CONFIG_COMPLETE_FLAG):
return None
global mgmt_if
global platform_conf_mtime
if mgmt_if is not None and \
platform_conf_mtime == os.stat(tsc.PLATFORM_CONF_FILE).st_mtime:
# The platform.conf file hasn't been modified since we read it,
# so return the cached value.
return mgmt_if
config = ConfigParser.SafeConfigParser()
# The platform.conf file has no section headers, which causes problems
# for ConfigParser. So we'll fake it out.
ini_str = '[platform_conf]\n' + open(tsc.PLATFORM_CONF_FILE, 'r').read()
ini_fp = StringIO.StringIO(ini_str)
config.readfp(ini_fp)
try:
value = config.get('platform_conf', 'management_interface')
global nodetype
mgmt_if = value
platform_conf_mtime = os.stat(tsc.PLATFORM_CONF_FILE).st_mtime
except ConfigParser.Error:
logging.exception("Failed to read management_interface from config")
return None
return mgmt_if

View File

@ -0,0 +1,43 @@
"""
Copyright (c) 2015-2017 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
import os
try:
# The tsconfig module is only available at runtime
import tsconfig.tsconfig as tsc
INITIAL_CONFIG_COMPLETE_FLAG = os.path.join(
tsc.PLATFORM_CONF_PATH, ".initial_config_complete")
except:
pass
PATCH_AGENT_STATE_IDLE = "idle"
PATCH_AGENT_STATE_INSTALLING = "installing"
PATCH_AGENT_STATE_INSTALL_FAILED = "install-failed"
PATCH_AGENT_STATE_INSTALL_REJECTED = "install-rejected"
ADDRESS_VERSION_IPV4 = 4
ADDRESS_VERSION_IPV6 = 6
CONTROLLER_FLOATING_HOSTNAME = "controller"
AVAILABLE = 'Available'
APPLIED = 'Applied'
PARTIAL_APPLY = 'Partial-Apply'
PARTIAL_REMOVE = 'Partial-Remove'
COMMITTED = 'Committed'
UNKNOWN = 'n/a'
STATUS_OBSOLETE = 'OBS'
STATUS_RELEASED = 'REL'
STATUS_DEVELOPEMENT = 'DEV'
CLI_OPT_ALL = '--all'
CLI_OPT_DRY_RUN = '--dry-run'
CLI_OPT_RECURSIVE = '--recursive'
CLI_OPT_RELEASE = '--release'
ENABLE_DEV_CERTIFICATE_PATCH_IDENTIFIER = 'ENABLE_DEV_CERTIFICATE'

View File

@ -0,0 +1,45 @@
"""
Copyright (c) 2017 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
class PatchError(Exception):
"""Base class for patching exceptions."""
def __init__(self, message=None):
self.message = message
def __str__(self):
return self.message or ""
class MetadataFail(PatchError):
"""Metadata error."""
pass
class RpmFail(PatchError):
"""RPM error."""
pass
class RepoFail(PatchError):
"""Repo error."""
pass
class PatchFail(PatchError):
"""General patching error."""
pass
class PatchValidationFailure(PatchError):
"""Patch validation error."""
pass
class PatchMismatchFailure(PatchError):
"""Patch validation error."""
pass

View File

@ -0,0 +1,64 @@
"""
Copyright (c) 2014-2017 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
from cgcs_patch.patch_functions import LOG
PATCHMSG_UNKNOWN = 0
PATCHMSG_HELLO = 1
PATCHMSG_HELLO_ACK = 2
PATCHMSG_SYNC_REQ = 3
PATCHMSG_SYNC_COMPLETE = 4
PATCHMSG_HELLO_AGENT = 5
PATCHMSG_HELLO_AGENT_ACK = 6
PATCHMSG_QUERY_DETAILED = 7
PATCHMSG_QUERY_DETAILED_RESP = 8
PATCHMSG_AGENT_INSTALL_REQ = 9
PATCHMSG_AGENT_INSTALL_RESP = 10
PATCHMSG_DROP_HOST_REQ = 11
PATCHMSG_STR = {
PATCHMSG_UNKNOWN: "unknown",
PATCHMSG_HELLO: "hello",
PATCHMSG_HELLO_ACK: "hello-ack",
PATCHMSG_SYNC_REQ: "sync-req",
PATCHMSG_SYNC_COMPLETE: "sync-complete",
PATCHMSG_HELLO_AGENT: "hello-agent",
PATCHMSG_HELLO_AGENT_ACK: "hello-agent-ack",
PATCHMSG_QUERY_DETAILED: "query-detailed",
PATCHMSG_QUERY_DETAILED_RESP: "query-detailed-resp",
PATCHMSG_AGENT_INSTALL_REQ: "agent-install-req",
PATCHMSG_AGENT_INSTALL_RESP: "agent-install-resp",
PATCHMSG_DROP_HOST_REQ: "drop-host-req",
}
class PatchMessage(object):
def __init__(self, msgtype=PATCHMSG_UNKNOWN):
self.msgtype = msgtype
self.msgversion = 1
self.message = {}
def decode(self, data):
if 'msgtype' in data:
self.msgtype = data['msgtype']
if 'msgversion' in data:
self.msgversion = data['msgversion']
def encode(self):
self.message['msgtype'] = self.msgtype
self.message['msgversion'] = self.msgversion
def data(self):
return {'msgtype': self.msgtype}
def msgtype_str(self):
if self.msgtype in PATCHMSG_STR:
return PATCHMSG_STR[self.msgtype]
return "invalid-type"
def handle(self, sock, addr):
LOG.info("Unhandled message type: %s" % self.msgtype)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,66 @@
"""
Copyright (c) 2017 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
import os
from Crypto.Signature import PKCS1_PSS
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Util.asn1 import DerSequence
from binascii import a2b_base64
from cgcs_patch.patch_verify import read_RSA_key
# To save memory, read and hash 1M of files at a time
default_blocksize=1*1024*1024
# When we sign patches, look for private keys in the following paths
#
# The (currently hardcoded) path on the signing server will be replaced
# by the capability to specify filename from calling function.
private_key_files=['/signing/keys/formal-private-key.pem',
os.path.expandvars('$MY_REPO/build-tools/signing/dev-private-key.pem')
]
def sign_files(filenames, signature_file, private_key=None):
"""
Utility function for signing data in files.
:param filenames: A list of files containing the data to be signed
:param signature_file: The name of the file to which the signature will be
stored
:param private_key: If specified, sign with this private key. Otherwise,
the files in private_key_files will be searched for
and used, if found.
"""
# Hash the data across all files
blocksize=default_blocksize
data_hash = SHA256.new()
for filename in filenames:
with open(filename, 'rb') as infile:
data=infile.read(blocksize)
while len(data) > 0:
data_hash.update(data)
data=infile.read(blocksize)
# Find a private key to use, if not already provided
if private_key is None:
for filename in private_key_files:
# print 'Checking to see if ' + filename + ' exists\n'
if os.path.exists(filename):
# print 'Getting private key from ' + filename + '\n'
private_key = read_RSA_key(open(filename, 'rb').read())
assert (private_key is not None),"Could not find private signing key"
# Encrypt the hash (sign the data) with the key we find
signer = PKCS1_PSS.new(private_key)
signature = signer.sign(data_hash)
# Save it
with open(signature_file, 'wb') as outfile:
outfile.write(signature)

View File

@ -0,0 +1,147 @@
"""
Copyright (c) 2017 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
import os
import logging
from Crypto.Signature import PKCS1_v1_5
from Crypto.Signature import PKCS1_PSS
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Util.asn1 import DerSequence
from binascii import a2b_base64
from cgcs_patch.certificates import dev_certificate, formal_certificate
# To save memory, read and hash 1M of files at a time
default_blocksize=1*1024*1024
dev_certificate_marker='/etc/pki/wrs/dev_certificate_enable.bin'
LOG = logging.getLogger('main_logger')
def verify_hash(data_hash, signature_bytes, certificate_list):
"""
Checkes that a hash's signature can be validates against an approved
certificate
:param data_hash: A hash of the data to be validated
:param signature_bytes: A pre-generated signature (typically, the hash
encrypted with a private key)
:param certifcate_list: A list of approved certificates or public keys
which the signature is validated against
:return: True if the signature was validated against a certificate
"""
verified = False
for cert in certificate_list:
if verified:
break
pub_key = read_RSA_key(cert)
x = pub_key.exportKey()
# PSS is the recommended signature scheme, but some tools (like OpenSSL)
# use the older v1_5 scheme. We try to validate against both.
#
# We use PSS for patch validation, but use v1_5 for ISO validation
# since we want to generate detached sigs that a customer can validate
# OpenSSL
verifier = PKCS1_PSS.new(pub_key)
verified = verifier.verify(data_hash, signature_bytes)
if not verified:
verifier = PKCS1_v1_5.new(pub_key)
verified = verifier.verify(data_hash, signature_bytes)
return verified
def get_public_certificates():
"""
Builds a list of accepted certificates which can be used to validate
further things. This list may contain multiple certificates depending on
the configuration of the system (for instance, should we include the
developer certificate in the list).
:return: A list of certificates in PEM format
"""
cert_list = [formal_certificate]
# We enable the dev certificate based on the presence of a file. This file
# contains a hash of an arbitrary string ('Titanum patching') which has been
# encrypted with our formal private key. If the file is present (and valid)
# then we add the developer key to the approved certificates list
if os.path.exists(dev_certificate_marker):
with open(dev_certificate_marker) as infile:
signature = infile.read()
data_hash = SHA256.new()
data_hash.update('Titanium patching')
if verify_hash(data_hash, signature, cert_list):
cert_list.append(dev_certificate)
else:
msg = "Invalid data found in " + dev_certificate_marker
LOG.error(msg)
return cert_list
def read_RSA_key(key_data):
"""
Utility function for reading an RSA key half from encoded data
:param key_data: PEM data containing raw key or X.509 certificate
:return: An RSA key object
"""
try:
# Handle data that is just a raw key
key = RSA.importKey(key_data)
except ValueError:
# The RSA.importKey function cannot read X.509 certificates directly
# (depending on the version of the Crypto library). Instead, we
# may need to extract the key from the certificate before building
# the key object
#
# We need to strip the BEGIN and END lines from PEM first
x509lines = key_data.replace(' ','').split()
x509text = ''.join(x509lines[1:-1])
x509data = DerSequence()
x509data.decode(a2b_base64(x509text))
# X.509 contains a few parts. The first part (index 0) is the
# certificate itself, (TBS or "to be signed" cert) and the 7th field
# of that cert is subjectPublicKeyInfo, which can be imported.
# RFC3280
tbsCert = DerSequence()
tbsCert.decode(x509data[0])
# Initialize RSA key from the subjectPublicKeyInfo field
key = RSA.importKey(tbsCert[6])
return key
def verify_files(filenames, signature_file):
"""
Verify data files against a detached signature.
:param filenames: A list of files containing the data which was signed
:param public_key_file: A file containing the public key or certificate
corresponding to the key which signed the data
:param signature_file: The name of the file containing the signature
:return: True if the signature was verified, False otherwise
"""
# Hash the data across all files
blocksize=default_blocksize
data_hash = SHA256.new()
for filename in filenames:
with open(filename, 'rb') as infile:
data=infile.read(blocksize)
while len(data) > 0:
data_hash.update(data)
data=infile.read(blocksize)
# Get the signature
with open(signature_file, 'rb') as sig_file:
signature_bytes = sig_file.read()
# Verify the signature
certificate_list = get_public_certificates()
return verify_hash(data_hash, signature_bytes, certificate_list)

View File

@ -0,0 +1,92 @@
<br>
% if not pd is UNDEFINED and len(pd) > 0:
<table border="2" style="width:300px">
<tr>
<th>Patch ID</th>
<th>Patch Data</th>
</tr>
% for patch_id in sorted(pd.keys()):
${patchrow(patch_id)}
% endfor
</table>
% endif
% if not info is UNDEFINED and len(info) > 0:
<p>${info}</p>
% endif
% if not warning is UNDEFINED and len(warning) > 0:
<p>Warning:<br>${warning}</p>
% endif
% if not error is UNDEFINED and len(error) > 0:
<p>Error:<br>${error}</p>
% endif
<br><br>
<a href="/patch/query">Show all</a><br>
<a href="/patch/query?show=applied">Show applied</a><br>
<a href="/patch/query?show=available">Show available</a><br>
<a href="/patch/query_hosts">Query Hosts</a><br>
<br><br>
<form action="/patch/upload" method="POST" enctype="multipart/form-data">
<input type="file" name="file"/>
<button type="submit">Upload Patch</button>
</form>
<%def name="patchrow(patch_id)">
<%
p = pd[patch_id]
%>
<tr>
<td valign="top"><a href="/patch/show/${patch_id}">${patch_id}</a></td>
<td valign="top">
<table border="1" width=100%>
% if p["repostate"] != "":
<tr><td valign="top">Repo State:</td><td valign="top">${p["repostate"]}</td></tr>
% endif
% if p["patchstate"] != "":
<tr><td valign="top">Patch State:</td><td valign="top">${p["patchstate"]}</td></tr>
% endif
% if p["status"] != "":
<tr><td valign="top">Status:</td><td valign="top">${p["status"]}</td></tr>
% endif
% if p["unremovable" != ""]:
<tr><td valign="top">Unremovable:</td><td valign="top">${p["unremovable"]}</td></tr>
% endif
% if p["reboot_required" != ""]:
<tr><td valign="top">Reboot-Required:</td><td valign="top">${p["reboot_required"]}</td></tr>
% endif
% if p["summary"] != "":
<tr><td valign="top">Summary:</td><td valign="top">${p["summary"]}</td></tr>
% endif
% if p["description"] != "":
<tr><td valign="top">Description:</td><td valign="top">${p["description"]}</td></tr>
% endif
% if p["install_instructions"] != "":
<tr><td valign="top">Install Instructions:</td><td valign="top">${p["install_instructions"]}</td></tr>
% endif
% if p["warnings"] != "":
<tr><td valign="top">Warnings:</td><td valign="top">${p["warnings"]}</td></tr>
% endif
% if p["repostate"] == "Applied":
<tr>
<td valign="top">Actions:</td>
<td valign="top"><a href="/patch/remove/${patch_id}">Remove</a></td>
</tr>
% endif
% if p["repostate"] == "Available":
<tr>
<td valign="top">Actions:</td>
<td valign="top"><a href="/patch/apply/${patch_id}">Apply</a><br>
<a href="/patch/delete/${patch_id}">Delete</a></td>
</tr>
% endif
</table>
</td>
</tr>
</%def>

View File

@ -0,0 +1,95 @@
% if not pd is UNDEFINED:
<pd>
% if len(pd) > 0:
% for patch_id in sorted(pd.keys()):
${patchelem(patch_id)}
% endfor
% endif
</pd>
% endif
% if not info is UNDEFINED or not warning is UNDEFINED or not error is UNDEFINED:
<info>
% if not info is UNDEFINED and len(info) > 0:
${info}
% endif
</info>
<warning>
% if not warning is UNDEFINED and len(warning) > 0:
${warning}
% endif
</warning>
<error>
% if not error is UNDEFINED and len(error) > 0:
${error}
% endif
</error>
% endif
<%def name="patchelem(patch_id)">\
<%p = pd[patch_id] %>\
<patch>
<patch_id>
${patch_id}
</patch_id>
<status>
% if p["status"] != "":
${p["status"]}
% endif
</status>
<sw_version>
% if p["sw_version"] != "":
${p["sw_version"]}
% endif
</sw_version>
<repostate>
% if p["repostate"] != "":
${p["repostate"]}
% endif
</repostate>
<patchstate>
% if p["patchstate"] != "":
${p["patchstate"]}
% endif
</patchstate>
<status>
% if p["status"] != "":
${p["status"]}
% endif
</status>
<unremovable>
% if p["unremovable"] != "":
${p["unremovable"]}
% endif
</unremovable>
<reboot_required>
% if p["reboot_required"] != "":
${p["reboot_required"]}
% endif
</reboot_required>
<summary>
% if p["summary"] != "":
${p["summary"]}
% endif
</summary>
<description>
% if p["description"] != "":
${p["description"]}
% endif
</description>
<install_instructions>
% if p["install_instructions"] != "":
${p["install_instructions"]}
% endif
</install_instructions>
<warnings>
% if p["warnings"] != "":
${p["warnings"]}
% endif
</warnings>
<requires>
% if "requires" in p and len(p["requires"]) > 0:
% for req in sorted(p["requires"]):
<patch>${req}</patch>
% endfor
% endif
</requires>
</patch></%def>

View File

@ -0,0 +1,32 @@
<br>
<table border="2" style="width:300px">
<tr>
<th>Hostname</th>
<th>IP</th>
<th>Patch Patch?</th>
<th>Requires Reboot</th>
<th>Time since last ack</th>
</tr>
% for agent in data:
${agentrow(agent)}
% endfor
</table>
<br><br>
<a href="/patch/query">Show all</a><br>
<a href="/patch/query?show=applied">Show applied</a><br>
<a href="/patch/query?show=available">Show available</a><br>
<%def name="agentrow(agent)">
<tr>
<td>${agent["ip"]}</td>
<td>${agent["hostname"]}</td>
<td>${agent["patch_current"]}</td>
<td>${agent["requires_reboot"]}</td>
<td>${agent["secs_since_ack"]}</td>
</tr>
</%def>

View File

@ -0,0 +1,75 @@
% if not data is UNDEFINED and len(data) > 0:
<data>
% for host in data:
${hostelem(host)}
% endfor
</data>
% endif
<%def name="hostelem(host)">\
<%h = host %>\
<host>
<hostname>
% if h["hostname"] != "":
${h["hostname"]}
% endif
</hostname>
<requires_reboot>
% if h["requires_reboot"] != "":
${h["requires_reboot"]}
% endif
</requires_reboot>
<nodetype>
% if h["nodetype"] != "":
${h["nodetype"]}
% endif
</nodetype>
<ip>
% if h["ip"] != "":
${h["ip"]}
% endif
</ip>
<missing_pkgs>
% if "missing_pkgs" in h and len(h["missing_pkgs"]) > 0:
% for pkg in sorted(h["missing_pkgs"]):
<pkg>${pkg}</pkg>
% endfor
% endif
</missing_pkgs>
<installed>
% if "installed" in h and len(h["installed"]) > 0:
% for pkg in sorted(h["installed"]):
<pkg>
<name>${pkg}</name>
<pkgname>${h["installed"][pkg]}</pkgname>
</pkg>
% endfor
% endif
</installed>
<to_remove>
% if "to_remove" in h and len(h["to_remove"]) > 0:
% for pkg in sorted(h["to_remove"]):
<pkg>${pkg}</pkg>
% endfor
% endif
</to_remove>
<secs_since_ack>
% if h["secs_since_ack"] != "":
${h["secs_since_ack"]}
% endif
</secs_since_ack>
<patch_failed>
% if h["patch_failed"] != "":
${h["patch_failed"]}
% endif
</patch_failed>
<stale_details>
% if h["stale_details"] != "":
${h["stale_details"]}
% endif
</stale_details>
<patch_current>
% if h["patch_current"] != "":
${h["patch_current"]}
% endif
</patch_current>
</host></%def>

View File

@ -0,0 +1,83 @@
<br>
% if not metadata is UNDEFINED and len(metadata) > 0:
% for patch_id in sorted(metadata.keys()):
${showpatch(patch_id)}
% endfor
% endif
% if not info is UNDEFINED and len(info) > 0:
<p>${info}</p>
% endif
% if not warning is UNDEFINED and len(warning) > 0:
<p>Warning:<br>${warning}</p>
% endif
% if not error is UNDEFINED and len(error) > 0:
<p>Error:<br>${error}</p>
% endif
<br><br>
<a href="/patch/query">Show all</a><br>
<a href="/patch/query?show=applied">Show applied</a><br>
<a href="/patch/query?show=available">Show available</a><br>
<a href="/patch/query_hosts">Query Hosts</a><br>
<br><br>
<form action="/patch/upload" method="POST" enctype="multipart/form-data">
<input type="file" name="file"/>
<button type="submit">Upload Patch</button>
</form>
<%def name="showpatch(patch_id)">
<%
p = metadata[patch_id]
%>
<h2>${patch_id}</h2>
<table border="2">
% if p["repostate"] != "":
<tr><td valign="top">Repo State:</td><td valign="top">${p["repostate"]}</td></tr>
% endif
% if p["patchstate"] != "":
<tr><td valign="top">Patch State:</td><td valign="top">${p["patchstate"]}</td></tr>
% endif
% if p["status"] != "":
<tr><td valign="top">Status:</td><td valign="top">${p["status"]}</td></tr>
% endif
% if p["unremovable"] != "":
<tr><td valign="top">Unremovable:</td><td valign="top">${p["unremovable"]}</td></tr>
% endif
% if p["reboot_required"] != "":
<tr><td valign="top">Reboot-Required:</td><td valign="top">${p["reboot_required"]}</td></tr>
% endif
% if p["summary"] != "":
<tr><td valign="top">Summary:</td><td valign="top">${p["summary"]}</td></tr>
% endif
% if p["description"] != "":
<tr><td valign="top">Description:</td><td valign="top">${p["description"]}</td></tr>
% endif
% if p["install_instructions"] != "":
<tr><td valign="top">Install Instructions:</td><td valign="top">${p["install_instructions"]}</td></tr>
% endif
% if p["warnings"] != "":
<tr><td valign="top">Warnings:</td><td valign="top">${p["warnings"]}</td></tr>
% endif
% if "requires" in p and len(p["requires"]) > 0:
<tr><td valign="top">Requires:</td><td valign="top">
% for req in sorted(p["requires"]):
${req}<br>
% endfor
</td></tr>
% endif
% if not contents is UNDEFINED and patch_id in contents:
<tr><td valign="top">Contents:</td><td valign="top">
% for pkg in sorted(contents[patch_id]):
${pkg}<br>
% endfor
</td></tr>
% endif
</table>
</%def>

View File

@ -0,0 +1,92 @@
<contents>
% if not contents is UNDEFINED and len(contents) > 0:
% for patch_id in sorted(contents.keys()):
<patch id=${patch_id}>
% for pkg in sorted(contents[patch_id]):
<pkg>${pkg}</pkg>
% endfor
</patch>
% endfor
% endif
</contents>
<error>
% if not error is UNDEFINED and len(error) > 0:
${error}
% endif
</error>
<metadata>
% if not metadata is UNDEFINED and len(metadata) > 0:
% for patch_id in sorted(metadata.keys()):
${showpatch(patch_id)}
% endfor
% endif
</metadata>
<%def name="showpatch(patch_id)">\
<% p = metadata[patch_id] %>\
<patch>
<patch_id>
${patch_id}
</patch_id>
<status>
% if p["status"] != "":
${p["status"]}
% endif
</status>
<unremovable>
% if p["unremovable"] != "":
${p["unremovable"]}
% endif
</unremovable>
<reboot_required>
% if p["reboot_required"] != "":
${p["reboot_required"]}
% endif
</reboot_required>
<sw_version>
% if p["sw_version"] != "":
${p["sw_version"]}
% endif
</sw_version>
<repostate>
% if p["repostate"] != "":
${p["repostate"]}
% endif
</repostate>
<patchstate>
% if p["patchstate"] != "":
${p["patchstate"]}
% endif
</patchstate>
<status>
% if p["status"] != "":
${p["status"]}
% endif
</status>
<summary>
% if p["summary"] != "":
${p["summary"]}
% endif
</summary>
<description>
% if p["description"] != "":
${p["description"]}
% endif
</description>
<install_instructions>
% if p["install_instructions"] != "":
${p["install_instructions"]}
% endif
</install_instructions>
<warnings>
% if p["warnings"] != "":
${p["warnings"]}
% endif
</warnings>
<requires>
% if "requires" in p and len(p["requires"]) > 0:
% for req in sorted(p["requires"]):
<patch>${req}</patch>
% endfor
% endif
</requires>
</patch></%def>

View File

@ -0,0 +1,74 @@
"""
Copyright (c) 2016-2017 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
from netaddr import IPAddress
import cgcs_patch.constants as constants
import socket
import ctypes
import ctypes.util
libc = ctypes.CDLL(ctypes.util.find_library('c'))
def if_nametoindex(name):
return libc.if_nametoindex(name)
def gethostbyname(hostname):
""" gethostbyname with IPv6 support """
try:
return socket.getaddrinfo(hostname, None)[0][4][0]
except:
return None
def get_management_version():
""" Determine whether management is IPv4 or IPv6 """
controller_ip_string = gethostbyname(constants.CONTROLLER_FLOATING_HOSTNAME)
if controller_ip_string:
controller_ip_address = IPAddress(controller_ip_string)
return controller_ip_address.version
else:
return constants.ADDRESS_VERSION_IPV4
def get_management_family():
ip_version = get_management_version()
if ip_version == constants.ADDRESS_VERSION_IPV6:
return socket.AF_INET6
else:
return socket.AF_INET
def get_versioned_address_all():
ip_version = get_management_version()
if ip_version == constants.ADDRESS_VERSION_IPV6:
return "::"
else:
return "0.0.0.0"
def ip_to_url(ip_address_string):
""" Add brackets if an IPv6 address """
try:
ip_address = IPAddress(ip_address_string)
if ip_address.version == constants.ADDRESS_VERSION_IPV6:
return "[%s]" % ip_address_string
else:
return ip_address_string
except:
return ip_address_string
def ip_to_versioned_localhost(ip_address_string):
""" Add brackets if an IPv6 address """
ip_address = IPAddress(ip_address_string)
if ip_address.version == constants.ADDRESS_VERSION_IPV6:
return "::1"
else:
return "localhost"

View File

@ -0,0 +1,34 @@
Intended to run on a single build server. Currently yow-cgts2-lx
# On other build servers
mkdir -p /localdisk/designer/jenkins/bin
cp patch_id_allocator_client.py /localdisk/designer/jenkins/bin
# On the intended server: e.g. yow-cgts2-lx
mkdir -p /localdisk/designer/jenkins/bin
cp *py /localdisk/designer/jenkins/bin/
mkdir -p /localdisk/designer/jenkins/patch_ids
sudo cp patch_id_allocator_server.conf /etc/init
sudo initctl reload-configuration
sudo start script
# Change to a different server
edit patch_id_allocator_client.py
change the line ...
server = 'yow-cgts2-lx.wrs.com'
# TODO:
Need to back up the /localdisk/designer/jenkins/patch_ids directory
# Quick test
Point your browser at this url:
http://yow-cgts2-lx:8888/get_patch_id
expected result is:
CGCS_None_PATCH_0000
on each reload of the page, the number increments:
CGCS_None_PATCH_0001
CGCS_None_PATCH_0002
....

View File

@ -0,0 +1,49 @@
#!/usr/bin/python
#
# Copyright (c) 2013-2014 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import posixfile
import string
import time
directory="/localdisk/designer/jenkins/patch_ids"
def get_unique_id(filename, digits=4):
counter = 1
path = "%s/%s" % (directory, filename)
try:
# open for update
file = posixfile.open(path, "r+")
file.lock("w|", digits)
counter = int(file.read(digits)) + 1
except IOError:
# create it
try:
file = posixfile.open(path, "w")
file.lock("w|", digits)
except IOError:
print "creation of file '%s' failed" % path
return -1
file.seek(0) # rewind
format = "%%0%dd" % digits
file.write(format % counter)
# Note: close releases lock
file.close()
return counter
def get_patch_id(version, prefix="CGCS", digits=4):
filename = "%s_%s_patchid" % (prefix, version)
id = get_unique_id(filename)
if id < 0:
return None
patch_id_format = "%%s_%%s_PATCH_%%0%dd" % digits
patch_id = patch_id_format % (prefix, version, id)
return patch_id

View File

@ -0,0 +1,57 @@
#!/usr/bin/env python
#
# Copyright (c) 2013-2014 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import urllib
import urllib2
import getopt
import sys
opts = ['sw_version=', 'prefix=' ]
server = 'yow-cgts2-lx.wrs.com'
port = 8888
def request_patch_id(sw_version="1.01", prefix="CGCS"):
raw_parms = {}
raw_parms['sw_version'] = sw_version
raw_parms['prefix'] = prefix
print "raw_parms = %s" % str(raw_parms)
url = "http://%s:%d/get_patch_id" % (server, port)
params = urllib.urlencode(raw_parms)
response = urllib2.urlopen(url, params).read()
return response
def main():
optlist, remainder = getopt.getopt(sys.argv[1:], '', opts)
sw_version = None
prefix = None
raw_parms = {}
print "optlist = %s" % str(optlist)
print "remainder = %s" % str(remainder)
for key, val in optlist:
print "key = %s, val = %s" % (key, val)
if key == '--sw_version':
sw_version = val
print "sw_version = %s" % sw_version
raw_parms['sw_version'] = sw_version
if key == '--prefix':
prefix = val
print "prefix = %s" % prefix
raw_parms['prefix'] = prefix
# response = request_patch_id(sw_version=sw_version, prefix=prefix)
response = request_patch_id(**raw_parms)
print "response = %s" % str(response)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,16 @@
# upstart script for patch_id_allocator_server
#
# Intallation
# sudo cp patch_id_allocator_server.conf /etc/init
# sudo initctl reload-configuration
# sudo start script
description "patch_id service"
author "Scott Little <scott.little@windriver.com>"
start on runlevel [234]
stop on runlevel [0156]
chdir /tmp
exec /localdisk/designer/jenkins/bin/patch_id_allocator_server.py
respawn

View File

@ -0,0 +1,43 @@
#!/usr/bin/env python
#
# Copyright (c) 2013-2014 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import os
import sys
import web
import patch_id_allocator as pida
port = 8888
urls = (
'/get_patch_id', 'get_patch_id',
)
class get_patch_id:
def GET(self):
data = web.input(sw_version=None, prefix="CGCS")
output = pida.get_patch_id(data.sw_version, data.prefix)
return output
def POST(self):
data = web.input(sw_version=None, prefix="CGCS")
output = pida.get_patch_id(data.sw_version, data.prefix)
return output
class MyApplication(web.application):
def run(self, port=8080, *middleware):
func = self.wsgifunc(*middleware)
return web.httpserver.runsimple(func, ('0.0.0.0', port))
def main():
app = MyApplication(urls, globals())
app.run(port=port)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,20 @@
#!/usr/bin/env python
#
# Copyright (c) 2013-2015 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import setuptools
setuptools.setup(name='cgcs_patch',
version='1.0',
description='CGCS Patch',
packages=setuptools.find_packages(),
package_data = {
# Include templates
'': ['templates/*'],
}
)

712
cgcs-patch/restart-info.html Executable file
View File

@ -0,0 +1,712 @@
<!DOCTYPE html>
<html>
<head>
<style>
table, th, td {
border: 1px solid black;
border-collapse: collapse;
padding: 10px;
}
</style>
</head>
<body>
<table>
<caption>
<font size=10>Process restart information</font>
</caption>
<thead>
<tr>
<th>Process/Service</th>
<th>Function</th>
<th>In service patchable</th>
<th>Managed by</th>
<th>Restart command</th>
<th>Patch Restart command</th>
<th>Restart dependency</th>
<th>Impact(if restarted while in operation)</th>
<th>Special handling required</th>
</tr>
</thead>
<tr>
<td><font color="blue">ceilometer-polling</font></td>
<td>Daemon that polls Openstack services and build meters</td>
<td>Y</td>
<td>PMON</td>
<td><b>/etc/init.d/openstack-ceilometer-polling restart</b></td>
<td><b></b></td>
<td>N</td>
<td>As batch_polled_samples is set to True, may lose some samples that
are in the pollsters memory if the process is restarted exactly
when they have just finished polling for samples and are about to
publish these samples to RabbitMQ. This is about 10 millisecond
window for cpu_source and 0.03 millisecond 1 second window for
meter related sources.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">ceilometer-agent-notification</font></td>
<td>Daemon that listens to notifications on message queue, converts
them to Events and Samples and applies pipeline actions
</td>
<td>Y</td>
<td>SM</td>
<td><b>sm-restart-safe service ceilometer-agent-notification</b><br>
which runs the following:<br><br>
/bin/sh /usr/lib/ocf/resource.d/openstack/ceilometer-agent-notification stop<br>
/bin/sh /usr/lib/ocf/resource.d/openstack/ceilometer-agent-notification start
</td>
<td><b></b></td>
<td>N</td>
<td>May lose some samples/events if the process is restarted while they
are being transformed or converted.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">ceilometer-collector</font></td>
<td>Daemon that gathers and records event and metering data created by
notification and polling agents
</td>
<td>Y</td>
<td>SM</td>
<td><b>sm-restart-safe service ceilometer-collector</b><br>
which runs the following:<br><br>
/bin/sh /usr/lib/ocf/resource.d/openstack/ceilometer-collector stop<br>
/bin/sh /usr/lib/ocf/resource.d/openstack/ceilometer-collector
start
</td>
<td><b></b></td>
<td>N</td>
<td>May lose some samples/events if the process is restarted while they
are being persisted in Postgres DB. This is a tiny window
especially with recent optimization work (no message signature
verification, one single call to create_sample stored proc).<br>
Note: Making sure that child processes and their database
connections are released when a parent process is stopped is part
of collector functionality. It is not specific to in-service
patching.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">ceilometer-api</font></td>
<td>Service to query and view data recorded by the collector</td>
<td>Y</td>
<td>SM</td>
<td><b>sm-restart-safe service ceilometer-api</b><br>
which runs the following:<br><br>
/bin/sh /usr/lib/ocf/resource.d/openstack/ceilometer-api stop<br>
/bin/sh /usr/lib/ocf/resource.d/openstack/ceilometer-api start
</td>
<td><b></b></td>
<td>N</td>
<td>While the service is restarted, horizon or CLI ceilometer request
will fail. Horizon request will be re-established automatically in
its next polling interval. CLI command needs to be re-issued.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">ceilometer-expirer-active</font></td>
<td>Cron job that purges expired samples and events as well as related
meter and resource data
</td>
<td>Y</td>
<td>CRON</td>
<td><b>N/A</b><br><br>
To run the expirer manually: /usr/bin/ceilometer-expirer-active
</td>
<td><b></b></td>
<td>N</td>
<td>There is no need to restart after patch. The change will take
effect next time the expirer cron job is run.<br>
Unless there are new features specifically planned for expirer,
this code is very stable.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">haproxy</font></td>
<td>A Proxy service that is responsible for forwarding external REST
API requests to Open Stack and Titanium Cloud services that listening on the
internal interfaces.
</td>
<td>Y</td>
<td>SM</td>
<td><b>sm-restart-safe service haproxy</b><br>
which runs the following:<br><br>
/bin/sh /etc/init.d/haproxy stop<br>
/bin/sh /etc/init.d/haproxy start
</td>
<td><b>/usr/local/sbin/patch-restart-haproxy</b></td>
<td>N</td>
<td>While the service is restarted, the outstanding requests will fail
and new requests will get connection error until the service is
re-enabled.
</td>
<td>Y</td>
</tr>
<tr>
<td><font color="blue">sm</font></td>
<td>Service management daemon</td>
<td>N</td>
<td>PMON</td>
<td><b>/etc/init.d/sm restart</b></td>
<td><b></b></td>
<td>N</td>
<td>Will cause all services disabled on the active controller before
the standby controller takes over the control.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">sm-api</font></td>
<td>Daemon that provides sm api</td>
<td>N</td>
<td>PMON</td>
<td><b></b></td>
<td><b></b></td>
<td>N</td>
<td></td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">sm-eru</font></td>
<td>Daemon that records sm eru data</td>
<td>N</td>
<td></td>
<td><b></b></td>
<td><b></b></td>
<td>N</td>
<td></td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">sm-watchdog</font></td>
<td>Daemon that loads NFS watchdog module to look for and handle
stalled NFS threads
</td>
<td>N</td>
<td></td>
<td><b></b></td>
<td><b></b></td>
<td>N</td>
<td></td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">neutron-server</font></td>
<td>Service that manages network functions</td>
<td>Y</td>
<td>SM</td>
<td><b>sm-restart-safe service neutron-server</b><br>
which runs the following:<br><br>
/bin/sh /usr/lib/ocf/resource.d/openstack/neutron-server stop<br>
/bin/sh /usr/lib/ocf/resource.d/openstack/neutron-server start
</td>
<td><b>/bin/neutron-restart neutron-server</b><br/>or<br/><b>/bin/neutron-restart --all</b></td>
<td>N</td>
<td>Will cause neutron services to not be available while restarting,
which will prevent instances from being created while it is down.
Could cause RPCs from computes to fail while it is restarting.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">neutron-dhcp-agent</font></td>
<td>Agent on compute node that manages DHCP servers for tenant
networks
</td>
<td>Y</td>
<td>PMON</td>
<td><b>/etc/init.d/neutron-dhcp-agent restart</b></td>
<td><b>/bin/neutron-restart neutron-dhcp-agent</b><br/>or<br/><b>/bin/neutron-restart --all</b></td>
<td>N</td>
<td>Will prevent binding new DHCP servers while it is down. Requires
special handling to kill metadata haproxy processes for networks.
</td>
<td>Y</td>
</tr>
<tr>
<td><font color="blue">neutron-metadata-agent</font></td>
<td>Agent on compute node serving metadata to nodes</td>
<td>Y</td>
<td>PMON</td>
<td><b>/etc/init.d/neutron-metadata-agent restart</b></td>
<td><b>/bin/neutron-restart neutron-metadata-agent</b><br/>or<br/><b>/bin/neutron-restart --all</b></td>
<td>N</td>
<td>Nodes will not be able to receive metadata while it is down</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">neutron-sriov-nic-agent</font></td>
<td>Agent on compute node responsible for setting SR-IOV port
information
</td>
<td>Y</td>
<td>PMON</td>
<td><b>/etc/init.d/neutron-sriov-nic-agent restart</b></td>
<td><b>/bin/neutron-restart neutron-sriov-nic-agent</b><br/>or<br/><b>/bin/neutron-restart --all</b></td>
<td>N</td>
<td>Will not be able to set device parameters while restarting</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">neutron-bgp-dragent</font></td>
<td>BGP dynamic routing agent on controller node
</td>
<td>Y</td>
<td>PMON</td>
<td><b>/etc/init.d/neutron-bgp-dragent restart</b></td>
<td><b>/bin/neutron-restart neutron-bgp-dragent</b><br/>or<br/><b>/bin/neutron-restart --all</b></td>
<td>N</td>
<td>Will not be able to set device parameters while restarting</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">keystone-all</font></td>
<td>Keystone provides services that support an identity, token
management, and service catalog and policy functionality.
</td>
<td>Y</td>
<td>SM</td>
<td><b>sm-restart-safe service keystone</b><br>
which runs the following:<br><br>
/bin/sh /usr/lib/ocf/resource.d/openstack/keystone stop<br>
/bin/sh /usr/lib/ocf/resource.d/openstack/keystone start
</td>
<td><b>/usr/local/sbin/patch-restart-processes keystone-all</b></td>
<td>N</td>
<td>While the service is restarted, the outstanding requests will fail
and new requests will get connection error until the service is
re-enabled.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">aodh-api</font></td>
<td>Aodh service that handles API requests for openstack alarming.</td>
<td>Y</td>
<td>SM</td>
<td><b>sm-restart-safe service aodh-api</b><br>
which runs the following:<br><br>
/bin/sh /usr/lib/ocf/resource.d/openstack/aodh-api stop<br>
/bin/sh /usr/lib/ocf/resource.d/openstack/aodh-api start
</td>
<td><b></b></td>
<td>N</td>
<td>While the service is restarted, the outstanding requests will fail
and new requests will get connection error until the service is
re-enabled.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">aodh-evaluator</font></td>
<td>Aodh service that performs threshold evaluation for openstack
alarming.
</td>
<td>Y</td>
<td>SM</td>
<td><b>sm-restart-safe service aodh-evaluator</b><br>
which runs the following:<br><br>
/bin/sh /usr/lib/ocf/resource.d/openstack/aodh-evaluator stop<br>
/bin/sh /usr/lib/ocf/resource.d/openstack/aodh-evaluator start
</td>
<td><b></b></td>
<td>N</td>
<td>While the service is restarted no openstack alarm threshold
evaluations will be executed until the service is re-enabled.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">aodh-listener</font></td>
<td>Aodh service that generates alarms based on events.</td>
<td>Y</td>
<td>SM</td>
<td><b>sm-restart-safe service aodh-listener</b><br>
which runs the following:<br><br>
/bin/sh /usr/lib/ocf/resource.d/openstack/aodh-listener stop<br>
/bin/sh /usr/lib/ocf/resource.d/openstack/aodh-listener start
</td>
<td><b></b></td>
<td>N</td>
<td>While the service is restarted no openstack event based alarms will
be generated until the service is re-enabled.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">aodh-notifier</font></td>
<td>Aodh service that sends openstack alarm notifications.</td>
<td>Y</td>
<td>SM</td>
<td><b>sm-restart-safe service aodh-notifier</b><br>
which runs the following:<br><br>
/bin/sh /usr/lib/ocf/resource.d/openstack/aodh-notifier stop<br>
/bin/sh /usr/lib/ocf/resource.d/openstack/aodh-notifier start
</td>
<td><b></b></td>
<td>N</td>
<td>While the service is restarted no openstack alarm threshold
notifications will be issued until the service is re-enabled.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">aodh-expirer-active</font></td>
<td>Cron job that purges expired openstack alarms</td>
<td>Y</td>
<td>CRON</td>
<td><b>N/A</b><br><br>
To run the expirer manually: /usr/bin/aodh-expirer-active
</td>
<td><b></b></td>
<td>N</td>
<td>There is no need to restart after patch. The change will take
effect next time the expirer cron job is run.<br>
Unless there are new features specifically planned for expirer,
this code is very stable.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">heat-api</font></td>
<td>Heat service for API requests for openstack orchestration.</td>
<td>Y</td>
<td>SM</td>
<td><b>sm-restart-safe service heat-api</b><br>
which runs the following:<br><br>
/bin/sh /usr/lib/ocf/resource.d/openstack/heat-api stop<br>
/bin/sh /usr/lib/ocf/resource.d/openstack/heat-api start
</td>
<td><b></b></td>
<td>N</td>
<td>While the service is restarted, horizon or CLI heat requests will
fail. Horizon will re-established automatically. CLI commands needs
to be re-issued. Heat stack updates in progress may fail.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">heat-api-cfn</font></td>
<td>Heat service for AWS Cloudformation API requests.</td>
<td>Y</td>
<td>SM</td>
<td><b>sm-restart-safe service heat-api-cfn</b><br>
which runs the following:<br><br>
/bin/sh /usr/lib/ocf/resource.d/openstack/heat-api-cfn stop<br>
/bin/sh /usr/lib/ocf/resource.d/openstack/heat-api-cfn start
</td>
<td><b></b></td>
<td>N</td>
<td>While the service is restarted, cloudformation API requests such as
autoscaling will not be processed.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">heat-api-cloudwatch</font></td>
<td>Heat service for AWS Cloudwatch metric collection.</td>
<td>Y</td>
<td>SM</td>
<td><b>sm-restart-safe service heat-api-cloudwatch</b><br>
which runs the following:<br><br>
/bin/sh /usr/lib/ocf/resource.d/openstack/heat-api-cloudwatch stop<br>
/bin/sh /usr/lib/ocf/resource.d/openstack/heat-api-cloudwatch start
</td>
<td><b></b></td>
<td>N</td>
<td>While the service is restarted, stats sent from VMs will not be
processed.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">heat-engine</font></td>
<td>Heat service for AWS Cloudwatch metric collection.</td>
<td>Y</td>
<td>SM</td>
<td><b>sm-restart-safe service heat-engine</b><br>
which runs the following:<br><br>
/bin/sh /usr/lib/ocf/resource.d/openstack/heat-engine stop<br>
/bin/sh /usr/lib/ocf/resource.d/openstack/heat-engine start
</td>
<td><b></b></td>
<td>N</td>
<td>While the service is restarted, openstrack heat orchestration
commands will not be processed. Stacks being created, deleted or
updated will fail and need to be re-initiated.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">heat-purge-deleted-active</font></td>
<td>Cron job that purges deleted openstack heat stacks from the
database
</td>
<td>Y</td>
<td>CRON</td>
<td><b>N/A</b><br><br>
To run the expirer manually: /usr/bin/heat-purge-deleted-active
</td>
<td><b></b></td>
<td>N</td>
<td>There is no need to restart after patch. The change will take
effect next time the cron job is run.<br>
Unless there are new features specifically planned, this code is
very stable.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">Glance</font></td>
<td>Glance imaging service - a single script restarts both glance-api
and glance-registry.
</td>
<td>Y</td>
<td>SM</td>
<td><b>/usr/bin/restart-glance</b><br>
</td>
<td><b></b></td>
<td>N</td>
<td>While the service is restarted, the outstanding requests will
continue and new requests will get connection error until the
service is re-enabled. The graceful restart takes more than 30
secs the process is killed. Timers are configurable from the
restart script
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">Cinder</font></td>
<td>Cinder volume service - a single script restarts cinder-volume,
cinder-scheduler and cinder-api.
</td>
<td>Y</td>
<td>SM</td>
<td><b>/usr/bin/restart-cinder</b><br>
</td>
<td><b></b></td>
<td>N</td>
<td>While the service is restarted, the outstanding requests will
continue and new requests will get connection error until the
service is re-enabled. Timers are configurable from the restart
script
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">Horizon</font></td>
<td>Horizon - Openstack Dashboard GUI used to control openstack and Titanium Cloud
operations
</td>
<td>Y</td>
<td>SM</td>
<td><b>sm-restart service horizon</b><br>
</td>
<td><b>/usr/bin/horizon-patching-restart</b></td>
<td>N</td>
<td>When horizon is restarted via the patch restart command all users
will be logged out. If they try to log back in before the server is
up again they will see an internal server error. It usually takes
less than a minute for the service to restart
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">IO-Monitor</font></td>
<td>Daemon which monitors cinder devices and raises alarms for excessive storage IO load.</td>
<td>Y</td>
<td>PMON</td>
<td><b>pmon-restart io-monitor-manager</b></td>
<td><b>/usr/local/sbin/patch-restart-processes io-monitor-manager</b></td>
<td>N</td>
<td>Generally there should be no impact. It is very unlikely for
the system to encounter an excessive storage IO load which will
only last a couple of seconds until the io-monitor process is restarted,
such that it will not be detected.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">vim</font></td>
<td>Virtual Infrastructure Manager</td>
<td>Y</td>
<td>SM</td>
<td><b>sm-restart-safe service vim</b></td>
<td><b></b></td>
<td>N</td>
<td>While the service is restarting, requests through the VIM API or
through the Nova API Proxy will fail. Any instance actions normally
triggered due to instance state changes (from nova) will not occur
until the process starts up again and audits the instance states.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">vim-api</font></td>
<td>Virtual Infrastructure Manager API</td>
<td>Y</td>
<td>SM</td>
<td><b>sm-restart-safe service vim-api</b></td>
<td><b></b></td>
<td>N</td>
<td>While the service is restarting, requests through the external VIM
API will fail.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">vim-webserver</font></td>
<td>Virtual Infrastructure Manager Web Server</td>
<td>Y</td>
<td>SM</td>
<td><b>sm-restart-safe service vim-webserver</b></td>
<td><b></b></td>
<td>N</td>
<td>No impact. This service is for design use only.</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">nova-api</font></td>
<td>Nova API Service</td>
<td>Y</td>
<td>SM</td>
<td><b>sm-restart-safe service nova-api</b><br></td>
<td><b>/bin/nova-restart</b></td>
<td>N</td>
<td>While the service is restarted, the outstanding requests will
fail and new requests will get connection error until the service
is re-enabled.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">nova-placement-api</font></td>
<td>Nova Placement API Service</td>
<td>Y</td>
<td>SM</td>
<td><b>sm-restart-safe service nova-placement-api</b><br></td>
<td><b>/bin/nova-restart</b></td>
<td>N</td>
<td>While the service is restarted, the outstanding requests will
fail and new requests will get connection error until the service
is re-enabled.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">nova-conductor</font></td>
<td>Nova Conductor Service</td>
<td>Y</td>
<td>SM</td>
<td><b>sm-restart-safe service nova-conductor</b><br></td>
<td><b>/bin/nova-restart</b></td>
<td>N</td>
<td>While the service is restarted, the outstanding requests will
fail and new requests will get connection error until the service
is re-enabled.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">nova-scheduler</font></td>
<td>Nova Scheduler Service</td>
<td>Y</td>
<td>SM</td>
<td><b>sm-restart-safe service nova-scheduler</b><br></td>
<td><b>/bin/nova-restart</b></td>
<td>N</td>
<td>While the service is restarted, the outstanding requests will
fail and new requests will get connection error until the service
is re-enabled.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">nova-console-auth</font></td>
<td>Nova Console Auth Service</td>
<td>Y</td>
<td>SM</td>
<td><b>sm-restart-safe service nova-console-auth</b><br></td>
<td><b>/bin/nova-restart</b></td>
<td>N</td>
<td>While the service is restarted, the outstanding requests will
fail and new requests will get connection error until the service
is re-enabled.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">nova-novnc</font></td>
<td>Nova VNC Service</td>
<td>Y</td>
<td>SM</td>
<td><b>sm-restart-safe service nova-novnc</b><br></td>
<td><b>/bin/nova-restart</b></td>
<td>N</td>
<td>While the service is restarted, the outstanding requests will
fail and new requests will get connection error until the service
is re-enabled.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">nova-compute</font></td>
<td>Nova Compute Service</td>
<td>Y</td>
<td>PMON</td>
<td><b>/usr/local/sbin/pmon-restart nova-compute</b><br></td>
<td><b>/bin/nova-restart</b></td>
<td>N</td>
<td>While the services is restarted, the outstanding requests will
fail and new requests will get connection error until the service
is re-enabled.
</td>
<td>N</td>
</tr>
<tr>
<td><font color="blue">ceph-osd & ceph-mon</font></td>
<td>Ceph OSD and Monitor processes</td>
<td>Y</td>
<td>PMON</td>
<td><b>/etc/ceph/ceph_pmon_wrapper.sh restart</b><br></td>
<td><b>/etc/ceph/ceph_pmon_wrapper.sh restart</b></td>
<td>N</td>
<td>Ceph processes on a node will restart (ceph-mon and ceph-osd). The restart
will take at most 30s and functionality should not be affected. Note that this
command should not be executed at the same time on storage-0 and any of the
controller nodes as we do not support restarting two of the three ceph-mon at
the same time.
</td>
<td>Restarting it on controller-0, controller-1 & storage-0,
at the same time with glance, cinder, nova, ceph-rest-api, sysinv or ceph-manager
on the active controller should be avoided due to ~30 secs delay to ceph APIs.
This delay happens when any of the ceph-mon changes state and may cause timeouts
when dependent services restart. Recommendations: (1) On the active controller,
restart Ceph before the other service; (2) updating ctrl-0,ctrl-1 & storage-0
at the same time should be avoided.</td>
</tr>
<tfoot>
<tr>
<th>Process/Service</th>
<th>Function</th>
<th>In service patchable</th>
<th>Managed by</th>
<th>Restart command</th>
<th>Patch Restart command</th>
<th>Restart dependency</th>
<th>Impact(if restarted while in operation)</th>
<th>Special handling required</th>
</tr>
</tfoot>
</table>
</body>
</html>

10
mwa-chilon.map Normal file
View File

@ -0,0 +1,10 @@
cgcs/middleware/sysinv/recipes-common|sysinv
cgcs/middleware/patching/recipes-common/cgcs-patch|cgcs-patch
cgcs/middleware/patching/recipes-common/patch-alarm|patch-alarm
cgcs/middleware/patching/recipes-common/patch-boot-args|patch-boot-args
cgcs/middleware/patching/recipes-common/patch-scripts|patch-scripts
cgcs/middleware/patching/recipes-common/requests-toolbelt|requests-toolbelt
cgcs/middleware/patching/recipes-common/smart-helper|smart-helper
cgcs/middleware/config/recipes-compute/computeconfig/computeconfig|computeconfig
cgcs/middleware/config/recipes-control/controllerconfig/controllerconfig|controllerconfig
cgcs/middleware/config/recipes-common/tsconfig|tsconfig

202
patch-alarm/LICENSE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

13
patch-alarm/PKG-INFO Normal file
View File

@ -0,0 +1,13 @@
Metadata-Version: 1.1
Name: patch-alarm
Version: 1.0
Summary: Patch alarm management
Home-page:
Author: Windriver
Author-email: info@windriver.com
License: Apache-2.0
Description: Patch alarm management
Platform: UNKNOWN

95
patch-alarm/centos/build_srpm Executable file
View File

@ -0,0 +1,95 @@
source "$SRC_BASE/build-tools/spec-utils"
if [ "x$DATA" == "x" ]; then
echo "ERROR: Environment variable 'DATA' not defined."
exit 1
fi
if [ ! -f "$DATA" ]; then
echo "ERROR: Couldn't find '$PWD/$DATA'"
exit 1
fi
unset TIS_PATCH_VER # Ensure there's nothing in the env already
source $DATA
if [ -z "$TIS_PATCH_VER" ]; then
echo "ERROR: TIS_PATCH_VER must be defined"
exit 1
fi
SRC_DIR="patch-alarm"
EXTRA_DIR="scripts"
VERSION=$(grep '^Version:' PKG-INFO | awk -F ': ' '{print $2}' | sed -e 's/^[[:space:]]*//')
TAR_NAME=$(grep '^Name:' PKG-INFO | awk -F ': ' '{print $2}' | sed -e 's/^[[:space:]]*//')
CUR_DIR=`pwd`
BUILD_DIR="$RPMBUILD_BASE"
mkdir -p $BUILD_DIR/SRPMS
TAR="$TAR_NAME-$VERSION.tar.gz"
TAR_PATH="$BUILD_DIR/SOURCES/$TAR"
TAR_NEEDED=0
if [ -f $TAR_PATH ]; then
n=`find . -cnewer $TAR_PATH -and ! -path './.git*' \
-and ! -path './build/*' \
-and ! -path './.pc/*' \
-and ! -path './patches/*' \
-and ! -path "./$DISTRO/*" \
-and ! -path './pbr-*.egg/*' \
| wc -l`
if [ $n -gt 0 ]; then
TAR_NEEDED=1
fi
else
TAR_NEEDED=1
fi
if [ $TAR_NEEDED -gt 0 ]; then
tar czvf $TAR_PATH $SRC_DIR $EXTRA_DIR \
--exclude='.git*' \
--exclude='build' \
--exclude='.pc' \
--exclude='patches' \
--exclude="$DISTRO" \
--exclude='pbr-*.egg' \
--transform "s,^$SRC_DIR,$TAR_NAME-$VERSION,"
fi
for SPEC in `ls $BUILD_DIR/SPECS`; do
SPEC_PATH="$BUILD_DIR/SPECS/$SPEC"
RELEASE=`spec_find_tag Release "$SPEC_PATH" 2>> /dev/null`
if [ $? -ne 0 ]; then
echo "ERROR: 'Release' not found in '$SPEC_PATH'"
fi
NAME=`spec_find_tag Name "$SPEC_PATH" 2>> /dev/null`
if [ $? -ne 0 ]; then
echo "ERROR: 'Name' not found in '$SPEC_PATH'"
fi
SRPM="$NAME-$VERSION-$RELEASE.src.rpm"
SRPM_PATH="$BUILD_DIR/SRPMS/$SRPM"
BUILD_NEEDED=0
if [ -f $SRPM_PATH ]; then
n=`find . -cnewer $SRPM_PATH | wc -l`
if [ $n -gt 0 ]; then
BUILD_NEEDED=1
fi
else
BUILD_NEEDED=1
fi
if [ $BUILD_NEEDED -gt 0 ]; then
echo "SPEC file: $SPEC_PATH"
echo "SRPM build directory: $BUILD_DIR"
echo "TIS_PATCH_VER: $TIS_PATCH_VER"
sed -i -e "1 i%define tis_patch_ver $TIS_PATCH_VER" $SPEC_PATH
rpmbuild -bs $SPEC_PATH --define="%_topdir $BUILD_DIR" --define="_tis_dist .tis"
fi
done

View File

@ -0,0 +1 @@
TIS_PATCH_VER=2

View File

@ -0,0 +1,55 @@
Summary: Patch alarm management
Name: patch-alarm
Version: 1.0
Release: %{tis_patch_ver}%{?_tis_dist}
License: Apache-2.0
Group: base
Packager: Wind River <info@windriver.com>
URL: unknown
Source0: %{name}-%{version}.tar.gz
%define debug_package %{nil}
BuildRequires: python-setuptools
Requires: python-devel
Requires: /bin/bash
%description
TIS Platform Patching
%define pythonroot /usr/lib64/python2.7/site-packages
%prep
%setup
%build
%{__python} setup.py build
%install
%{__python} setup.py install --root=$RPM_BUILD_ROOT \
--install-lib=%{pythonroot} \
--prefix=/usr \
--install-data=/usr/share \
--single-version-externally-managed
install -m 755 -d %{buildroot}%{_bindir}
install -m 755 -d %{buildroot}%{_sysconfdir}/init.d
install -m 700 ${RPM_BUILD_DIR}/scripts/bin/patch-alarm-manager \
%{buildroot}%{_bindir}/patch-alarm-manager
install -m 700 ${RPM_BUILD_DIR}/scripts/init.d/patch-alarm-manager \
%{buildroot}%{_sysconfdir}/init.d/patch-alarm-manager
%clean
rm -rf $RPM_BUILD_ROOT
%files
%defattr(-,root,root,-)
%doc LICENSE
%{pythonroot}/patch_alarm
%{pythonroot}/patch_alarm-*.egg-info
"%{_bindir}/patch-alarm-manager"
"%{_sysconfdir}/init.d/patch-alarm-manager"

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,6 @@
"""
Copyright (c) 2014 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""

View File

@ -0,0 +1,223 @@
"""
Copyright (c) 2014 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
###################
# IMPORTS
###################
import logging
import time
import requests
import json
import os
from daemon import runner
from fm_api import fm_api
from fm_api import constants as fm_constants
import cgcs_patch.config as cfg
from cgcs_patch.patch_functions import configure_logging
from cgcs_patch.constants import ENABLE_DEV_CERTIFICATE_PATCH_IDENTIFIER
###################
# CONSTANTS
###################
LOG_FILE = '/var/log/patch-alarms.log'
PID_FILE = '/var/run/patch-alarm-manager.pid'
#logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG)
###################
# METHODS
###################
def start_polling():
cfg.read_config()
patch_alarm_daemon = PatchAlarmDaemon()
alarm_runner = runner.DaemonRunner(patch_alarm_daemon)
alarm_runner.daemon_context.umask = 0o022
alarm_runner.do_action()
###################
# CLASSES
###################
class PatchAlarmDaemon():
""" Daemon process representation of
the patch monitoring program
"""
def __init__(self):
# Daemon-specific init
self.stdin_path = '/dev/null'
self.stdout_path = '/dev/null'
self.stderr_path = '/dev/null'
self.pidfile_path = PID_FILE
self.pidfile_timeout = 5
self.api_addr = "127.0.0.1:%d" % cfg.api_port
self.fm_api = fm_api.FaultAPIs()
def run(self):
configure_logging()
requests_logger = logging.getLogger('requests')
requests_logger.setLevel(logging.CRITICAL)
while True:
# start monitoring patch status
self.check_patch_alarms()
# run/poll every 1 min
time.sleep(60)
def check_patch_alarms(self):
self._handle_patch_alarms()
self._get_handle_failed_hosts()
def _handle_patch_alarms(self):
url = "http://%s/patch/query" % self.api_addr
try:
req = requests.get(url)
except:
return
entity_instance_id = "%s=%s" % (fm_constants.FM_ENTITY_TYPE_HOST, "controller")
raise_pip_alarm = False
raise_obs_alarm = False
raise_cert_alarm = False
if req.status_code == 200:
data = json.loads(req.text)
if 'pd' in data:
for patch_id, metadata in data['pd'].iteritems():
if 'patchstate' in metadata and \
(metadata['patchstate'] == 'Partial-Apply' or metadata['patchstate'] == 'Partial-Remove'):
raise_pip_alarm = True
if 'status' in metadata and \
(metadata['status'] == 'OBS' or metadata['status'] == 'Obsolete'):
raise_obs_alarm = True
# If there is a patch in the system (in any state) that is
# named some variation of "enable-dev-certificate", raise
# the 'developer certificate could allow for untrusted
# patches' alarm
if ENABLE_DEV_CERTIFICATE_PATCH_IDENTIFIER in patch_id:
raise_cert_alarm = True
pip_alarm = self.fm_api.get_fault(fm_constants.FM_ALARM_ID_PATCH_IN_PROGRESS,
entity_instance_id)
if raise_pip_alarm and pip_alarm is None:
logging.info("Raising patch-in-progress alarm")
fault = fm_api.Fault(alarm_id = fm_constants.FM_ALARM_ID_PATCH_IN_PROGRESS,
alarm_type = fm_constants.FM_ALARM_TYPE_5,
alarm_state = fm_constants.FM_ALARM_STATE_SET,
entity_type_id = fm_constants.FM_ENTITY_TYPE_HOST,
entity_instance_id = entity_instance_id,
severity = fm_constants.FM_ALARM_SEVERITY_MINOR,
reason_text = 'Patching operation in progress',
probable_cause = fm_constants.ALARM_PROBABLE_CAUSE_65,
proposed_repair_action = 'Complete reboots of affected hosts',
service_affecting = False)
self.fm_api.set_fault(fault)
elif not raise_pip_alarm and pip_alarm is not None:
logging.info("Clearing patch-in-progress alarm")
self.fm_api.clear_fault(fm_constants.FM_ALARM_ID_PATCH_IN_PROGRESS,
entity_instance_id)
obs_alarm = self.fm_api.get_fault(fm_constants.FM_ALARM_ID_PATCH_OBS_IN_SYSTEM,
entity_instance_id)
if raise_obs_alarm and obs_alarm is None:
logging.info("Raising obsolete-patch-in-system alarm")
fault = fm_api.Fault(alarm_id = fm_constants.FM_ALARM_ID_PATCH_OBS_IN_SYSTEM,
alarm_type = fm_constants.FM_ALARM_TYPE_5,
alarm_state = fm_constants.FM_ALARM_STATE_SET,
entity_type_id = fm_constants.FM_ENTITY_TYPE_HOST,
entity_instance_id = entity_instance_id,
severity = fm_constants.FM_ALARM_SEVERITY_WARNING,
reason_text = 'Obsolete patch in system',
probable_cause = fm_constants.ALARM_PROBABLE_CAUSE_65,
proposed_repair_action = 'Remove and delete obsolete patches',
service_affecting = False)
self.fm_api.set_fault(fault)
elif not raise_obs_alarm and obs_alarm is not None:
logging.info("Clearing obsolete-patch-in-system alarm")
self.fm_api.clear_fault(fm_constants.FM_ALARM_ID_PATCH_OBS_IN_SYSTEM,
entity_instance_id)
cert_alarm = self.fm_api.get_fault(fm_constants.FM_ALARM_ID_NONSTANDARD_CERT_PATCH,
entity_instance_id)
if raise_cert_alarm and cert_alarm is None:
logging.info("Raising developer-certificate-enabled alarm")
fault = fm_api.Fault(alarm_id = fm_constants.FM_ALARM_ID_NONSTANDARD_CERT_PATCH,
alarm_type = fm_constants.FM_ALARM_TYPE_9,
alarm_state = fm_constants.FM_ALARM_STATE_SET,
entity_type_id = fm_constants.FM_ENTITY_TYPE_HOST,
entity_instance_id = entity_instance_id,
severity = fm_constants.FM_ALARM_SEVERITY_CRITICAL,
reason_text = 'Developer patch certificate is enabled',
probable_cause = fm_constants.ALARM_PROBABLE_CAUSE_65,
proposed_repair_action = 'Reinstall system to disable certificate and remove untrusted patches',
suppression = False,
service_affecting = False)
self.fm_api.set_fault(fault)
def _get_handle_failed_hosts(self):
url = "http://%s/patch/query_hosts" % self.api_addr
try:
req = requests.get(url)
except:
return
entity_instance_id = "%s=%s" % (fm_constants.FM_ENTITY_TYPE_HOST, "controller")
failed_hosts = []
if req.status_code == 200:
data = json.loads(req.text)
if 'data' in data:
for host in data['data']:
if 'hostname' in host and 'patch_failed' in host and host['patch_failed']:
failed_hosts.append(host['hostname'])
# Query existing alarms
patch_failed_alarm = self.fm_api.get_fault(fm_constants.FM_ALARM_ID_PATCH_HOST_INSTALL_FAILED,
entity_instance_id)
if len(failed_hosts) > 0:
reason_text = "Patch installation failed on the following hosts: %s" % ", ".join(sorted(failed_hosts))
if patch_failed_alarm is None or reason_text != patch_failed_alarm.reason_text:
if patch_failed_alarm is None:
logging.info("Raising patch-host-install-failure alarm")
else:
logging.info("Updating patch-host-install-failure alarm")
fault = fm_api.Fault(alarm_id = fm_constants.FM_ALARM_ID_PATCH_HOST_INSTALL_FAILED,
alarm_type = fm_constants.FM_ALARM_TYPE_5,
alarm_state = fm_constants.FM_ALARM_STATE_SET,
entity_type_id = fm_constants.FM_ENTITY_TYPE_HOST,
entity_instance_id = entity_instance_id,
severity = fm_constants.FM_ALARM_SEVERITY_MAJOR,
reason_text = reason_text,
probable_cause = fm_constants.ALARM_PROBABLE_CAUSE_65,
proposed_repair_action = 'Undo patching operation',
service_affecting = False)
self.fm_api.set_fault(fault)
elif patch_failed_alarm is not None:
logging.info("Clearing patch-host-install-failure alarm")
self.fm_api.clear_fault(fm_constants.FM_ALARM_ID_PATCH_HOST_INSTALL_FAILED,
entity_instance_id)
return False

View File

@ -0,0 +1,19 @@
#!/usr/bin/env python
"""
Copyright (c) 2014 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
import setuptools
setuptools.setup(name='patch_alarm',
version='1.0.0',
description='CEPH alarm',
license='Apache-2.0',
packages=['patch_alarm'],
entry_points={
}
)

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python
"""
Copyright (c) 2014 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
import sys
try:
from patch_alarm import patch_alarm_manager
except EnvironmentError as e:
print >> sys.stderr, "Error importing patch_alarm_manager: ", str(e)
sys.exit(1)
patch_alarm_manager.start_polling()

View File

@ -0,0 +1,98 @@
#!/bin/sh
#
# Copyright (c) 2014 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
### BEGIN INIT INFO
# Provides: patch-alarm-manager
# Required-Start: $patch
# Required-Stop: $patch
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Daemon for polling patch status
# Description: Daemon for polling patch status
### END INIT INFO
DESC="patch-alarm-manager"
DAEMON="/usr/bin/patch-alarm-manager"
RUNDIR="/var/run"
PIDFILE=$RUNDIR/$DESC.pid
start()
{
if [ -e $PIDFILE ]; then
PIDDIR=/prod/$(cat $PIDFILE)
if [ -d ${PIDFILE} ]; then
echo "$DESC already running."
exit 0
else
echo "Removing stale PID file $PIDFILE"
rm -f $PIDFILE
fi
fi
echo -n "Starting $DESC..."
mkdir -p $RUNDIR
start-stop-daemon --start --quiet \
--pidfile ${PIDFILE} --exec ${DAEMON} start
#--make-pidfile
if [ $? -eq 0 ]; then
echo "done."
else
echo "failed."
exit 1
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
}
status()
{
pid=`cat $PIDFILE 2>/dev/null`
if [ -n "$pid" ]; then
if ps -p $pid &>/dev/null ; then
echo "$DESC is running"
exit 0
else
echo "$DESC is not running but has pid file"
exit 1
fi
fi
echo "$DESC is not running"
exit 3
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart|force-reload|reload)
stop
start
;;
status)
status
;;
*)
echo "Usage: $0 {start|stop|force-reload|restart|reload|status}"
exit 1
;;
esac
exit 0

202
patch-boot-args/LICENSE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,27 @@
Name: EXAMPLE_0001
Summary: TIS In-Service Patch Scripts Example
Version: 1.0
Release: %{tis_patch_ver}%{?_tis_dist}
License: Apache-2.0
Group: base
Packager: Wind River <info@windriver.com>
Source0: example-restart
%install
install -Dp -m 700 %{S:0} %{buildroot}%{_patch_scripts}/%{name}
%description
%{summary}
%files
%defattr(-,root,root,-)
%{_patch_scripts}/*
%post
cp -f %{_patch_scripts}/%{name} %{_runtime_patch_scripts}/
exit 0
%preun
cp -f %{_patch_scripts}/%{name} %{_runtime_patch_scripts}/
exit 0

View File

@ -0,0 +1,2 @@
COPY_LIST="scripts/*"
TIS_PATCH_VER=0

View File

@ -0,0 +1,128 @@
#!/bin/bash
#
# Copyright (c) 2016 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
#
# This script provides an example in-service patching restart
#
#
# The patching subsystem provides a patch-functions bash source file
# with useful function and variable definitions.
#
. /etc/patching/patch-functions
#
# We can now check to see what type of node we're on, if it's locked, etc,
# and act accordingly
#
#
# Declare an overall script return code
#
declare -i GLOBAL_RC=$PATCH_STATUS_OK
# NOTE: The following restart example code could be implemented in scripts
# owned by the various domains, with a single high-level call in the patch-script.
# This would be the preferred method, in fact, to ensure the patch-scripts
# themselves are simple and clean.
#
#
# First up, we'll handle restarting the sysinv-agent, which runs on all nodes
#
if [ ! -f $PATCH_FLAGDIR/sysinv-agent.restarted ]
then
# The sysinv-agent has not yet been restarted in this patch operation
systemctl status sysinv-agent.service
if [ $? -eq 0 ]
then
# The daemon is running, so restart it
loginfo "$0: Restarting sysinv-agent"
pmon-restart sysinv-agent
touch $PATCH_FLAGDIR/sysinv-agent.restarted
# Wait up to 15 seconds for service to recover
let -i UNTIL=$SECONDS+15
while [ $UNTIL -ge $SECONDS ]
do
# Check to make sure it's running
systemctl status sysinv-agent.service
if [ $? -eq 0 ]
then
break
fi
# Not running... Let's wait a couple of seconds and check again
sleep 2
done
systemctl status sysinv-agent.service
if [ $? -ne 0 ]
then
# Still not running! Clear the flag and mark the RC as failed
rm -f $PATCH_FLAGDIR/sysinv-agent.restarted
GLOBAL_RC=$PATCH_STATUS_FAILED
loginfo "$0: Failed to restart sysinv-agent"
fi
fi
fi
#
# Next, handle restarting horizon.
# TODO: There will be some SM enhancements coming to provide
# utilities we can use to facilitate in-service patching.
# For now, we'll do this a slightly uglier fashion
#
if is_controller
then
# Horizon only runs on the controller
if [ ! -f $PATCH_FLAGDIR/horizon.restarted ]
then
# Check SM to see if Horizon is running
sm-query service horizon | grep -q 'enabled-active'
if [ $? -eq 0 ]
then
loginfo "$0: Restarting horizon"
# Ask SM to restart Horizon
sm-restart service horizon
touch $PATCH_FLAGDIR/horizon.restarted
# Wait up to 30 seconds for service to recover
let -i UNTIL=$SECONDS+30
while [ $UNTIL -ge $SECONDS ]
do
# Check to see if it's running
sm-query service horizon | grep -q 'enabled-active'
if [ $? -eq 0 ]
then
break
fi
# Still not running? Let's wait 5 seconds and check again
sleep 5
done
sm-query service horizon | grep -q 'enabled-active'
if [ $? -ne 0 ]
then
# Still not running! Clear the flag and mark the RC as failed
loginfo "$0: Failed to restart horizon"
rm -f $PATCH_FLAGDIR/horizon.restarted
GLOBAL_RC=$PATCH_STATUS_FAILED
sm-query service horizon
fi
fi
fi
fi
#
# Exit the script with the overall return code
#
exit $GLOBAL_RC

Some files were not shown because too many files have changed in this diff Show More