diff --git a/CONTRIBUTORS.wrs b/CONTRIBUTORS.wrs new file mode 100644 index 00000000..ad61649f --- /dev/null +++ b/CONTRIBUTORS.wrs @@ -0,0 +1,10 @@ +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: +============= +Bart Wensley +Tao Liu +Eric Macdonald +Jack Ding diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/LICENSE @@ -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. diff --git a/README.rst b/README.rst new file mode 100644 index 00000000..33bbf739 --- /dev/null +++ b/README.rst @@ -0,0 +1,5 @@ +======= +stx-nfv +======= + +StarlingX NFVI Orchestration diff --git a/guest-agent/PKG-INFO b/guest-agent/PKG-INFO new file mode 100644 index 00000000..e9c7c6ea --- /dev/null +++ b/guest-agent/PKG-INFO @@ -0,0 +1,12 @@ +Metadata-Version: 1.1 +Name: guest-scale-agent +Version: 2.0 +Summary: Titanium Cloud agent and helper app to scale VMs up/down +Home-page: +Author: Windriver +Author-email: info@windriver.com +License: Apache-2.0 + +Description: Titanium Cloud agent and helper app to scale VMs up/down + +Platform: UNKNOWN diff --git a/guest-agent/centos/build_srpm.data b/guest-agent/centos/build_srpm.data new file mode 100644 index 00000000..55bf2d1c --- /dev/null +++ b/guest-agent/centos/build_srpm.data @@ -0,0 +1,2 @@ +SRC_DIR="guest-scale-agent-2.0" +TIS_PATCH_VER=6 diff --git a/guest-agent/centos/guest-scale-agent.spec b/guest-agent/centos/guest-scale-agent.spec new file mode 100644 index 00000000..a11a9cb6 --- /dev/null +++ b/guest-agent/centos/guest-scale-agent.spec @@ -0,0 +1,137 @@ +Summary: Titanium Cloud agent and helper app to scale VMs up/down +Name: guest-scale-agent +Version: 2.0 +%define patchlevel %{tis_patch_ver} +Release: %{tis_patch_ver}%{?_tis_dist} +License: Apache-2.0 +Group: base +Packager: Wind River +URL: unknown + +%define cgcs_sdk_deploy_dir /opt/deploy/cgcs_sdk +Source0: %{name}-%{version}.tar.gz + +BuildRequires: json-c-devel +BuildRequires: host-guest-comm-dev +BuildRequires: guest-host-comm-dev +BuildRequires: systemd-devel + +Requires: bash + +%description +Titanium Cloud agent and helper app to scale VMs up/down + +%package -n guest-scale-agent-dbg +Summary: Titanium Cloud agent and helper app to scale VMs up/down - Debugging files +Group: devel + +%description -n guest-scale-agent-dbg +Titanium Cloud agent and helper app to scale VMs up/down This package contains ELF +symbols and related sources for debugging purposes. + +%package -n guest-scale-agent-dev +Summary: Titanium Cloud agent and helper app to scale VMs up/down - Development files +Group: devel +Requires: guest-scale-agent = %{version}-%{release} + +%description -n guest-scale-agent-dev +Titanium Cloud agent and helper app to scale VMs up/down This package contains +symbolic links, header files, and related items necessary for software +development. + +%package -n guest-scale-helper +Summary: Titanium Cloud agent and helper app to scale VMs up/down +Group: base +Requires: rtld(GNU_HASH) + +%description -n guest-scale-helper +Titanium Cloud agent and helper app to scale VMs up/down + +%package -n %{name}-cgts-sdk +Summary: SDK files for Titanium Cloud agent and helper app to scale VMs up/down +Group: devel + +%description -n %{name}-cgts-sdk +SDK files for Titanium Cloud agent and helper app to scale VMs up/down +%prep +%setup + + +%build +VER=%{version} +MAJOR=`echo $VER | awk -F . '{print $1}'` +MINOR=`echo $VER | awk -F . '{print $2}'` +PATCH=%{patchlevel} +make all VER=${VER} MAJOR=${MAJOR} MINOR=${MINOR} PATCH=${PATCH} + +%global _buildsubdir %{_builddir}/%{name}-%{version} + +%install +install -d 750 -d %{buildroot}/usr/sbin +install -d 750 -d %{buildroot}%{_sysconfdir}/init.d + +install -m 750 -d %{buildroot}/usr +install -m 750 -d %{buildroot}/usr/src +install -m 750 -d %{buildroot}/usr/src/debug +install -m 750 -d %{buildroot}/usr/src/debug/%{name}-%{version} +install -d 750 -d %{buildroot}/usr/sbin/.debug + +install -m 750 %{_buildsubdir}/scripts/app_scale_helper %{buildroot}/usr/sbin/app_scale_helper +install -m 750 %{_buildsubdir}/scripts/offline_cpus %{buildroot}/usr/sbin/offline_cpus +install -m 750 %{_buildsubdir}/bin/guest_scale_helper %{buildroot}/usr/sbin/guest_scale_helper +install -m 750 %{_buildsubdir}/bin/guest_scale_agent %{buildroot}/usr/sbin/guest_scale_agent +install -m 750 %{_buildsubdir}/scripts/init_offline_cpus %{buildroot}/etc/init.d/offline_cpus +install -m 750 %{_buildsubdir}/bin/guest_scale_agent %{buildroot}/usr/sbin/.debug/guest_scale_agent + +install -d %{buildroot}%{_unitdir} +install -m 750 %{_buildsubdir}/scripts/offline-cpus.service %{buildroot}%{_unitdir}/offline-cpus.service +install -m 750 %{_buildsubdir}/scripts/guest-scale-agent.service %{buildroot}%{_unitdir}/guest-scale-agent.service + +# Deploy to the SDK deployment directory +install -d %{buildroot}%{cgcs_sdk_deploy_dir} +install -m 644 sdk/wrs-guest-scale-%{version}.%{patchlevel}.tgz %{buildroot}%{cgcs_sdk_deploy_dir}/wrs-guest-scale-%{version}.%{patchlevel}.tgz + + +%post +%systemd_post offline-cpus.service +%systemd_post guest-scale-agent.service +/usr/bin/systemctl enable offline-cpus.service >/dev/null 2>&1 +/usr/bin/systemctl enable guest-scale-agent.service >/dev/null 2>&1 + +%preun +%systemd_preun offline-cpus.service +%systemd_preun guest-scale-agent.service + +%postun +%systemd_postun guest-scale-agent.service +%systemd_postun offline-cpus.service + +%files +%defattr(-,root,root,-) + +/usr/sbin/guest_scale_agent +/usr/sbin/offline_cpus +/usr/sbin/app_scale_helper +/etc/init.d/offline_cpus +%{_unitdir}/offline-cpus.service +%{_unitdir}/guest-scale-agent.service + +%files -n guest-scale-agent-dbg +%defattr(-,root,root,-) + +/usr/src/debug/* +/usr/sbin/.debug/guest_scale_agent + +%files -n guest-scale-agent-dev +%defattr(-,root,root,-) + +/usr/sbin/.debug/guest_scale_agent + +%files -n guest-scale-helper +%defattr(-,root,root,-) + +/usr/sbin/guest_scale_helper + +%files -n %{name}-cgts-sdk +%{cgcs_sdk_deploy_dir}/wrs-guest-scale-%{version}.%{patchlevel}.tgz + diff --git a/guest-agent/guest-scale-agent-2.0/LICENSE b/guest-agent/guest-scale-agent-2.0/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/guest-agent/guest-scale-agent-2.0/LICENSE @@ -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. diff --git a/guest-agent/guest-scale-agent-2.0/Makefile b/guest-agent/guest-scale-agent-2.0/Makefile new file mode 100644 index 00000000..0a2cf192 --- /dev/null +++ b/guest-agent/guest-scale-agent-2.0/Makefile @@ -0,0 +1,110 @@ +# +# BSD LICENSE +# +# Copyright(c) 2013-2016, Wind River Systems, Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Wind River Systems nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Note: if you change either Makefile or Makefile.sdk you need to manually +# do a "make clean" and then "make". + +EXECS = guest_scale_helper guest_scale_agent + +CFLAGS= -g -Wall +ODIR=obj +BINDIR=bin + +BINEXECS=$(addprefix $(BINDIR)/, $(EXECS)) + +GUEST_SCALE_HELPER_SRCS = guest_scale_helper.c parser.c +GUEST_SCALE_HELPER_OBJ := $(patsubst %,$(ODIR)/%,$(GUEST_SCALE_HELPER_SRCS:.c=.o)) + +GUEST_SCALE_AGENT_SRCS= guest_scale_agent.c parser.c +GUEST_SCALE_AGENT_OBJ := $(patsubst %,$(ODIR)/%,$(GUEST_SCALE_AGENT_SRCS:.c=.o)) + +OBJS = $(GUEST_SCALE_OBJ) $(GUEST_SCALE_AGENT_OBJ) +DEPS = $(OBJS:.o=.d) sdk/sdk.d +-include $(DEPS) + +EXPORT_SDK := wrs-guest-scale-$(MAJOR).$(MINOR).$(PATCH) + +# SYSROOT = $(shell for i in $(LD) ; do echo $$i; done | grep sysroot= | awk -F = '{ print $$2 }'`) +SYSROOT = $(shell echo "$(LD)" | sed -n -e 's/^.*sysroot=//p' | awk '{ print $$1 }') +LIBSERVERGROUP_VER = $(shell find $(SYSROOT) | grep libservergroup.so. | sed -n -e 's/^.*libservergroup\.so\.//p') + +print-%: + @echo $* = $($*) + +printvars: + @$(foreach V,$(sort $(.VARIABLES)), $(if $(filter-out environment% default automatic, $(origin $V)),$(warning $V=$($V) ($(value $V))))) + +.PHONY: printvars + +all: host_all +host_all: $(BINEXECS) sdk/$(EXPORT_SDK).tgz + +# The dependency tracking for the SDK tarball is rather crude. If any +# file changes they're all copied over again. This is quick, so it's +# not worth getting fancy. +sdk/$(EXPORT_SDK).tgz: printvars + @echo "Making SDK" + @mkdir -p sdk/$(EXPORT_SDK) + @mkdir -p sdk/$(EXPORT_SDK)/bin + @mkdir -p sdk/$(EXPORT_SDK)/obj + @mkdir -p sdk/$(EXPORT_SDK)/scripts + @cp scripts/* sdk/$(EXPORT_SDK)/scripts + @echo "MAJOR=$(MAJOR)" > sdk/$(EXPORT_SDK)/Makefile + @echo "MINOR=$(MINOR)" >> sdk/$(EXPORT_SDK)/Makefile + @echo "PATCH=$(PATCH)" >> sdk/$(EXPORT_SDK)/Makefile + @cat Makefile.sdk >> sdk/$(EXPORT_SDK)/Makefile + @cat docs/README.txt | sed 's/1\.0\.0/$(MAJOR).$(MINOR).$(PATCH)/' | sed 's/9.9.9/${LIBSERVERGROUP_VER}/' > sdk/$(EXPORT_SDK)/README.txt + @cp docs/TiS-Guest-Resource-Scaling.pdf sdk/$(EXPORT_SDK)/TiS-Guest-Resource-Scaling.pdf + @cp LICENSE sdk/$(EXPORT_SDK)/LICENSE + @cp $(GUEST_SCALE_AGENT_SRCS) sdk/$(EXPORT_SDK) + @cp misc.h sdk/$(EXPORT_SDK) + @cd sdk && tar czf $(EXPORT_SDK).tgz $(EXPORT_SDK) + @echo -n "sdk/$(EXPORT_SDK).tgz: Makefile.sdk docs/README.txt docs/TiS-Guest-Resource-Scaling.pdf " > sdk/sdk.d + @echo -n "scripts/init_offline_cpus scripts/offline_cpus scripts/offline-cpus.service" >> sdk/sdk.d + @echo -n "scripts/app_scale_helper " >> sdk/sdk.d + @echo -n "$(GUEST_SCALE_AGENT_SRCS) " >> sdk/sdk.d + @echo "misc.h " >> sdk/sdk.d + @echo '*' > sdk/.gitignore + + +$(ODIR)/%.o: %.c + $(CC) -c $(CFLAGS) $(CFLAGS2) -MMD -o $@ $< + +$(BINDIR)/guest_scale_agent: $(GUEST_SCALE_AGENT_OBJ) + $(CC) -o $@ $^ $(LDFLAGS) -lguesthostmsg -ljson-c + +$(BINDIR)/guest_scale_helper: $(GUEST_SCALE_HELPER_OBJ) + $(CC) -o $@ $^ $(LDFLAGS) -lhostguestmsg -ljson-c + +# Add the host clean as a dependency to the SDK stuff +clean: host_clean +host_clean: + rm -rf sdk diff --git a/guest-agent/guest-scale-agent-2.0/Makefile.sdk b/guest-agent/guest-scale-agent-2.0/Makefile.sdk new file mode 100644 index 00000000..369787f9 --- /dev/null +++ b/guest-agent/guest-scale-agent-2.0/Makefile.sdk @@ -0,0 +1,74 @@ +# +# BSD LICENSE +# +# Copyright(c) 2013-2016, Wind River Systems, Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Wind River Systems nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +.PHONY: all clean + +EXECS= guest_scale_agent + +CFLAGS= -g -Wall + +ODIR=obj +BINDIR=bin + +GUEST_SCALE_AGENT_SRCS= guest_scale_agent.c parser.c +GUEST_SCALE_AGENT_OBJ := $(patsubst %,$(ODIR)/%,$(GUEST_SCALE_AGENT_SRCS:.c=.o)) + +BINEXECS=$(addprefix $(BINDIR)/, $(EXECS)) + +OBJS = $(GUEST_SCALE_AGENT_OBJ) + +_DEPS = *.h +DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS)) +DEPS = $(OBJS:.o=.d) +-include $(DEPS) + + +WRS_SERVER_GROUP_DIR ?= $(shell find ../wrs-server-group*[0-9] -maxdepth 0 -type d | sort | tail -n 1) +WRS_SERVER_GROUP_INC2 = $(WRS_SERVER_GROUP_DIR)/include +WRS_SERVER_GROUP_INC ?= $(shell if [ -d $(WRS_SERVER_GROUP_INC2) ]; then echo "$(WRS_SERVER_GROUP_INC2)"; else echo "/usr/include"; fi) +WRS_SERVER_GROUP_LIB2 = $(WRS_SERVER_GROUP_DIR)/lib +WRS_SERVER_GROUP_LIB3 = $(shell if [ -d /usr/lib64 ]; then echo "/usr/lib64"; else echo "/usr/lib"; fi) +WRS_SERVER_GROUP_LIB ?= $(shell if [ -d $(WRS_SERVER_GROUP_LIB2) ]; then echo "$(WRS_SERVER_GROUP_LIB2)"; else echo "$(WRS_SERVER_GROUP_LIB3)"; fi) + +CFLAGS += -I$(WRS_SERVER_GROUP_INC) +LDFLAGS += -L$(WRS_SERVER_GROUP_LIB) + +$(ODIR)/%.o: %.c + $(CC) -c $(CFLAGS) $(CFLAGS2) -MMD -o $@ $< + +all: $(BINEXECS) + +$(BINDIR)/guest_scale_agent: $(GUEST_SCALE_AGENT_OBJ) + $(CC) -o $@ $^ $(LDFLAGS) -lguesthostmsg -ljson-c + +clean: + rm -rf $(ODIR)/* *~ core $(BINDIR)/* diff --git a/guest-agent/guest-scale-agent-2.0/bin/.gitignore b/guest-agent/guest-scale-agent-2.0/bin/.gitignore new file mode 100644 index 00000000..5e7d2734 --- /dev/null +++ b/guest-agent/guest-scale-agent-2.0/bin/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/guest-agent/guest-scale-agent-2.0/docs/README.txt b/guest-agent/guest-scale-agent-2.0/docs/README.txt new file mode 100644 index 00000000..4f55ebf1 --- /dev/null +++ b/guest-agent/guest-scale-agent-2.0/docs/README.txt @@ -0,0 +1,243 @@ +BSD LICENSE + +Copyright(c) 2013-2016, Wind River Systems, Inc. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of Wind River Systems nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------- + +DESCRIPTION +=========== +Guest Server Scaling is a service to allow a guest to scale the capacity of a +single guest server up and down on demand. + +Current supported scaling operation is CPU scaling. + +The resources can be scaled up/down from the nova CLI or GUI. Scaling can also +be set up via heat to be automatically triggered based on Ceilometer statistics. +(This will not be covered in this document, see the full documentation and the +heat SDK for how to configure heat templates for scaling a single guest server.) + +This package contains an agent and a number of scripts to be included in the +guest image. These will handle the guest side of the coordinated efforts +involved in scaling up/down guest resources. + + +DEPENDENCIES +============ + NOTE that this wrs-guest-scale SDK module has both a compile-time and run-time + dependency on the wrs-server-group SDK module. + + This wrs-guest-scale SDK module requires that the wrs-server-group SDK tarball + has been previously extracted and built, and that the resulting libraries + and headers have been placed in a location that can be found by the normal build + tools, or the WRS_SERVER_GROUP_DIR environment variable has been set. + + The output of BOTH the wrs-guest-scale SDK module and the wrs-server-group SDK + module are required to be installed in a guest image for guest resource scaling. + + +REQUIREMENTS +============ + Compilation: + Linux OS, x86_64 architecture + gcc compiler + development libraries and headers for glibc + development libraries and headers for libguesthostmsg + (built by the wrs-server-group SDK package) + development libraries and headers for json-c + + VM Runtime: + Linux OS, x86_64 architecture; CONFIG_HOTPLUG_CPU=y|m + runtime libraries for glibc + runtime libraries for libguesthostmsg + "guest_agent" binary daemon + (provided by the wrs-server-group SDK package) + runtime libraries for json-c + + The code has been tested with glibc 2.15, gcc 4.6 and json-c 0.12.99 but it + should run on other versions without difficulty. + + +DELIVERABLE +=========== +The Guest Server Scaling service is delivered as source with the required +Makefiles in a compressed tarball called "wrs-guest-scale-#.#.#.tgz", such that +it can be compiled for the applicable guest linux distribution. + + +COMPILE +======= +Pre-requisite: + Ensure that the wrs-server-group SDK tarball has been previously extracted and + built, and that the resulting libraries and headers have been placed in a + location that can be found by the normal build tools, or the WRS_SERVER_GROUP_DIR + environment variable has been set. + +Extract the tarball contents: + + tar xvf wrs-guest-scale-#.#.#.tgz + +To compile: + + # Note: assumes wrs-server-group-#.#.#.tgz has already been extracted and compiled. + + cd wrs-guest-scale-#.#.# + + # If wrs-guest-scale-#.#.#.tgz and wrs-server-group-#.#.#.tgz where extracted in a common directory + make + + # Otherwise supply the path to where wrs-server-group can be found. e.g. + make WRS_SERVER_GROUP_DIR=/usr/src/wrs-server-group-#.#.# + +This will produce: + +1) An executable "bin/guest_scale_agent". This handles the basic vCPU scaling +in the guest, and calls out to a helper script if present to support +application-specific customization. It should be configured to respawn +(via /etc/inittab or some other process monitor) if it dies for any reason. +This executable must be installed into the guest (e.g. in "/usr/sbin") and configured +to run at startup as early as possible. +NOTE + The "guest_agent" executable from the wrs-server-group SDK package MUST ALSO + be installed into the guest (e.g. in "/usr/bin"), configured to run at startup + as early as possible and configured to respawn via /etc/inittab or some other + process monitor (in case it dies for any reason). + +2) A script "script/app_scale_helper". This is an optional script that is +intended to allow for app-specific customization. If present, it must be +installed in "/usr/sbin". If present, it will be called by "guest_scale_agent" +when scaling in either direction. + +3) A script "script/offline_cpus". This must be run later in the init sequence, +after guest_scale_agent has started up but before the application has started +any CPU-affined applications. A helper script "script/init_offline_cpus" has +been provided, and should be installed to "/etc/init.d/offline_cpus. The +"offline_cpus" script will offline vCPUs in the guest to match the status on +the hypervisor. This covers the case where we are booting up with some CPUs +offlined by the hypervisor. + +4) For systemd users, the files "scripts/guest-scale-agent.service" and +"scripts/offline-cpus.service" should be copied to /lib/systemd/system/. + + +Note: +The inclusion of the files into the build system and the guest image and the +configuration of the guest startup scripts is left up to the user to allow for +different build systems and init subsystems in the guest. + + +INSTALL +======= +Installing in a running VM: + + As the root user + 1) Copy "bin/guest_scale_agent" to /usr/sbin in the VM. + + 2) Copy "scripts/app_scale_helper" and "scripts/offline_cpus" to /usr/sbin in the VM. + + 3) Copy "scripts/init_offline_cpus" to "/etc/init.d/offline_cpus". (Note the name change.) + + 4) Copy "scripts/guest-scale-agent.service" and "scripts/offline-cpus.service" to + /lib/systemd/system/. + + 5) Run "systemctl enable guest-scale-agent.service", "systemctl enable offline-cpus.service", + "systemctl start guest-scale-agent.service", "systemctl start offline-cpus.service" + +The VM should now be ready to scale up and down. + + +USAGE +===== +The service is designed to be simple to use. A basic description is given +below, but more details are provided in the source and scripts. + +1) Create a new flavor (or edit an existing flavor) such that the number of +vCPUs in the flavor matches the desired maximum number of vCPUs. To specify the +minimum number of vCPUs, create an "extra spec" metadata entry for the flavor +with a key of "wrs:min_vcpus" and a value that is an integer number between one +and the max number of vCPUs. This can be done from the CLI or the GUI. (In the +GUI select the "Admin" tab, go to the "Flavor" navigation link, click on a +flavor name, select the "Extra Specs" tab, click on "Create", select +"Minimum Number of CPUs" from the pulldown, and enter the desired value.) + +2) Build BOTH the wrs-server-group SDK package and this wrs-guest-scale package, +and install the output of BOTH packages in an image. Lastly, ensure that the +CONFIG_HOTPLUG_CPU kernel config option is set in the image kernel. + +3) Boot the image. It will come up with the full set of vCPUs. + +4) To reduce the number of online vCPUs in the guest server, run +"nova scale cpu down" from the controller (or anywhere else you can run +nova commands). This will pass a message up into the guest, where it will be +handled by "guest_scale_agent". That in turn will call out to +"/usr/sbin/app_scale_helper" (if it exists) which is expected to pick a vCPU to +offline. This script can be modified/replaced as needed for application- +specific purposes. By default, it will select the highest-numbered online vCPU. +If the script isn't present or errors out, then "guest_scale_agent" will itself +select the highest-numbered online vCPU as the one to be offlined. It will then +tell the guest kernel to offline the selected vCPU, and will pass the selected +vCPU back down to the hypervisor, which will adjust vCPU affinity so that the +underlying physical CPU can be freed up for use by other VMs. At this point +displaying the information for the guest server will show it using less than the +maximum number of cpus. + +5) To increase the number of online vCPUs, run "nova scale cpu up". +Assuming the resources are available the hypervisor will allocate a physical CPU +and will associate it with the guest server. "guest_scale_agent" will set the +lowest-numbered offline vCPU to "online", and will pass the vCPU number to +"/usr/sbin/app_scale_helper" (if it exists) for the application to do any +special handling that may be required. + + +The behaviour of a scaled-down server during various nova operations is as +follows: + +live migration: server remains scaled-down +pause/unpause: server remains scaled-down +stop/start: server remains scaled-down +evacuation: server remains scaled-down +rebuild: server remains scaled-down +automatic restart on crash: server remains scaled-down +cold migration: server reverts to max vcpus +resize: server reverts to max vcpus for the new flavor + +If a snapshot is taken of a scaled-down server, a new server booting the +snapshot will start with the number of vCPUs specified by the flavor. + +CAVEATS +======= +It is possible for the scale-up operation to fail if the compute node has +already allocated all of its resources to other guests. If this happens, +the system will not do any automatic migration to try to free up resources. +Manual action will be required to free up resources. + +Any CPUs that are handling userspace DPDK/AVP packet processing should not be +offlined. It may appear to work, but may lead to packet loss. This can be +enforced by setting the wrs:min_vcpus value appropriately high. + +If hyperthreading is used, the flavor must set hw:cpu_thread_policy to +isolate and set cpu_policy to dedicated. diff --git a/guest-agent/guest-scale-agent-2.0/docs/TiS-Guest-Resource-Scaling.doc b/guest-agent/guest-scale-agent-2.0/docs/TiS-Guest-Resource-Scaling.doc new file mode 100644 index 00000000..5e0ad4da Binary files /dev/null and b/guest-agent/guest-scale-agent-2.0/docs/TiS-Guest-Resource-Scaling.doc differ diff --git a/guest-agent/guest-scale-agent-2.0/docs/TiS-Guest-Resource-Scaling.pdf b/guest-agent/guest-scale-agent-2.0/docs/TiS-Guest-Resource-Scaling.pdf new file mode 100644 index 00000000..70092031 Binary files /dev/null and b/guest-agent/guest-scale-agent-2.0/docs/TiS-Guest-Resource-Scaling.pdf differ diff --git a/guest-agent/guest-scale-agent-2.0/guest_scale_agent.c b/guest-agent/guest-scale-agent-2.0/guest_scale_agent.c new file mode 100644 index 00000000..5c2ceb2a --- /dev/null +++ b/guest-agent/guest-scale-agent-2.0/guest_scale_agent.c @@ -0,0 +1,515 @@ +/** +* Copyright (c) <2013-2016>, Wind River Systems, Inc. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1) Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2) Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* 3) Neither the name of Wind River Systems nor the names of its contributors may be +* used to endorse or promote products derived from this software without specific +* prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "misc.h" + +gh_info_t *info; + +/* Scaling Request/Response message is encoded in JSON format. + The message sent out to UNIX socket is a null-terminated JSON format string + without embedded newlines. + + Format: + {key:value,key:value,..., key:value} + + Key/value pairs for Scaling Request: + "version": - version of the interface + "timeout_ms": - timeout for app_scale_helper scripts + "resource": “cpu” - indicate the resouce to scale. + Only cpu is currently supported. + "direction“: "up” or “down” + "online_cpu": - vcpu number to online when scale up + "online_cpus": - array of current online cpus + when request was sent. + example: [0,1,2,3,4,5] + + Key/value pairs for Scaling Response: + "version": + "resource": “cpu” + "direction“: "up” or “down” + "online_cpu": - vcpu number to online when scale up + "offline_cpu": - actual offlined vcpu number + "online_cpus": - array of current online cpus + when response was sent. + "result": "success" or "fail" + "err_msg": - error message if result is fail + +*/ + +#define CPU_SCRIPT "/usr/sbin/app_scale_helper" + +// generic function to call out to helper script +// need to add support for timeout in here in case script hangs +int call_helper_script(char *cmd, int timeout_ms) +{ + FILE *fp; + int rc; + + fp = popen(cmd, "w"); + if (fp) { + rc = pclose(fp); + if (rc == -1) { + ERR_LOG("pclose failed: %m"); + return -1; + } else { + if (WIFEXITED(rc)) { + rc = WEXITSTATUS(rc); + if (rc == 127) { + ERR_LOG("problem with shell or helper script, possibly script missing"); + return -1; + } else + return rc; + } else { + return -1; + } + } + } else { + ERR_LOG("popen failed due to fork/pipe/memory"); + return -1; + } +} + + +int online_cpu(unsigned cpu) +{ + int fd; + int rc; + char buf[100]; + char val; + snprintf(buf, sizeof(buf), "/sys/devices/system/cpu/cpu%u/online", cpu); + fd = open(buf, O_RDWR); + if (fd < 0) { + ERR_LOG("can't open cpu online path: %m"); + return -1; + } + rc = read(fd, &val, 1); + if (rc != 1){ + ERR_LOG("can't read cpu online value: %m"); + return -1; + } + if (val == '1') { + ERR_LOG("cpu %d is already online", cpu); + return 0; + } + val = '1'; + rc = write(fd, &val, 1); + if (rc != 1){ + ERR_LOG("can't set cpu %d online", cpu); + return -1; + } + return 0; +} + +int offline_cpu(unsigned cpu) +{ + int fd; + int rc; + char buf[100]; + char val; + snprintf(buf, sizeof(buf), "/sys/devices/system/cpu/cpu%u/online", cpu); + fd = open(buf, O_RDWR); + if (fd < 0) { + ERR_LOG("can't open cpu online path: %m"); + return -1; + } + rc = read(fd, &val, 1); + if (rc != 1){ + ERR_LOG("can't read cpu online value: %m"); + return -1; + } + if (val == '0') { + ERR_LOG("cpu %d is already offline\n", cpu); + return 0; + } + val = '0'; + rc = write(fd, &val, 1); + if (rc != 1){ + ERR_LOG("can't set cpu %d offline", cpu); + return -1; + } + return 0; +} + +// read /sys/devices/system/cpu/online and get the last cpu listed +int get_highest_online_cpu(void) +{ + int fd, rc; + char buf[256]; + char *start; + unsigned int cpu; + fd = open("/sys/devices/system/cpu/online", O_RDONLY); + if (fd < 0) { + ERR_LOG("can't fopen /sys/devices/system/cpu/online: %m"); + return -1; + } + + rc = read(fd, buf, sizeof(buf)); + if (rc < 2) { + ERR_LOG("error parsing /sys/devices/system/cpu/online, too few chars"); + return -1; + } + + // go to the end of the string + start = buf+rc-1; + if(*start != '\n') { + ERR_LOG("error parsing /sys/devices/system/cpu/online, not null-terminated"); + return -1; + } + + // now go backwards until we get to a separator or the beginning of the string + while ((*start != ',') && (*start != '-') && (start != buf)) + start--; + + start++; + rc = sscanf(start, "%u", &cpu); + if (rc != 1) { + ERR_LOG("error parsing /sys/devices/system/cpu/online, bad number"); + return -1; + } + + return cpu; +} + + +char *get_online_cpu_range(void) +{ + FILE *file; + int rc; + char *str = NULL; + file = fopen("/sys/devices/system/cpu/online", "r"); + if (!file) { + ERR_LOG("can't fopen /sys/devices/system/cpu/online: %m"); + return 0; + } + rc = fscanf(file, "%ms", &str); + if (rc != 1) + ERR_LOG("can't read /sys/devices/system/cpu/online: %m"); + fclose(file); + return str; +} + + +void cpu_scale_down(json_object *jobj_request, + json_object *jobj_response) +{ + char cmd[1000]; + int cpu=-1; + int rc; + + //build our command to send to the helper script + rc = snprintf(cmd, sizeof(cmd), "%s --cpu_del\n", CPU_SCRIPT); + if ((rc > sizeof(cmd)) || rc < 0) { + ERR_LOG("error generating command: %m"); + goto pick_cpu; + } + + struct json_object *jobj_timeout_ms; + int timeout_ms; + if (!json_object_object_get_ex(jobj_request, TIMEOUT_MS, &jobj_timeout_ms)) + { + ERR_LOG("failed to parse timeout_ms"); + goto failed; + } + + errno = 0; + timeout_ms = json_object_get_int(jobj_timeout_ms); + if(errno){ + ERR_LOG("Error converting timeout_ms: %s", strerror(errno)); + goto failed; + } + + // call app helper script to select cpu to offline + rc = call_helper_script(cmd, timeout_ms); + if (rc < 0) { + ERR_LOG("call to app helper script failed\n"); + goto pick_cpu; + } else if (rc == 0) { + ERR_LOG("call to app helper script return invalid cpu number 0\n"); + goto pick_cpu; + } else { + INFO_LOG("app helper script chose cpu %d to offline\n", rc); + cpu = rc; + } + +pick_cpu: + // if the app helper script doesn't exist or didn't return + // a cpu to offline, pick one ourselves + if (cpu == -1) { + cpu = get_highest_online_cpu(); + if (cpu <= 0) { + ERR_LOG("unable to find cpu to offline\n"); + goto failed; + } + } + + // try to offline selected cpu + rc = offline_cpu(cpu); + if (rc < 0) { + ERR_LOG("failed to set cpu %d offline\n", cpu); + goto failed; + } + + INFO_LOG("set cpu %d offline", cpu); + + // we have successfully offlined the cpu + json_object_object_add(jobj_response, RESULT, json_object_new_string("success")); + json_object_object_add(jobj_response, OFFLINE_CPU, json_object_new_int(cpu)); + struct online_cpus *current_online_cpus = range_to_array(get_online_cpu_range()); + + // no need to release jobj_array as its ownership is transferred to jobj_response + struct json_object *jobj_array = new_json_obj_from_array(current_online_cpus); + json_object_object_add(jobj_response, ONLINE_CPUS, jobj_array); + return; + +failed: + json_object_object_add(jobj_response, RESULT, json_object_new_string("fail")); + json_object_object_add(jobj_response, ERR_MSG, json_object_new_string(errorbuf)); + return; +} + + +void cpu_scale_up(json_object *jobj_request, + json_object *jobj_response) +{ + char cmd[1000]; + struct json_object *jobj_timeout_ms; + if (!json_object_object_get_ex(jobj_request, TIMEOUT_MS, &jobj_timeout_ms)) { + ERR_LOG("failed to parse timeout_ms"); + goto failed; + } + int timeout_ms = json_object_get_int(jobj_timeout_ms); + + struct json_object *jobj_cpu; + if (!json_object_object_get_ex(jobj_request, ONLINE_CPU, &jobj_cpu)) { + ERR_LOG("failed to parse online_cpu"); + goto failed; + } + int cpu = json_object_get_int(jobj_cpu); + + //online_cpus is optional + struct json_object *jobj_online_cpus; + const char *online_cpus; + if (!json_object_object_get_ex(jobj_request, ONLINE_CPUS, &jobj_online_cpus)) { + ERR_LOG("failed to parse online_cpus"); + goto failed; + } + + json_object_object_get_ex(jobj_request, ONLINE_CPUS, &jobj_online_cpus); + if (!json_object_is_type(jobj_online_cpus, json_type_array)) { + ERR_LOG("failed to parse online_cpus"); + goto failed; + } + online_cpus = json_object_to_json_string_ext(jobj_online_cpus, JSON_C_TO_STRING_PLAIN); + + int rc = online_cpu(cpu); + if (rc < 0) { + printf("failed to set cpu %d online\n", cpu); + goto failed; + } + + INFO_LOG("set cpu %d online", cpu); + + // Now try to call out to the helper script + // If it fails, not the end of the world. + + rc = snprintf(cmd, sizeof(cmd), "%s --cpu_add %d %s\n", + CPU_SCRIPT, cpu, online_cpus); + + if ((rc > 0) && (rc < sizeof(cmd))) { + rc = call_helper_script(cmd, timeout_ms); + if (rc != 0) + ERR_LOG("call to app helper script failed, return code: %d\n", rc); + } else + ERR_LOG("error generating command: %m"); + + json_object_object_add(jobj_response, RESULT, json_object_new_string("success")); + json_object_object_add(jobj_response, ONLINE_CPU, json_object_new_int(cpu)); + struct online_cpus *current_online_cpus = range_to_array(get_online_cpu_range()); + + // no need to release jobj_array as its ownership is transferred to jobj_response + struct json_object *jobj_array = new_json_obj_from_array(current_online_cpus); + json_object_object_add(jobj_response, ONLINE_CPUS, jobj_array); + + return; + +failed: + json_object_object_add(jobj_response, RESULT, json_object_new_string("fail")); + json_object_object_add(jobj_response, ERR_MSG, json_object_new_string(errorbuf)); + return; +} + + +/* Callback message handler. This will be called by the generic guest/host + * messaging library when a valid message arrives from the host. + */ +void msg_handler(const char *source_addr, json_object *jobj_request) +{ + int rc; + + // parse version + struct json_object *jobj_version; + if (!json_object_object_get_ex(jobj_request, VERSION, &jobj_version)) { + ERR_LOG("failed to parse version"); + return; + } + int version = json_object_get_int(jobj_version); + + if (version != CUR_VERSION) { + ERR_LOG("invalid version %d, expecting %d", version, CUR_VERSION); + return; + } + + // parse msg_type + struct json_object *jobj_msg_type; + if (!json_object_object_get_ex(jobj_request, MSG_TYPE, &jobj_msg_type)) { + ERR_LOG("failed to parse msg_type"); + return; + } + const char *msg_type = json_object_get_string(jobj_msg_type); + + if (!strcmp(msg_type, MSG_TYPE_NACK)) { + struct json_object *jobj_log_msg; + if (!json_object_object_get_ex(jobj_request, LOG_MSG, &jobj_log_msg)) { + ERR_LOG("Nack: failed to parse log_msg"); + } + const char *log_msg = json_object_get_string(jobj_log_msg); + ERR_LOG("Nack received, error message from host: %s", log_msg); + return; + } else if (!strcmp(msg_type, MSG_TYPE_SCALE_REQUEST)) { + ; + } else { + ERR_LOG("unknown message type: %s", msg_type); + return; + } + + struct json_object *jobj_response = json_object_new_object(); + if (jobj_response == NULL) { + ERR_LOG("failed to allocate json object for response"); + return; + } + + struct json_object *jobj_resource; + if (!json_object_object_get_ex(jobj_request, RESOURCE, &jobj_resource)) { + ERR_LOG("failed to parse resource"); + goto done; + } + const char *resource = json_object_get_string(jobj_resource); + + struct json_object *jobj_direction; + if (!json_object_object_get_ex(jobj_request, DIRECTION, &jobj_direction)) { + ERR_LOG("failed to parse direction'"); + goto done; + } + const char *direction = json_object_get_string(jobj_direction); + + rc = -1; + if (!strcmp(resource,"cpu")) { + if (!strcmp(direction,"up")) { + cpu_scale_up(jobj_request, jobj_response); + } else if (!strcmp(direction,"down")) { + cpu_scale_down(jobj_request, jobj_response); + } + } + + json_object_object_add(jobj_response, VERSION, json_object_new_int(CUR_VERSION)); + json_object_object_add(jobj_response, RESOURCE, jobj_resource); + json_object_object_add(jobj_response, DIRECTION, jobj_direction); + + const char *response = json_object_to_json_string_ext(jobj_response, JSON_C_TO_STRING_PLAIN); + + // Send response back to the sender. + rc = gh_send_msg(info, source_addr, response); + if (rc < 0) { + ERR_LOG("gh_send_msg failed: %s\n", gh_get_error(info)); + return; + } +done: + json_object_put(jobj_response); +} + + +void wait_for_messages(int fd) +{ + int rc; + fd_set rfds, rfds_tmp; + + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + + while(1) { + rfds_tmp = rfds; + rc = select(fd+1, &rfds_tmp, NULL, NULL, NULL); + if (rc > 0) { + if (gh_process_msg(info) < 0) { + ERR_LOG("problem processing messages: %s\n", + gh_get_error(info)); + } + } else if (rc < 0) { + ERR_LOG("select(): %m"); + } + } +} + + +int main() +{ + int fd = gh_init(msg_handler, SCALE_AGENT_ADDR, &info); + if (fd == -1) { + if (!info) + ERR_LOG("Unable to allocate memory for info: %m"); + else + ERR_LOG("Unable to initialize guest/host messaging: %s\n", + gh_get_error(info)); + return -1; + } + INFO_LOG("Running offline_cpus script"); + system("offline_cpus"); + wait_for_messages(fd); + + return 0; +} diff --git a/guest-agent/guest-scale-agent-2.0/guest_scale_helper.c b/guest-agent/guest-scale-agent-2.0/guest_scale_helper.c new file mode 100644 index 00000000..cf7207b6 --- /dev/null +++ b/guest-agent/guest-scale-agent-2.0/guest_scale_helper.c @@ -0,0 +1,534 @@ +/** +* Copyright (c) <2013-2016>, Wind River Systems, Inc. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1) Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2) Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* 3) Neither the name of Wind River Systems nor the names of its contributors may be +* used to endorse or promote products derived from this software without specific +* prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +/* This is intended to run as a helper function, called by nova, to pass data up + * into the guest and receive data back from the guest and return it to nova. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "misc.h" + +hg_info_t *info; + +#define SPARE_ALLOC 128 + +#define INSTANCE_NAME_SIZE 32 +#define NACK_LOG_SIZE 500 + +#define UNIX_ADDR_LEN 16 +#define DEFAULT_TIMEOUT_MS 1000 +#define TIMEOUT_OVERHEAD_MS 500 +#define MIN_SCRIPT_TIMEOUT_MS 500 +int timeout_ms = DEFAULT_TIMEOUT_MS; + +int *request_online_cpus; +int len_request_online_cpus; +int request_cpu; + +void usage() { + printf("guest_scale_helper --instance_name \n"); + printf(" --cpu_del | --cpu_add \n"); + printf(" [--timeout ]\n"); + printf("\n"); + exit(-1); +} + +void handle_cpu_scale_up(json_object *jobj_response, const char *source_instance) +{ + int rc = -1; + char log_msg[NACK_LOG_SIZE]; + + struct json_object *jobj_result; + if (!json_object_object_get_ex(jobj_response, RESULT, &jobj_result)) { + snprintf(log_msg, NACK_LOG_SIZE, "failed to parse result"); + goto failed; + } + const char *result = json_object_get_string(jobj_result); + + if (!strcmp(result, "fail")) { + struct json_object *jobj_err_msg; + const char *err_msg; + + if (!json_object_object_get_ex(jobj_response, ERR_MSG, &jobj_err_msg)) + err_msg=""; + else + err_msg = json_object_get_string(jobj_err_msg); + ERR_LOG("Error: guest helper scaling cpu up failed: %s\n", err_msg); + goto out; + } + + struct json_object *jobj_online_cpu; + if (!json_object_object_get_ex(jobj_response, ONLINE_CPU, &jobj_online_cpu)) { + snprintf(log_msg, NACK_LOG_SIZE, "failed to parse online_cpu"); + goto failed; + } + int online_cpu = json_object_get_int(jobj_online_cpu); + + struct json_object *jobj_online_cpus; + + json_object_object_get_ex(jobj_response, ONLINE_CPUS, &jobj_online_cpus); + if (!json_object_is_type(jobj_online_cpus, json_type_array)) { + snprintf(log_msg, NACK_LOG_SIZE, "failed to parse online_cpus"); + goto failed; + } + + int i, len_response; + len_response = json_object_array_length(jobj_online_cpus); + int *response_online_cpus = malloc(len_response*sizeof(int)); + + for (i=0; i< len_response; i++){ + response_online_cpus[i] = json_object_get_int(json_object_array_get_idx(jobj_online_cpus, i)); + } + + // compare request and response, assuming cpus are in the same order + if ( (len_response - len_request_online_cpus ) <=1 ) { + int req =0; + int rsp = 0; + int found_req = 0; + while (req < len_request_online_cpus){ + + if (response_online_cpus[rsp] == request_online_cpus[req]) { + req++; rsp++; + } else if (response_online_cpus[rsp] == request_cpu) { + rsp++; + found_req = 1; + // protect against infinite loop + if (rsp == len_response) + break; + } else { + ERR_LOG("Error: cpu %d online by guest but not online in nova\n", response_online_cpus[rsp]); + break; + } + } + + if ((!found_req) && (req == len_request_online_cpus)) { + if ((len_response == len_request_online_cpus) || + (response_online_cpus[len_response] != request_cpu)) { + ERR_LOG("Error: cpu %d online by nova but not online in guest\n", response_online_cpus[req]); + } + } + } + else { + ERR_LOG("Error: guest's online cpu range doesn't match nova\n"); + char buf[1024]; + print_array(buf, response_online_cpus, len_response); + ERR_LOG("guest online cpu range: %s\n", buf); + } + + // Yay, everything looks good. + free(response_online_cpus); + free(request_online_cpus); + exit(online_cpu); + rc = online_cpu; +failed: + send_nack(log_msg, source_instance); +out: + free(request_online_cpus); + exit(rc); +} + + +void handle_cpu_scale_down(json_object *jobj_response, const char *source_instance) +{ + int rc = -1; + struct json_object *jobj_result; + char log_msg[NACK_LOG_SIZE]; + + if (!json_object_object_get_ex(jobj_response, RESULT, &jobj_result)) { + snprintf(log_msg, NACK_LOG_SIZE, "failed to parse result"); + goto failed; + } + const char *result = json_object_get_string(jobj_result); + + if (!strcmp(result, "fail")) { + struct json_object *jobj_err_msg; + const char *err_msg; + if (!json_object_object_get_ex(jobj_response, ERR_MSG, &jobj_err_msg)) + err_msg=""; + else + err_msg = json_object_get_string(jobj_err_msg); + ERR_LOG("problem, guest helper scaling cpu down failed: %s\n", err_msg); + goto out; + } + + struct json_object *jobj_offline_cpu; + if (!json_object_object_get_ex(jobj_response, OFFLINE_CPU, &jobj_offline_cpu)) { + snprintf(log_msg, NACK_LOG_SIZE, "failed to parse offline_cpu"); + goto failed; + } + int offline_cpu = json_object_get_int(jobj_offline_cpu); + + struct json_object *jobj_online_cpus; + json_object_object_get_ex(jobj_response, ONLINE_CPUS, &jobj_online_cpus); + if (!json_object_is_type(jobj_online_cpus, json_type_array)) { + snprintf(log_msg, NACK_LOG_SIZE, "failed to parse online_cpus"); + goto failed; + } + + int i, len_response; + len_response = json_object_array_length(jobj_online_cpus); + int *response_online_cpus = malloc(len_response*sizeof(int)); + + for (i=0; i< len_response; i++){ + response_online_cpus[i] = json_object_get_int(json_object_array_get_idx(jobj_online_cpus, i)); + } + + if (response_online_cpus[len_response] > offline_cpu) { + ERR_LOG("Error: cpu %d is still online in guest\n", offline_cpu); + } + + // Yay, everything looks good. + free(response_online_cpus); + free(request_online_cpus); + rc = offline_cpu; + exit(rc); +failed: + send_nack(log_msg, source_instance); +out: + free(request_online_cpus); + exit(rc); +} + +// This should call exit(0) on success or exit(-1) on permanent failure(). +// Returning will continue listening. +// Theoretically this could come from any instance, need to fix that. +void msg_handler(const char *source_addr, const char *source_instance, struct json_object *jobj_response) +{ + // parse version + struct json_object *jobj_version; + char log_msg[NACK_LOG_SIZE]; + + if (!json_object_object_get_ex(jobj_response, VERSION, &jobj_version)) { + snprintf(log_msg, NACK_LOG_SIZE, "failed to parse version"); + goto failed; + } + int version = json_object_get_int(jobj_version); + + if (version != CUR_VERSION) { + snprintf(log_msg, NACK_LOG_SIZE, "invalid version %d, expecting %d", version, CUR_VERSION); + goto failed; + } + + struct json_object *jobj_resource; + if (!json_object_object_get_ex(jobj_response, RESOURCE, &jobj_resource)) { + snprintf(log_msg, NACK_LOG_SIZE, "failed to parse resource"); + goto failed; + } + const char *resource = json_object_get_string(jobj_resource); + + struct json_object *jobj_direction; + if (!json_object_object_get_ex(jobj_response, DIRECTION, &jobj_direction)) { + snprintf(log_msg, NACK_LOG_SIZE, "failed to parse direction"); + goto failed; + } + const char *direction = json_object_get_string(jobj_direction); + + if (!strcmp(resource,"cpu")) { + if (!strcmp(direction,"up")) { + handle_cpu_scale_up(jobj_response, source_instance); + } else if (!strcmp(direction,"down")) { + handle_cpu_scale_down(jobj_response, source_instance); + } + } + + // if handle_cpu_scale_up/down is called, program should exit, + // so this is only called when scale up/down are not properly handled. + sprintf(log_msg, "unknown message, resource %s, direction %s", + resource, direction); + +failed: + send_nack(log_msg, source_instance); +} + +// instance_name will be of the form instance-xxxxxxxx +// We want to make a name of the form scale-xxxxxxxx +void instance_to_addr(const char *instance_name, char *addr) +{ + const char *match = "instance-"; + const char *replace = "scale-"; + char *tmp = strstr(instance_name, match); + if (!tmp) { + ERR_LOG("Instance name %s doesn't match expected pattern\n", + instance_name); + exit(-1); + } + strcpy(addr, replace); + strncpy(addr+strlen(replace), instance_name+strlen(match), + UNIX_ADDR_LEN-strlen(replace)-1); + addr[UNIX_ADDR_LEN-1]='\0'; +} + +void handle_message(const char *request, const char *instance_name) +{ + int rc; + fd_set rfds, rfds_tmp; + int fd; + char addr[UNIX_ADDR_LEN]; + + INFO_LOG("handling scaling request: %s", request); + + // Create a unique address from the instance name. When using a helper app + // this is needed in order to handle simultaneous scale events for different + // servers on the same hypervisor. + instance_to_addr(instance_name, addr); + + fd = hg_init(msg_handler, addr, &info); + if (fd == -1) { + if (!info) + ERR_LOG("Unable to allocate memory for info: %m"); + else + ERR_LOG("Unable to initialize guest/host messaging: %s\n", + hg_get_error(info)); + exit(-1); + } + + rc = hg_send_msg(info, SCALE_AGENT_ADDR, instance_name, request); + if (rc < 0) { + ERR_LOG("hg_send_msg failed: %s\n", hg_get_error(info)); + exit(-1); + } + + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + + while(1) { + rfds_tmp = rfds; + rc = select(fd+1, &rfds_tmp, NULL, NULL, NULL); + if (rc > 0) { + if (hg_process_msg(info) < 0) { + ERR_LOG("problem processing messages: %s\n", + hg_get_error(info)); + } + } else if (rc < 0) { + ERR_LOG("select(): %m"); + } + } +} + +void send_nack(char *log_msg, const char *instance_name) +{ + ERR_LOG("sending Nack with error: %s\n", log_msg); + struct json_object *jobj_msg = json_object_new_object(); + if (jobj_msg == NULL) { + ERR_LOG("failed to allocate json object for nack msg\n"); + return; + } + + json_object_object_add(jobj_msg, VERSION, json_object_new_int(CUR_VERSION)); + json_object_object_add(jobj_msg, MSG_TYPE, json_object_new_string(MSG_TYPE_NACK)); + json_object_object_add(jobj_msg, LOG_MSG, json_object_new_string(log_msg)); + + const char *msg = json_object_to_json_string_ext(jobj_msg, JSON_C_TO_STRING_PLAIN); + hg_send_msg(info, SCALE_AGENT_ADDR, instance_name, msg); + + json_object_put(jobj_msg); +} + +void handle_timeout(int sig) +{ + _exit(-2); +} + +void setup_timeout(int timeout_ms) +{ + int rc; + struct itimerval itv; + itv.it_interval.tv_sec = 0; + itv.it_interval.tv_usec = 0; + itv.it_value.tv_sec = 0; + itv.it_value.tv_usec = timeout_ms * 1000; + + // normalize the timer + while(itv.it_value.tv_usec >= 1000000) { + itv.it_value.tv_usec -= 1000000; + itv.it_value.tv_sec += 1; + } + + rc = setitimer(ITIMER_REAL, &itv, NULL); + if (rc < 0) { + ERR_LOG("unable to set timeout"); + exit(-1); + } + + if (signal(SIGALRM, handle_timeout) == SIG_ERR) + ERR_LOG("unable to set timeout handler, continuing anyway: %m"); +} + +struct json_object *create_new_jobj_msg(int timeout_ms, + const char *resource, + const char *direction, + int cpu, + const char *online_cpus) +{ + //validate values + if (timeout_ms < TIMEOUT_OVERHEAD_MS) { + printf("timeout %d too short\n", timeout_ms); + goto invalid_values; + } + + if (strcmp(resource, "cpu")!=0) { + printf("invalid resource %s\n", resource); + goto invalid_values; + } + + struct json_object *jobj_online_cpus; + if (!strcmp(direction, "up")) { + jobj_online_cpus = json_tokener_parse(online_cpus); + if (!json_object_is_type(jobj_online_cpus, json_type_array)) { + printf("invalid online_cpus %s\n", online_cpus); + goto invalid_values; + } + len_request_online_cpus = json_object_array_length(jobj_online_cpus); + request_online_cpus = malloc(len_request_online_cpus*sizeof(int)); + int i; + for (i=0; i< len_request_online_cpus; i++) { + request_online_cpus[i] = json_object_get_int(json_object_array_get_idx(jobj_online_cpus, i)); + } + + } else if (strcmp(direction, "down")!=0) { + printf("invalid direction %s\n", direction); + goto invalid_values; + } + + struct json_object *jobj_msg = json_object_new_object(); + if (jobj_msg == NULL) { + printf("failed to allocate json object for msg\n"); + return NULL; + } + + json_object_object_add(jobj_msg, VERSION, json_object_new_int(CUR_VERSION)); + json_object_object_add(jobj_msg, MSG_TYPE, json_object_new_string(MSG_TYPE_SCALE_REQUEST)); + json_object_object_add(jobj_msg, TIMEOUT_MS, json_object_new_int(timeout_ms - MIN_SCRIPT_TIMEOUT_MS)); + json_object_object_add(jobj_msg, RESOURCE, json_object_new_string(resource)); + json_object_object_add(jobj_msg, DIRECTION, json_object_new_string(direction)); + + if (!strcmp(direction, "up")) { + json_object_object_add(jobj_msg, ONLINE_CPU, json_object_new_int(cpu)); + json_object_object_add(jobj_msg, ONLINE_CPUS, jobj_online_cpus); + } + + return jobj_msg; + +invalid_values: + usage(); + return NULL; +} + +int main(int argc, char *argv[]) +{ + int i; + char *instance_name; + + // msg values + int cpu; + const char *resource; + const char *direction; + const char *request_online_cpus_str = NULL; + + for(i=1;i INSTANCE_NAME_SIZE) { + printf("instance name is too large\n"); + usage(); + } else + instance_name = argv[i]; + } + else { + printf("instance_name option specified without name\n"); + usage(); + } + } else if (0==strcmp(argv[i], "--cpu_add")) { + i++; + if (i, Wind River Systems, Inc. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1) Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2) Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* 3) Neither the name of Wind River Systems nor the names of its contributors may be +* used to endorse or promote products derived from this software without specific +* prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#define CUR_VERSION 2 + +#define LOG_MSG_SIZE 100 +#define SCALE_AGENT_ADDR "cgcs.scale" +#define ERRORSIZE 400 + +// keys for guest scaling messages +#define VERSION "version" +#define TIMEOUT_MS "timeout_ms" +#define RESOURCE "resource" +#define DIRECTION "direction" +#define ONLINE_CPU "online_cpu" +#define OFFLINE_CPU "offline_cpu" +#define ONLINE_CPUS "online_cpus" +#define RESULT "result" +#define ERR_MSG "err_msg" +#define MSG_TYPE "msg_type" +#define LOG_MSG "log_msg" // for Nack + +// message types for scaling messages +#define MSG_TYPE_SCALE_REQUEST "scale_request" +#define MSG_TYPE_NACK "nack" + +char errorbuf[ERRORSIZE]; + +#define LOG(priority, format, ...) \ + syslog(priority, "%s(%d): " format, __FILE__, __LINE__, ##__VA_ARGS__) + +#define ERR_LOG(format, ...) \ + do { \ + LOG(LOG_DAEMON|LOG_ERR, format, ##__VA_ARGS__); \ + fprintf(stderr, format, ##__VA_ARGS__); \ + snprintf(errorbuf, sizeof(errorbuf)-1, format, ##__VA_ARGS__); \ + } while (0) + +#define INFO_LOG(format, ...) \ + do { \ + LOG(LOG_DAEMON|LOG_INFO, format, ##__VA_ARGS__); \ + } while (0) + + +struct online_cpus { + int numcpus; + char status[]; +}; + +struct online_cpus *range_to_array(const char *range); +struct json_object *new_json_obj_from_array (struct online_cpus *cpuarray); +void print_array(char *buf, int *array, int len); + diff --git a/guest-agent/guest-scale-agent-2.0/obj/.gitignore b/guest-agent/guest-scale-agent-2.0/obj/.gitignore new file mode 100644 index 00000000..5e7d2734 --- /dev/null +++ b/guest-agent/guest-scale-agent-2.0/obj/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/guest-agent/guest-scale-agent-2.0/parser.c b/guest-agent/guest-scale-agent-2.0/parser.c new file mode 100644 index 00000000..3eaef1c1 --- /dev/null +++ b/guest-agent/guest-scale-agent-2.0/parser.c @@ -0,0 +1,150 @@ +/** +* Copyright (c) <2013-2016>, Wind River Systems, Inc. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1) Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2) Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* 3) Neither the name of Wind River Systems nor the names of its contributors may be +* used to endorse or promote products derived from this software without specific +* prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include "misc.h" + + +/* Print a range of numbers in a consistent way. */ +char print_range(int start, int end, char *str) +{ + int len; + if (start == end) + len = sprintf(str, "%d,", start); + else + len = sprintf(str, "%d-%d,", start, end); + return len; +} + +void print_array(char *buf, int *array, int len) { + int i; + for(i = 0; i < len; i++) { + sprintf(buf+strlen(buf),"%d,", array[i]); + } + // remove the last comma + buf[strlen(buf)-1]='\0'; +} + +#define BUFLEN 1024 + +/* Takes as input a string representation of online cpus of the form + * "0,1,3-5", and allocates and returns a struct representing whether + * each given cpu is online. + */ +struct online_cpus *range_to_array(const char *range) +{ + struct online_cpus *cpuarray = (struct online_cpus *) malloc(BUFLEN); + int start, end; + int inrange = 0; + char *token, *tmp; + int done = 0; + tmp = strdup(range); + strcpy(tmp, range); + token = tmp; + + if (*tmp == '\0') { + /* empty string, no online cpus */ + cpuarray->numcpus = 0; + return cpuarray; + } + + while (1) { + tmp++; + if (*tmp == '\0') + done = 1; + if (done || (*tmp == ',')) { + /* expect single value or ending a range */ + if (!token) { + ERR_LOG("format error, missing token, unable to parse range\n"); + goto error; + } + *tmp = '\0'; + end = atoi(token); + token = 0; + if (inrange) { + int i; + for (i=start; i<= end; i++) + cpuarray->status[i] = 1; + inrange = 0; + } else { + cpuarray->status[end] = 1; + } + } else if (*tmp == '-') { + if (inrange) { + ERR_LOG("format error, unable to parse range\n"); + goto error; + } + if (!token) { + ERR_LOG("format error, missing token, unable to parse range\n"); + goto error; + } + *tmp = '\0'; + start = atoi(token); + token = 0; + inrange = 1; + } else { + /* expect a numerical value */ + if ((*tmp < '0') || (*tmp > '9')) { + ERR_LOG("format error, expected a numerical value, unable to parse range\n"); + goto error; + } + + if (!token) + token = tmp; + } + if (done) + break; + } + cpuarray->numcpus = end+1; + return cpuarray; +error: + free(cpuarray); + return 0; +} + + +struct json_object *new_json_obj_from_array (struct online_cpus *cpuarray) +{ + int i; + + struct json_object *jobj_array = json_object_new_array(); + if (jobj_array == NULL) { + return NULL; + } + + for (i=0;inumcpus;i++) { + if (cpuarray->status[i]) { + json_object_array_add(jobj_array, json_object_new_int(i)); + } + } + return jobj_array; +} diff --git a/guest-agent/guest-scale-agent-2.0/parser_test.c b/guest-agent/guest-scale-agent-2.0/parser_test.c new file mode 100644 index 00000000..88cdf1a8 --- /dev/null +++ b/guest-agent/guest-scale-agent-2.0/parser_test.c @@ -0,0 +1,67 @@ +# +# Copyright(c) 2013-2016, Wind River Systems, Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Wind River Systems nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +#include +#include +#include +#include + +void printstruct(struct online_cpus *cpuarray) +{ + int i; + printf("num cpus: %d\n", cpuarray->numcpus); + for (i=0;inumcpus;i++) + printf("%d: %d\n", i, cpuarray->status[i]); +} + + +struct online_cpus cpus = {15, {1,0,0,0,0,0,1,1,1,0,0,0,1,1,1}}; +int main() +{ + char *str, *str2; + struct online_cpus *newcpus; + printf("initial: \n"); + printstruct(&cpus); + printf("string:\n"); + str = "0,6-8,12-14"; + newcpus = range_to_array(str); + printf("fromstring: \n"); + printstruct(newcpus); + newcpus = range_to_array(""); + printf("empty: \n"); + printstruct(newcpus); + newcpus = range_to_array("0--2"); + newcpus = range_to_array("0-1-2"); + newcpus = range_to_array("0,,2"); + newcpus = range_to_array("0,-2"); + newcpus = range_to_array("1,2-a"); + + return 0; +} diff --git a/guest-agent/guest-scale-agent-2.0/scripts/app_scale_helper b/guest-agent/guest-scale-agent-2.0/scripts/app_scale_helper new file mode 100755 index 00000000..71bd3898 --- /dev/null +++ b/guest-agent/guest-scale-agent-2.0/scripts/app_scale_helper @@ -0,0 +1,107 @@ +#!/bin/bash +# +# Copyright(c) 2013-2016, Wind River Systems, Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Wind River Systems nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# This is a basic sample script showing what the script needs to be able to do. +# The script can be edited as needed or replaced by a script or binary that +# works in the same way, or can be deleted entirely if the default behaviour +# of offlining the highest-numbered CPU is satisfactory and you don't care +# about logging scaling events. +# +# We expect to be called in one of the following ways: +# +# "--cpu_add " +# +# This indicates that "cpu" has been set online, and we can now make use of it. +# Do whatever is necessary to start using it, then return 0. +# +# +# "--cpu_del" +# In this case we pick an online cpu to offline, and do whatever we need to in +# order to stop using it, and return the cpu number. Note that CPU 0 must +# always remain online. + +info_log() +{ + logger -p daemon.info -t $0 "$*" + echo $* +} + +err_log() +{ + logger -p daemon.err -t $0 "$*" + echo $* +} + +usage() +{ + err_log "usage: $0 {--cpu_del | --cpu_add }" + exit -1 +} + +cpu_scale_up () +{ + CPU_NUM=$1 + CPU_ONLINE_RANGE=$2 + info_log "cpu add notification, cpu: ${CPU_NUM}" + info_log "cpu_online_range: ${CPU_ONLINE_RANGE}" +} + +cpu_scale_down () +{ + # Make sure host & guest views of offline cpus are consistent + /usr/sbin/offline_cpus + + info_log "cpu del request" + #pick the highest online cpu + CPUS_ONLINE=`cat /sys/devices/system/cpu/online` + CPU_NUM=${CPUS_ONLINE##*[,-]} + + info_log "selected cpu ${CPU_NUM} to offline" + return $CPU_NUM +} + +if [ $# -lt 1 ] +then + usage +elif [ $1 = "--cpu_add" ] +then + if [ $# = 3 ] + then + cpu_scale_up $2 $3 + else + err_log "--cpu_add option called with $# args, expected 3" + usage + fi +elif [ $1 = "--cpu_del" ] +then + cpu_scale_down +else + usage +fi diff --git a/guest-agent/guest-scale-agent-2.0/scripts/guest-scale-agent.service b/guest-agent/guest-scale-agent-2.0/scripts/guest-scale-agent.service new file mode 100644 index 00000000..3a98f564 --- /dev/null +++ b/guest-agent/guest-scale-agent-2.0/scripts/guest-scale-agent.service @@ -0,0 +1,13 @@ +[Unit] +Description=Guest Scale Agent +After=cloud-init.service +Wants=guest-agent.service + +[Service] +ExecStart=/usr/sbin/guest_scale_agent +Type=simple +Restart=always +RestartSec=0 + +[Install] +WantedBy=multi-user.target diff --git a/guest-agent/guest-scale-agent-2.0/scripts/init_offline_cpus b/guest-agent/guest-scale-agent-2.0/scripts/init_offline_cpus new file mode 100644 index 00000000..4f2654be --- /dev/null +++ b/guest-agent/guest-scale-agent-2.0/scripts/init_offline_cpus @@ -0,0 +1,64 @@ +#!/bin/sh +# +# Copyright(c) 2013-2016, Wind River Systems, Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Wind River Systems nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +DAEMON=/usr/sbin/offline_cpus +NAME=offline_cpus +DESC="offline cpus marked offline by hypervisor" +ARGS= + +test -f $DAEMON || exit 0 + +case "$1" in + start) + echo -n "running $DESC: $NAME... " + $DAEMON $ARGS + echo "done." + ;; + stop) + echo -n "stopping $DESC: $NAME... " + echo "done." + ;; + status) + echo -n "$NAME is not running" + ;; + restart) + echo "restarting $DESC: $NAME... " + $0 stop + $0 start + echo "done." + ;; + *) + echo "Usage: $0 {start|stop|status|restart}" + exit 1 + ;; +esac + +exit 0 diff --git a/guest-agent/guest-scale-agent-2.0/scripts/offline-cpus.service b/guest-agent/guest-scale-agent-2.0/scripts/offline-cpus.service new file mode 100644 index 00000000..3238d4cd --- /dev/null +++ b/guest-agent/guest-scale-agent-2.0/scripts/offline-cpus.service @@ -0,0 +1,12 @@ +[Unit] +Description=Titanium Cloud agent and helper app to scale VMs up/down +After=cloud-init.service + +[Service] +Type=simple +RemainAfterExit=yes +ExecStart=/etc/init.d/offline_cpus start +ExecStop=/etc/init.d/offline_cpus stop + +[Install] +WantedBy=multi-user.target diff --git a/guest-agent/guest-scale-agent-2.0/scripts/offline_cpus b/guest-agent/guest-scale-agent-2.0/scripts/offline_cpus new file mode 100644 index 00000000..4d3472fe --- /dev/null +++ b/guest-agent/guest-scale-agent-2.0/scripts/offline_cpus @@ -0,0 +1,54 @@ +#!/bin/bash +# +# Copyright(c) 2013-2016, Wind River Systems, Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Wind River Systems nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +# Query the metadata server for the set of cpus that are supposed to be offline +# It will be in the form "set([])" for the empty set, and "set([1, 2, 3])" for the +# non-empty set. +CPUSET_OFFLINE=`wget -t 3 -T 5 -qO - http://169.254.169.254/latest/meta-data/offline_cpuset` +RC=$? +if [ $RC -ne 0 ] +then + logger -p daemon.err "${0}: unable to obtain "offline_cpuset" value. rc: ${RC}" + exit -1 +fi + +OFFLINE_CPUS=`echo ${CPUSET_OFFLINE}|grep -oE [0-9]+` +for CPU in $OFFLINE_CPUS +do + # Offline this cpu to match the underlying virtual machine. + echo 0 > /sys/devices/system/cpu/cpu${CPU}/online + RC=$? + if [ $RC -ne 0 ] + then + logger -p daemon.err "${0}: unable to offline cpu ${CPU}. rc: ${RC}" + exit -1 + fi +done diff --git a/guest-client/LICENSE b/guest-client/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/guest-client/LICENSE @@ -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. diff --git a/guest-client/PKG-INFO b/guest-client/PKG-INFO new file mode 100644 index 00000000..e738a302 --- /dev/null +++ b/guest-client/PKG-INFO @@ -0,0 +1,12 @@ +Metadata-Version: 1.1 +Name: guest-client +Version: 3.0.1 +Summary: Titanium Cloud Platform Guest-Client Heartbeat Package +Home-page: +Author: Windriver +Author-email: info@windriver.com +License: Apache-2.0 + +Description: Guest-Client with heartbeat functionality. + +Platform: UNKNOWN diff --git a/guest-client/centos/build_srpm.data b/guest-client/centos/build_srpm.data new file mode 100644 index 00000000..f3386382 --- /dev/null +++ b/guest-client/centos/build_srpm.data @@ -0,0 +1,3 @@ +SRC_DIR=$CGCS_BASE/mwa-thales/guest-client +COPY_LIST="$SRC_DIR/*" +TIS_PATCH_VER=5 diff --git a/guest-client/centos/guest-client.spec b/guest-client/centos/guest-client.spec new file mode 100644 index 00000000..61e68d86 --- /dev/null +++ b/guest-client/centos/guest-client.spec @@ -0,0 +1,114 @@ +Summary: Guest-Client +Name: guest-client +Version: 3.0.1 +Release: %{tis_patch_ver}%{?_tis_dist} +License: Apache-2.0 +Group: base +Packager: Wind River +URL: unknown + +Source0: %{name}-%{version}.tar.gz +#Source1: guest_heartbeat_msg_defs.h + +%define cgcs_sdk_deploy_dir /opt/deploy/cgcs_sdk + +%bcond_without systemd + +%if %{with systemd} +Requires(post): systemd +Requires(preun): systemd +Requires(postun): systemd +BuildRequires: systemd +%endif + +%package -n guest-client-devel +Summary: Guest-Client - Development files +Group: devel +Provides: guest-client-dev(x86_64) = 3.0.1-r1.0 + +%package -n %{name}-cgts-sdk +Summary: Guest-Client - SDK files +Group: devel +BuildRequires: json-c-devel + +%description +Guest-Client with heartbeat functionality. + +%description -n guest-client-devel +Guest-Client with heartbeat functionality. This package contains symbolic +links, header files, and related items necessary for software development. + +%description -n %{name}-cgts-sdk +Guest-Client SDK files + +%prep +%setup +#%build -C guest-client-3.0.1/guest_client/src/heartbeat +#tar czf %{name}-%{version}.tgz %{name}-%{version} +#find . -name "*.tgz" +#echo `pwd` +# Build for guest-client package +cd guest-client-3.0.1 +make clean +make build sysconfdir=%{_sysconfdir} +make sample +make tar ARCHIVE_NAME=wrs-guest-heartbeat-%{version} +find build +cd .. + +# Install for guest-client package +%install +install -m 750 -d %{buildroot}/usr +install -m 750 -d %{buildroot}%{_includedir} +install -m 750 -d %{buildroot}%{_includedir}/guest-client +install -m 640 -p -D %{name}-%{version}/guest_client/src/heartbeat/guest_heartbeat_msg_defs.h %{buildroot}%{_includedir}/guest-client/guest_heartbeat_msg_defs.h +install -d %{buildroot}%{cgcs_sdk_deploy_dir} +install -m 640 %{name}-%{version}/build/wrs-guest-heartbeat-%{version}.tgz %{buildroot}%{cgcs_sdk_deploy_dir} +%if %{with systemd} +install -m 644 -p -D %{name}-%{version}/guest_client/scripts/guest-client.service %{buildroot}%{_unitdir}/guest-client.service +install -m 744 -p -D %{name}-%{version}/guest_client/scripts/guest-client.systemd %{buildroot}%{_sysconfdir}/guest-client/guest-client.systemd +%endif +install -m 750 -d %{buildroot}%{_sysconfdir}/guest-client/heartbeat +install -m 755 -p -D %{name}-%{version}/guest_client/scripts/guest_heartbeat.conf %{buildroot}%{_sysconfdir}/guest-client/heartbeat/guest_heartbeat.conf +install -m 755 -p -D %{name}-%{version}/guest_client/scripts/sample_event_handling_script %{buildroot}%{_sysconfdir}/guest-client/heartbeat/sample_event_handling_script +install -m 755 -p -D %{name}-%{version}/guest_client/scripts/sample_health_check_script %{buildroot}%{_sysconfdir}/guest-client/heartbeat/sample_health_check_script +install -m 640 -p -D %{name}-%{version}/build/guest-client %{buildroot}/usr/local/bin/guest-client +install -m 640 -p -D %{name}-%{version}/build/libguest_common_api.so.%{version} %{buildroot}/usr/local/lib/libguest_common_api.so.%{version} +install -m 640 -p -D %{name}-%{version}/build/libguest_heartbeat_api.so.%{version} %{buildroot}/usr/local/lib/libguest_heartbeat_api.so.%{version} + +%files +%defattr(755,root,root,-) +%{_sysconfdir}/guest-client/heartbeat/guest_heartbeat.conf +%{_sysconfdir}/guest-client/heartbeat/sample_event_handling_script +%{_sysconfdir}/guest-client/heartbeat/sample_health_check_script +%defattr(644,root,root,-) +/usr/local/lib/libguest_common_api.so.%{version} +/usr/local/lib/libguest_heartbeat_api.so.%{version} +%attr(744,-,-) /usr/local/bin/guest-client +%if %{with systemd} +%{_unitdir}/guest-client.service +%attr(744,-,-) %{_sysconfdir}/guest-client/guest-client.systemd +%endif + +%preun +%if %{with systemd} +/usr/bin/systemctl stop guest-client >/dev/null 2>&1 +%systemd_preun guest-client.service +systemctl reload +%endif + +%post +%if %{with systemd} +%systemd_post guest-client.service +systemctl reload +/usr/bin/systemctl enable guest-client >/dev/null 2>&1 +%endif + +%files -n guest-client-devel + +%defattr(644,root,root,-) + +/usr/include/guest-client/guest_heartbeat_msg_defs.h + +%files -n %{name}-cgts-sdk +%{cgcs_sdk_deploy_dir}/wrs-guest-heartbeat-%{version}.tgz diff --git a/guest-client/guest-client-3.0.1/LICENSE b/guest-client/guest-client-3.0.1/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/guest-client/guest-client-3.0.1/LICENSE @@ -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. diff --git a/guest-client/guest-client-3.0.1/Makefile b/guest-client/guest-client-3.0.1/Makefile new file mode 100755 index 00000000..4dc62d1c --- /dev/null +++ b/guest-client/guest-client-3.0.1/Makefile @@ -0,0 +1,119 @@ +# +# Copyright(c) 2013-2016, Wind River Systems, Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Wind River Systems nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +SHELL = /bin/sh + +## Configuration Directory Variables +prefix := /usr/local +exec_prefix := $(prefix) +sysconfdir := $(prefix)/etc +includedir := $(prefix)/include +libdir := $(exec_prefix)/lib +bindir := $(exec_prefix)/bin + +MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) +CURRENT_DIR := $(patsubst %/,%,$(dir $(MAKEFILE_PATH))) +PARENT_DIR := $(patsubst %/,%,$(dir $(CURRENT_DIR))) +BUILD_DIR := $(CURRENT_DIR)/build +PACKAGE_DIR := $(BUILD_DIR)/package +PACKAGE_ROOT_DIR := $(PACKAGE_DIR)/rootdir + +ARCHIVE_NAME := wrs-guest-client-3.0.1 +ARCHIVE_PKG_NAME := wrs-guest-client-3.0.1_pkg + +INIT_TYPE := sysv + +.PHONY: all build sample clean distclean tar package install uninstall + +all: build + +build: + mkdir -p --mode 755 $(BUILD_DIR) + @(cd guest_client_api; make --no-print-directory build \ + sysconfdir=$(sysconfdir) BUILD_DIR=$(BUILD_DIR)) + @(cd guest_client; make --no-print-directory build \ + sysconfdir=$(sysconfdir) BUILD_DIR=$(BUILD_DIR)) + +tar: + @(mkdir -p --mode 755 $(BUILD_DIR)) + @(ln -sf $(CURRENT_DIR) $(BUILD_DIR)/$(ARCHIVE_NAME)) + @(cd $(BUILD_DIR); tar -pczf $(ARCHIVE_NAME).tgz \ + $(ARCHIVE_NAME)/Makefile \ + $(ARCHIVE_NAME)/LICENSE \ + $(ARCHIVE_NAME)/README \ + $(ARCHIVE_NAME)/README.build \ + $(ARCHIVE_NAME)/README.usage \ + $(ARCHIVE_NAME)/TiS-Guest-Heartbeat-Service.pdf \ + $(ARCHIVE_NAME)/packaging \ + $(ARCHIVE_NAME)/include \ + $(ARCHIVE_NAME)/guest_client \ + $(ARCHIVE_NAME)/guest_client_api \ + --exclude $(ARCHIVE_NAME)/guest_client/build \ + --exclude $(ARCHIVE_NAME)/guest_client/src/test \ + --exclude $(ARCHIVE_NAME)/guest_client_api/build \ + --directory $(BUILD_DIR)) + +sample: + mkdir -p --mode 755 $(BUILD_DIR) + @(cd guest_client_api; make --no-print-directory sample \ + sysconfdir=$(sysconfdir) BUILD_DIR=$(BUILD_DIR)) + @(cd guest_client; make --no-print-directory sample \ + sysconfdir=$(sysconfdir) BUILD_DIR=$(BUILD_DIR)) + +clean: + @-(cd guest_client_api; make --no-print-directory clean BUILD_DIR=$(BUILD_DIR)) + @-(cd guest_client; make --no-print-directory clean BUILD_DIR=$(BUILD_DIR)) + +distclean: clean + +package: + @(mkdir -p --mode 755 $(PACKAGE_DIR)) + @(mkdir -p --mode 755 $(PACKAGE_ROOT_DIR)) + @(ln -sf $(PACKAGE_DIR) $(BUILD_DIR)/$(ARCHIVE_PKG_NAME)) + @(echo "Packaging guest-client.pkg in $(PACKAGE_DIR)") + @(cp $(CURRENT_DIR)/packaging/guest-client.pkg $(PACKAGE_DIR)/guest-client.pkg) + @(chmod 755 $(PACKAGE_DIR)/guest-client.pkg) + @-(cd guest_client_api; make --no-print-directory package BUILD_DIR=$(BUILD_DIR) \ + PACKAGE_DIR=$(PACKAGE_DIR) prefix=$(prefix) exec_prefix=$(exec_prefix) \ + sysconfdir=$(sysconfdir) includedir=$(includedir) libdir=$(libdir) \ + bindir=$(bindir)) + @-(cd guest_client; make --no-print-directory package BUILD_DIR=$(BUILD_DIR) \ + PACKAGE_DIR=$(PACKAGE_DIR) prefix=$(prefix) exec_prefix=$(exec_prefix) \ + sysconfdir=$(sysconfdir) includedir=$(includedir) libdir=$(libdir) \ + bindir=$(bindir)) + @(cd $(BUILD_DIR); tar -pczf $(ARCHIVE_PKG_NAME).tgz \ + $(ARCHIVE_PKG_NAME)/* --directory $(PACKAGE_DIR)) + +install: package + @(echo "Installing ...") + @(cd $(PACKAGE_DIR); ./guest-client.pkg install $(INIT_TYPE) $(DESTDIR)) + +uninstall: package + @(echo "Uninstalling ...") + @(cd $(PACKAGE_DIR); ./guest-client.pkg uninstall $(INIT_TYPE) $(DESTDIR)) diff --git a/guest-client/guest-client-3.0.1/README b/guest-client/guest-client-3.0.1/README new file mode 100755 index 00000000..178d7f58 --- /dev/null +++ b/guest-client/guest-client-3.0.1/README @@ -0,0 +1,72 @@ +Copyright(c) 2013-2017, Wind River Systems, Inc. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of Wind River Systems nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------- + +TITANIUM CLOUD - GUEST HEARTBEAT SDK +=========================================== + +DESCRIPTION +=========== + Titanium Cloud Guest-Client provides a heartbeat service to monitor the + health of guest application(s) within a VM running under WindRiver's + Titanium Cloud. Loss of heartbeat will result in a corrective action + being taken against the VM. The heartbeat interval and corrective action + is specified by the VM. + + Titanium Cloud Guest-Client also provides guest application(s) within + a VM running under WindRiver's Titanium Cloud, the ability to receive + notification of and vote to accept or reject actions about to be performed + against the VM. On notifications, the guest application within the VM can + take this opportunity to cleanly shut down or transfer its service to a + peer VM. + + +-------- Virtual Machine ------------------------+ + | | + | VM-Health-Check-Script | + | / | + | / Guest-Application-1 | + Titanium Cloud <----> TiS-Guest-Client <-----> ... | + | \ Guest-Application-8 | + | \ | + | VM-Event-Script | + | | + +-------------------------------------------------+ + + See also: + + README.build - building and installing. + + README.usage - configuring and using the heartbeat, notification + and voting functionality. + + +DELIVERY +======== + Titanium Cloud Guest-Client is delivered as source in a compressed + tarball, wrs-guest-client-v3.0.0.tgz, such that it can be compiled for + the applicable VM Linux distribution. diff --git a/guest-client/guest-client-3.0.1/README.build b/guest-client/guest-client-3.0.1/README.build new file mode 100755 index 00000000..0e6d4522 --- /dev/null +++ b/guest-client/guest-client-3.0.1/README.build @@ -0,0 +1,291 @@ +Copyright(c) 2013-2017, Wind River Systems, Inc. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of Wind River Systems nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------- + +This file contains instructions for building and installing the Titanium Cloud +Guest-Client. + + +REQUIREMENTS +============ + The Guest-Client uses the SIGIO functionality of the virtio-serial device + to track host connection up, down and port unplug events. This functionality + requires a kernel version equal to or greater than 2.6.37. + + Compilation: + Linux OS ## Same architecture as the VM. + + gnu make + gcc compiler + + development libraries and headers for: + libc ## C library + rt ## Real-Time library + json-c ## JSON-C library + + VM Runtime: + Linux OS + + runtime libraries: + libc ## C library + rt ## Real-Time library + json-c ## JSON-C library + + +COMPILE +======= + Extract the tarball contents ... + + tar -xvf wrs-guest-heartbeat-v3.0.0.tgz + + To compile, simply run ... + + cd wrs-guest-heartbeat-3.0.0 + make + + This will create a build output directory called "build" in the + wrs-guest-heartbeat-3.0.0 directory. The build output directory + contains the following relevant files: + + libguest_common_api.so.3.0.0 + libguest_heartbeat_api.so.3.0.0 + guest-client + + The libguest_common_api library contains all the common code for + the Guest-Client API libraries. + + The libguest_heartbeat_api library contains the heartbeat, voting, + and notification functionality to interact with the Guest-Client. + A guest application wanting to use this functionality needs to link + in the libguest_common_api and libguest_heartbeat_api libraries. + + The guest-client executable is the Guest-Client daemon that heartbeats + guest applications, and orchestrates votes on and notifications of + pending actions against the VM. The Guest-Client reports the health, + notification and voting results of guest application(s) to the + Titanium Cloud. + + The following variables are available to change the build output + directory and where the system configuration files will be located: + + BUILD_DIR - used to change the build output directory, defaults + to the wrs-guest-heartbeat-3.0.0/build directory. + + prefix - used to construct other variables below, defaults to + the /usr/local directory. + + sysconfdir - directory for configuration files, set to + $(prefix)/etc + + Note: all directory variables must specify an absolute path. + + Usage: make = + + To compile the sample-guest-app, simply run ... + + cd wrs-guest-heartbeat-3.0.0 + make sample + + The sample-guest-app executable is for demonstration purposes only. + It demonstrates how a guest application can use the guest heartbeat + library, libguest_heartbeat_api, to interact with the Guest-Client. + + To compile with a different compiler, simply run ... + + cd wrs-guest-heartbeat-3.0.0 + make CC=/x86_64/buildroot/host/usr/bin/x86_64-buildroot-linux-uclibc-cc + + +PACKAGE +======= + To build a tar package for installation, simply run ... + + cd wrs-guest-heartbeat-3.0.0 + make package + + This will create a tar file called wrs-guest-heartbeat-3.0.0_pkg.tgz + in the wrs-guest-heartbeat-3.0.0 build directory. + + If you changed the build output directory during compile, the BUILD_DIR + variable will have to be specified on the make package. Also if you + specified a different system configuration directory, the sysconfdir + variable, during compile, it will also have to be specified on the + make package. + + The following package configuration variables are available to customize + the package for installation: + + prefix - used to construct other variables below, defaults to + the /usr/local directory. + + exec_prefix - used to construct other executable variables, set to + the 'prefix' variable. + + sysconfdir - directory for configuration files, set to + $(prefix)/etc + + includedir - directory for installing header files, set to + $(prefix)/include + + libdir - directory for installing libraries, set to + $(exec_prefix)/lib + + bindir - directory for installing executable programs, set to + $(exec_prefix)/bin + + Note: all directory variables must specify an absolute path. + + Usage: make package = ... + + +INSTALL +======= + To install from source directories, simply run ... + + cd wrs-guest-heartbeat-3.0.0 + make install + + This will install the default configuration file, include files, + compiled libraries and executable into the following directories: + + guest_heartbeat.conf --> /usr/local/etc/guest-client/heartbeat + guest_api_types.h --> /usr/local/include + guest_heartbeat_api.h --> /usr/local/include + libguest_common_api.so.3.0.0 --> /usr/local/lib + libguest_heartbeat_api.so.3.0.0 --> /usr/local/lib + guest-client --> /usr/local/bin + + If you changed the build output directory during compile, the BUILD_DIR + variable will have to be specified on the make install. Also if you + specified a different system configuration directory, the sysconfdir + variable, during compile, it will also have to be specified on the + make install. + + The following install configuration variables are available to customize + installation: + + INIT_TYPE - the type of initialization the OS is using, one of + ( systemd or sysv ). Default to sysv. + + DESTDIR - prepended to each install target file, used to provide + an alternate environment to install in (i.e. support + for staged installs). + + prefix - used to construct other variables below, defaults to + the /usr/local directory. + + exec_prefix - used to construct other executable variables, set to + the 'prefix' variable. + + sysconfdir - directory for configuration files, set to + $(prefix)/etc + + includedir - directory for installing header files, set to + $(prefix)/include + + libdir - directory for installing libraries, set to + $(exec_prefix)/lib + + bindir - directory for installing executable programs, set to + $(exec_prefix)/bin + + Note: all directory variables must specify an absolute path. + + Usage: make = ... + + + + To install from a tar package file, simply run ... + + tar -xvf wrs-guest-heartbeat-v3.0.0_pkg.tgz + + cd wrs-guest-heartbeat-v3.0.0_pkg + guest-client.pkg install + + This will install the default configuration file, include files, + compiled libraries and executable into the following directories: + + guest_heartbeat.conf --> /usr/local/etc/guest-client/heartbeat + guest_api_types.h --> /usr/local/include + guest_heartbeat_api.h --> /usr/local/include + libguest_common_api.so.3.0.0 --> /usr/local/lib + libguest_heartbeat_api.so.3.0.0 --> /usr/local/lib + guest-client --> /usr/local/bin + + To uninstall: guest-client.pkg uninstall + + The following install configuration variables can be provided to + customize installation: + + INIT_TYPE - the type of initialization the OS is using, one of + ( systemd, sysv ). Optional parameter, defaults to + sysv. + + DESTDIR - prepended to each install target file, used to provide + an alternate environment to install in (i.e. support + for staged installs). Optional parameter. + + Note: all directory variables must specify an absolute path. + + guest-client.pkg [DESTDIR] + + +POST-INSTALL +============ + Running in the VM: + + Test if your platform is using systemd. First look for the systemd + process and then verify that the systemctl tool is available + + ps -ef | grep systemd + systemctl --version + + In this case use the systemd instructions, otherwise the sysv + instructions. + + systemd: + As the root user, + systemctl daemon-reload + systemctl start guest-client.service + systemctl enable guest-client.service + + sysv: + As the root user, + chkconfig --add guest-client + /etc/init.d/guest-client start + + Building into the VM Image: + + systemd: + No post install steps required. + + sysv: + ln -s /etc/init.d/guest-client /etc/rc#.d/K##guest-client + ln -s /etc/init.d/guest-client /etc/rc#.d/S##guest-client + + ... and build the VM image. diff --git a/guest-client/guest-client-3.0.1/README.usage b/guest-client/guest-client-3.0.1/README.usage new file mode 100755 index 00000000..a4995221 --- /dev/null +++ b/guest-client/guest-client-3.0.1/README.usage @@ -0,0 +1,385 @@ +Copyright(c) 2013-2017, Wind River Systems, Inc. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of Wind River Systems nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------- + +This file contains instructions for using the Titanium Cloud Guest-Client. + + +Titanium Cloud Setup +===================== + The following steps are required to setup the Titanium Cloud to heartbeat + a VM. + + 1. Create and modify a Flavor for your VM. + + A flavor extraspec, 'Guest Heartbeat', is used to indicate + that VMs of this flavor support Titanium Cloud Guest Heartbeat. + The default value is 'False'. + + If support is indicated, then as soon as the VM's Titanium Cloud + Guest-Client daemon registers with the Titanium Cloud Compute + Services on the compute node host, heartbeating will be enabled. + + a) Create a new flavor: + + via dashboard ... + - Select 'Admin->Flavors' to bring up the list of flavors + - Select '+ Create Flavor' in the upper right. + - Fill in the fields as desired + - Select 'Create Flavor' + + via command line ... + - nova flavor-create ... + + b) Modify the newly created flavor or an existing flavor: + + via dashboard ... + - Select 'Admin->Flavors' to bring up the list of flavors + - Choose a flavor to modify. + - Select the to go to the Flavor Detail page + - Select the Extra Specs TAB + - Select '+ Create' + - Select 'Guest Heartbeat' from pull-down Extra Spec menu + - Check the 'Guest Heartbeat' checkbox + - Select 'Create' + + via command line ... + - nova flavor-key set sw:wrs:guest:heartbeat=True + + Note: already running instances that were launched with this + flavor are NOT affected. + + 2) Launch a new instance of your VM. + + 3) Verify your VM is running with Guest Heartbeat enabled. + + Log into the VM. + + Guest-Client logs are written to syslog's 'daemon' facility, which + are typically logged by the syslog service to /var/log/daemon.log. + Please refer to syslog for details on log settings in order to + determine location of logged Guest-Client messages. + + Guest-Client logs are easy to identify. The logs always contain the + string 'Guest-Client'. A recursive grep of /var/log is one way to + determine where your syslog is sending the Guest-Client logs. + + LOG=`grep -r -l 'Guest-Client' /var/log` + echo $LOG + + /var/log/daemon.log + + A successful connection can be verified by looking for the + following log. + + grep "Guest-Client" $USER_LOG | grep "heartbeat state change" + + Guest-Client heartbeat state change from enabling to enabled + + +VM Setup +======== + Configuring Guest-Client Initialization/Start Scripts + ----------------------------------------------------- + The Titanium Cloud communicates with the Guest-Client through a character + device. The packaged initialization/startup scripts need to be updated to + specify the character device exposed by QEMU to the VM. + + +-- Virtual Machine ---+ + | | + | | + Titanium Cloud <-------------------> QEMU <------------> Guest-Client | + unix-stream-socket char-device | + | | + +----------------------+ + + The variable that needs updating in the initialization/start scripts is + called GUEST_CLIENT_DEVICE. + + Also the location of the Guest-Client binary needs to be updated in the + initialization/start scripts. The variable that needs updating is called + GUEST_CLIENT. + + + Configuring Guest Heartbeat & Application Health Check + ------------------------------------------------------ + The Guest-Client within your VM will register with the Titanium Cloud + Compute Services on the compute node host. Part of that registration + process is the specification of a heartbeat interval and a corrective + action for a failed/unhealthy VM. The values of heartbeat interval and + corrective action come from the guest_heartbeat.conf file and is located + in /etc/guest-client/heartbeat directory by default. + + Guest heartbeat works on a challenge response model. The Titanium + Server Compute Services on the compute node host will challenge the + Guest-Client daemon with a message each interval. The Guest-Client + must respond prior to the next interval with a message indicating good + health. If the Titanium Cloud Compute Services does not receive a valid + response, or if the response specifies that the VM is in ill health, then + corrective action is taken. + + The mechanism can be extended by allowing additional VM resident application + specific scripts and processes, to register for heartbeating. Each script + or process can specify its own heartbeat interval, and its own corrective + action to be taken against the VM as a whole. On ill health the Guest-Client + reports ill health to the Titanium Cloud Compute Services on the compute node + host on the next challenge, and provoke the corrective action. + + This mechanism allows for detection of a failed or hung QEMU/KVM instance, + or a failure of the OS within the VM to schedule the Guest-Client process + or to route basic IO, or an application level error/failure. + + Configuring the Guest-Client Heartbeat & Application Health Check ... + + The heartbeat interval defaults to every second and can be overridden + by the VM in the guest_heartbeat.conf. + + /etc/guest-client/heartbeat/guest_heartbeat.conf: + ## This specifies the interval between heartbeats in milliseconds between the + ## guest-client heartbeat and the Titanium Cloud Compute Services on the + ## compute node host. + HB_INTERVAL=1000 + + The corrective action defaults to 'reboot' and can be overridden by the + VM in the guest_heartbeat.conf. + + /etc/guest-client/heartbeat/guest_heartbeat.conf: + ## This specifies the corrective action against the VM in the case of a + ## heartbeat failure between the guest-client and Titanium Cloud Compute + ## Services on the compute node host and also when the health script + ## configured below fails. + ## + ## Your options are: + ## "log" Only a log is issued. + ## "reboot" Issue a reboot against this VM. + ## "stop" Issue a stop against this VM. + ## + CORRECTIVE_ACTION="reboot" + + A health check script can be registered to run periodically to verify + the health of the VM. This is specified in the guest_heartbeat.conf. + + /etc/guest-client/heartbeat/guest_heartbeat.conf: + ## The Path to the health check script. This is optional. + ## The script will be called periodically to check for the health of the VM. + ## The health check interval is specified in seconds. + HEALTH_CHECK_INTERVAL=30 + HEALTH_CHECK_SCRIPT="/etc/guest-client/heartbeat/sample_health_check_script" + + + Configuring Guest Notifications and Voting + ------------------------------------------ + The Guest-Client running in the VM can be used as a conduit for + notifications of VM lifecycle events being taken by the Titanium Cloud that + will impact this VM. Reboots, pause/resume and migrations are examples of + the types of events your VM can be notified of. Depending on the event, a + vote on the event maybe required before a notification is sent. Notifications + may precede the event, follow it or both. The full table of events and + notifications is found below. + + Titanium Action Event Name Vote* Pre-notification Post-notification Timeout + --------------- ----------------- ---- ---------------- ----------------- ------- + stop stop yes yes no shutdown + reboot reboot yes yes no shutdown + pause pause yes yes no suspend + unpause unpause no no yes resume + suspend suspend yes yes no suspend + resume resume no no yes resume + resize resize_begin yes yes no suspend + resize_end no no yes resume + live-migrate live_migrate_begin yes yes no suspend + live_migrate_end no no yes resume + cold-migrate cold_migrate_begin yes yes no suspend + cold_migrate_end no no yes resume** + + * voting has its own timeout called 'vote' that is event independent. + ** after VM reboot and reconnection which is subject to the 'restart' timeout. + + Notifications are an opportunity for the VM to take preparatory actions + in anticipation of the forthcoming event, or recovery actions after + the event has completed. A few examples + - A reboot or stop notification might allow the application to stop + accepting transactions and cleanly wrap up existing transactions. + - A 'resume' notification after a suspend might trigger a time + adjustment. + - Pre and post migrate notifications might trigger the application + to de-register and then re-register with a network load balancer. + + If you register a notification handler, it will receive all events. If + an event is not of interest, it should return immediately with a + successful return code. + + A process may only register a single notification handler. However + multiple processes may independently register handlers. Also a script + based handler may be registered via the guest_heartbeat.conf. When + multiple processes and scripts register notification handlers, they + will be run in parallel. + + Notifications are subject to configurable timeouts. Timeouts are + specified by each registered process and in the guest_heartbeat.conf. + The timeouts in the guest_heartbeat.conf govern the maximum time all + registered notification handlers have to complete. + + While pre-notification handlers are running, the event will be delayed. + If the timeout is reached, the event will be allowed to proceed. + + While post-notification handlers are running, or waiting to be run, + the Titanium Cloud will not be able to declare the action complete. + Keep in mind that many events that offer a post notification will + require the VM's Guest-Client to reconnect to the compute host, and + that may be further delayed while the VM is rebooted as in a cold + migration. When post-notification is finally triggered, it is subject + to a timeout as well. If the timeout is reached, the event will be + declared complete. + + NOTE: A post-event notification that follows a reboot, as in the + cold_migrate_end event, is a special case. It will be triggered as + soon as the local heartbeat server reconnects with the compute host, + and likely before any processes have a chance to register a handler. + The only handler guaranteed to see such a notification is a script + directly registered by the Guest-Client itself via guest_heartbeat.conf. + + + In addition to notifications, there is also an opportunity for the VM + to vote on any proposed event. Voting precedes all notifications, + and offers the VM a chance to reject the event the Titanium Cloud wishes + to initiate. If multiple handlers are registered, it only takes one + rejection to abort the event. + + The same handler that handles notifications also handles voting. + + Voting is subject to a configurable timeout. The same timeout applies + regardless of the event. The timeout is specified when the Guest-Client + registers with compute services on the host. The timeout is specified in + the guest_heartbeat.conf file. This timeout governs the maximum time all + registered voting handlers have to complete the vote. + + Any voters that fail to vote within the timeout are assumed to have agreed + with the proposed action. + + Rejecting an event should be the exception, not the rule, reserved for + cases when the VM is handling an exceptionally sensitive operation, + as well as a slow operation that can't complete in the notification timeout. + An example + - an active-standby application deployment (1:1), where the active + rejects a shutdown or pause or ... due to its peer standby is not + ready or synchronized. + + A vote handler should generally not take any action beyond returning its + vote. Just because you vote to accept, doesn't mean all your peers + will also accept (i.e. the event might not happen). Taking an action + against an event that never happens is almost certainly NOT what you want. + Instead save your actions for the notification that follows if no one + rejects. The one exception might be to temporarily block the initiation of + any new task that would cause you to vote to reject an event in the near + future. The theory being that the requester of the event may retry in + the near future. + + The Titanium Cloud is not required to offer a vote. Voting may be + bypassed on certain recovery scenarios. + + Configuring Guest-Client Notification and Voting ... + + ## The overall time to vote in seconds regardless of the event being voted + ## upon. It should reflect the slowest of all expected voters when in a sane + ## and healthy condition, plus some allowance for scheduling and messaging. + VOTE=8 + + ## The overall time to handle a stop or reboot notification in seconds. + ## It should reflect the slowest of all expected notification handlers + ## when in a sane and healthy condition, plus some allowance for scheduling + ## and messaging. + SHUTDOWN_NOTICE=8 + + ## The overall time to handle a pause, suspend or migrate-begin notification + ## in seconds. It should reflect the slowest of all expected notification + ## handlers when in a sane and healthy condition, plus some allowance for + ## scheduling and messaging. + SUSPEND_NOTICE=8 + + ## The overall time to handle an unpause, resume or migrate-end notification + ## in seconds. It should reflect the slowest of all expected notification + ## handlers when in a sane and healthy condition, plus some allowance for + ## scheduling and messaging. It does not include reboot time. + RESUME_NOTICE=13 + + ## The overall time to reboot, up to the point the guest-client heartbeat + ## starts in seconds. Allow for some I/O contention. + RESTART=300 + + ## The Path to the event notification script. This is optional. + ## The script will be called when an action is initiated that will impact + ## the VM. + ## + ## The event handling script is invoked with two parameters: + ## + ## event_handling_script + ## + ## MSG_TYPE is one of: + ## 'revocable' Indicating a vote is called for. Return zero to accept, + ## non-zero to reject. For a rejection, the first line of + ## stdout emitted by the script will be captured and logged + ## logged indicating why the event was rejected. + ## + ## 'irrevocable' Indicating this is a notification only. Take preparatory + ## actions and return zero if successful, or non-zero on + ## failure. For a failure, the first line of stdout + ## emitted by the script will be captured and logged + ## indicating the cause of the failure. + ## + ## EVENT is one of: ( 'stop', 'reboot', 'pause', 'unpause', 'suspend', + ## 'resume', 'live_migrate_begin', + ## 'live_migrate_end', 'cold_migrate_begin', + ## 'cold_migrate_end' ) + ## + EVENT_NOTIFICATION_SCRIPT="/etc/guest-client/heartbeat/sample_event_handling_script" + + +VM Application Setup +==================== + An application running in the VM may wish to register directly for voting + and notifications. See the guest_heartbeat_api.h for more details. A + working example can be found in the guest_client_api source directory in the + sample_guest_app.c file. + + To compile the sample-guest-app run ... + cd wrs-guest-heartbeat-3.0.0 + make sample + + This will create an executable called sample-guest-app in the 'build' + directory. + + When compiling the guest application ... + + include headers: + #include + #include + #include + + link with: + -lguest_common_api -lguest_heartbeat_api diff --git a/guest-client/guest-client-3.0.1/TiS-Guest-Heartbeat-Service.doc b/guest-client/guest-client-3.0.1/TiS-Guest-Heartbeat-Service.doc new file mode 100644 index 00000000..17bf86ca Binary files /dev/null and b/guest-client/guest-client-3.0.1/TiS-Guest-Heartbeat-Service.doc differ diff --git a/guest-client/guest-client-3.0.1/TiS-Guest-Heartbeat-Service.pdf b/guest-client/guest-client-3.0.1/TiS-Guest-Heartbeat-Service.pdf new file mode 100644 index 00000000..f2e90b7c Binary files /dev/null and b/guest-client/guest-client-3.0.1/TiS-Guest-Heartbeat-Service.pdf differ diff --git a/guest-client/guest-client-3.0.1/guest_client/Makefile b/guest-client/guest-client-3.0.1/guest_client/Makefile new file mode 100755 index 00000000..5bccc9fc --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/Makefile @@ -0,0 +1,84 @@ +# +# Copyright(c) 2013-2016, Wind River Systems, Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Wind River Systems nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +SHELL = /bin/sh + +## Configuration Directory Variables +prefix := /usr/local +exec_prefix := $(prefix) +sysconfdir := $(prefix)/etc +includedir := $(prefix)/include +libdir := $(exec_prefix)/lib +bindir := $(exec_prefix)/bin + +MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) +CURRENT_DIR := $(patsubst %/,%,$(dir $(MAKEFILE_PATH))) +BUILD_DIR := $(CURRENT_DIR)/build +CONFIG_DIR := $(sysconfdir)/guest-client/heartbeat +PACKAGE_DIR := $(BUILD_DIR)/package +PACKAGE_ROOT_DIR := $(PACKAGE_DIR)/rootdir + +.PHONY: all build sample clean distclean package + +all: build + +build: + mkdir -p --mode 755 $(BUILD_DIR) + @(cd src; make --no-print-directory build \ + sysconfdir=$(sysconfdir) BUILD_DIR=$(BUILD_DIR)) + +sample: + @: + +clean: + @-(cd src; make --no-print-directory clean BUILD_DIR=$(BUILD_DIR)) + +distclean: clean + +package: + @(mkdir -p --mode 755 $(PACKAGE_DIR)) + @(echo "Packaging guest-client.init in $(PACKAGE_DIR)") + @(cp $(CURRENT_DIR)/scripts/guest-client.init $(PACKAGE_DIR)/guest-client.init) + @(chmod 755 $(PACKAGE_DIR)/guest-client.init) + @(echo "Packaging guest-client.service in $(PACKAGE_DIR)") + @(cp $(CURRENT_DIR)/scripts/guest-client.service $(PACKAGE_DIR)/guest-client.service) + @(chmod 644 $(PACKAGE_DIR)/guest-client.service) + @(echo "Packaging guest-client.systemd in $(PACKAGE_DIR)") + @(cp $(CURRENT_DIR)/scripts/guest-client.systemd $(PACKAGE_DIR)/guest-client.systemd) + @(mkdir -p --mode 755 $(PACKAGE_ROOT_DIR)/$(CONFIG_DIR)) + @(echo "Packaging guest_heartbeat.conf in $(PACKAGE_ROOT_DIR)/$(CONFIG_DIR)") + @(cp $(CURRENT_DIR)/scripts/guest_heartbeat.conf $(PACKAGE_ROOT_DIR)/$(CONFIG_DIR)/guest_heartbeat.conf) + @(echo "Packaging sample_event_handling_script in $(PACKAGE_ROOT_DIR)/$(CONFIG_DIR)") + @(cp $(CURRENT_DIR)/scripts/sample_event_handling_script $(PACKAGE_ROOT_DIR)/$(CONFIG_DIR)/sample_event_handling_script) + @(echo "Packaging sample_health_check_script in $(PACKAGE_ROOT_DIR)/$(CONFIG_DIR)") + @(cp $(CURRENT_DIR)/scripts/sample_health_check_script $(PACKAGE_ROOT_DIR)/$(CONFIG_DIR)/sample_health_check_script) + @-(cd src; make --no-print-directory package BUILD_DIR=$(BUILD_DIR) \ + PACKAGE_DIR=$(PACKAGE_DIR) prefix=$(prefix) exec_prefix=$(exec_prefix) \ + sysconfdir=$(sysconfdir) includedir=$(includedir) libdir=$(libdir) \ + bindir=$(bindir)) diff --git a/guest-client/guest-client-3.0.1/guest_client/scripts/guest-client.init b/guest-client/guest-client-3.0.1/guest_client/scripts/guest-client.init new file mode 100755 index 00000000..f4cc6df0 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/scripts/guest-client.init @@ -0,0 +1,183 @@ +#! /bin/sh +# +# Copyright(c) 2013-2016, Wind River Systems, Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Wind River Systems nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +# +# chkconfig: - 96 96 +# +### BEGIN INIT INFO +# Short-Description: Guest-Client +# Provides: guest-client +# Default-Start: 3 5 +# Default-Stop: 0 1 2 6 +# Required-Start: +# Required-Stop: +### END INIT INFO + +# Assumes that LSB (Linux Standard Base) scripts are available. On Fedora and +# other Red Hat related distros, the redhat-lsb package is optional. +if [ -f /etc/init.d/functions ] +then + . /etc/init.d/functions +else + . /lib/lsb/init-functions +fi + +# LSB Exit Codes +# 0 - success +# 1 - generic or unspecified error +# 2 - invalid or excess arguments +# 3 - unimplemented feature +# 4 - user had insufficient privileges +# 5 - program is not installed +# 6 - program is not configured +# 7 - program is not running +# +# LSB Exit Status Codes +# 0 - program is running or service OK +# 1 - program is dead and /var/run pid file exists +# 2 - program is dead and /var/lock lock file exists +# 3 - program is not running +# 4 - program or service status is unknown +# +RETVAL=0 + +# Update binary location and device to be used, if necessary. +GUEST_CLIENT_NAME="guest-client" +GUEST_CLIENT_PIDFILE="/var/run/${GUEST_CLIENT_NAME}.pid" +GUEST_CLIENT="/usr/bin/${GUEST_CLIENT_NAME}" +GUEST_CLIENT_DEVICE="/dev/virtio-ports/cgcs.heartbeat" + +if [ ! -e "${GUEST_CLIENT}" ] +then + echo "${GUEST_CLIENT} is missing" + exit 5 +fi + +PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin + +case "$1" in + start) + args="" + if [ -n ${GUEST_CLIENT_DEVICE} ] + then + args="--device ${GUEST_CLIENT_DEVICE}" + fi + + echo -n "Starting ${GUEST_CLIENT_NAME}: " + if [ -n "`pidof ${GUEST_CLIENT}`" ] + then + # Something might have started guest-client already. + RETVAL=0 + else + start-stop-daemon --start --background --make-pidfile --pidfile ${GUEST_CLIENT_PIDFILE} --exec ${GUEST_CLIENT} -- ${args} + RETVAL=$? + fi + if [ ${RETVAL} -eq 0 ] + then + echo "OK" + else + echo "FAIL" + RETVAL=1 + fi + ;; + + stop) + echo -n "Stopping ${GUEST_CLIENT_NAME}: " + if [ -n "`pidof ${GUEST_CLIENT}`" ] + then + killproc ${GUEST_CLIENT} + fi + + SHUTDOWN_TIMEOUT=10 + count=0 + while [ ${count} -lt ${SHUTDOWN_TIMEOUT} ] + do + pidof ${GUEST_CLIENT} &> /dev/null + rc=$? + if [ ${rc} -eq 1 ] + then + echo "OK" + break + fi + count=`expr ${count} + 1` + sleep 1 + done + + pidof ${GUEST_CLIENT} &> /dev/null + rc=$? + if [ ${rc} -eq 0 ] + then + echo "FAIL" + RETVAL=1 + fi + + rm -f ${GUEST_CLIENT_PIDFILE} + ;; + + status) + pid=`cat ${GUEST_CLIENT_PIDFILE} 2>/dev/null` + if [ -n "${pid}" ] + then + if ps -p ${pid} &>/dev/null + then + echo "${GUEST_CLIENT_NAME} is running" + RETVAL=0 + else + echo "${GUEST_CLIENT_NAME} is not running but has pid file" + RETVAL=1 + fi + else + echo "${GUEST_CLIENT_NAME} is not running" + RETVAL=3 + fi + ;; + + restart) + $0 stop + sleep 1 + $0 start + ;; + + reload) + pid=`cat ${GUEST_CLIENT_PIDFILE} 2>/dev/null` + if [ -n "${pid}" ] + then + echo "${GUEST_CLIENT_NAME} reload" + kill -HUP ${pid} + fi + ;; + + *) + echo "usage: $0 { start | stop | status | restart | reload }" + ;; +esac + +exit ${RETVAL} diff --git a/guest-client/guest-client-3.0.1/guest_client/scripts/guest-client.service b/guest-client/guest-client-3.0.1/guest_client/scripts/guest-client.service new file mode 100755 index 00000000..837a617f --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/scripts/guest-client.service @@ -0,0 +1,57 @@ +# +# Copyright(c) 2013-2016, Wind River Systems, Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Wind River Systems nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +[Unit] +Description=Titanium Guest-Client +After=network.target + +[Service] +Type=forking +PIDFile=/var/run/guest-client.pid +EnvironmentFile=/etc/guest-client/heartbeat/guest_heartbeat.conf +ExecStart=/etc/guest-client/guest-client.systemd start +ExecStop=/etc/guest-client/guest-client.systemd stop +ExecReload=/etc/guest-client/guest-client.systemd restart +# We want systemd to give guest-client some time to finish gracefully, but +# still want it to kill guest-client after TimeoutStopSec if something went +# wrong during the graceful stop. Normally, systemd sends SIGTERM signal right +# after the ExecStop, which would kill heartbeat. We are sending useless SIGCONT +# here to give guest-client time to finish. +KillSignal=SIGCONT +# Exit code of 6 indicates it is not configured and no restart. +# if /dev/virtio-ports/cgcs.heartbeat is not found, guest-client +# returns 6 on exit. We don't want to keep restarting guest-client +# if it is not configured. +RestartPreventExitStatus=6 +Restart=always +RestartSec=15 + +[Install] +WantedBy=multi-user.target diff --git a/guest-client/guest-client-3.0.1/guest_client/scripts/guest-client.systemd b/guest-client/guest-client-3.0.1/guest_client/scripts/guest-client.systemd new file mode 100755 index 00000000..ba688efb --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/scripts/guest-client.systemd @@ -0,0 +1,171 @@ +#! /bin/sh +# +# Copyright(c) 2013-2016, Wind River Systems, Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Wind River Systems nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +# Assumes that LSB (Linux Standard Base) scripts are available. On Fedora and +# other Red Hat related distros, the redhat-lsb package is optional. +if [ -f /etc/init.d/functions ] +then + . /etc/init.d/functions +else + . /lib/lsb/init-functions +fi + +# LSB Exit Codes +# 0 - success +# 1 - generic or unspecified error +# 2 - invalid or excess arguments +# 3 - unimplemented feature +# 4 - user had insufficient privileges +# 5 - program is not installed +# 6 - program is not configured +# 7 - program is not running +# +# LSB Exit Status Codes +# 0 - program is running or service OK +# 1 - program is dead and /var/run pid file exists +# 2 - program is dead and /var/lock lock file exists +# 3 - program is not running +# 4 - program or service status is unknown +# +RETVAL=0 + +# Update binary location and device to be used, if necessary. +GUEST_CLIENT_NAME="guest-client" +GUEST_CLIENT_PIDFILE="/var/run/${GUEST_CLIENT_NAME}.pid" +GUEST_CLIENT="/usr/local/bin/${GUEST_CLIENT_NAME}" +GUEST_CLIENT_DEVICE="/dev/virtio-ports/cgcs.heartbeat" + +if [ ! -e "${GUEST_CLIENT}" ] +then + echo "${GUEST_CLIENT} is missing" + exit 5 +fi + +PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin + +case "$1" in + start) + args="" + if [ -n ${GUEST_CLIENT_DEVICE} ] + then + args="--device ${GUEST_CLIENT_DEVICE}" + fi + + echo -n "Starting ${GUEST_CLIENT_NAME}: " + if [ -n "`pidof ${GUEST_CLIENT}`" ] + then + # Something might have started guest-client already. + RETVAL=0 + else + start-stop-daemon --start --background --make-pidfile --pidfile ${GUEST_CLIENT_PIDFILE} --exec ${GUEST_CLIENT} -- ${args} + RETVAL=$? + fi + if [ ${RETVAL} -eq 0 ] + then + echo "OK" + else + echo "FAIL" + RETVAL=1 + fi + ;; + + stop) + echo -n "Stopping ${GUEST_CLIENT_NAME}: " + if [ -n "`pidof ${GUEST_CLIENT}`" ] + then + killproc ${GUEST_CLIENT} + fi + + SHUTDOWN_TIMEOUT=10 + count=0 + while [ ${count} -lt ${SHUTDOWN_TIMEOUT} ] + do + pidof ${GUEST_CLIENT} &> /dev/null + rc=$? + if [ ${rc} -eq 1 ] + then + echo "OK" + break + fi + count=`expr ${count} + 1` + sleep 1 + done + + pidof ${GUEST_CLIENT} &> /dev/null + rc=$? + if [ ${rc} -eq 0 ] + then + echo "FAIL" + RETVAL=1 + fi + + rm -f ${GUEST_CLIENT_PIDFILE} + ;; + + status) + pid=`cat ${GUEST_CLIENT_PIDFILE} 2>/dev/null` + if [ -n "${pid}" ] + then + if ps -p ${pid} &>/dev/null + then + echo "${GUEST_CLIENT_NAME} is running" + RETVAL=0 + else + echo "${GUEST_CLIENT_NAME} is not running but has pid file" + RETVAL=1 + fi + else + echo "${GUEST_CLIENT_NAME} is not running" + RETVAL=3 + fi + ;; + + restart) + $0 stop + sleep 1 + $0 start + ;; + + reload) + pid=`cat ${GUEST_CLIENT_PIDFILE} 2>/dev/null` + if [ -n "${pid}" ] + then + echo "${GUEST_CLIENT_NAME} reload" + kill -HUP ${pid} + fi + ;; + + *) + echo "usage: $0 { start | stop | status | restart | reload }" + ;; +esac + +exit ${RETVAL} diff --git a/guest-client/guest-client-3.0.1/guest_client/scripts/guest_heartbeat.conf b/guest-client/guest-client-3.0.1/guest_client/scripts/guest_heartbeat.conf new file mode 100755 index 00000000..eb9a898b --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/scripts/guest_heartbeat.conf @@ -0,0 +1,116 @@ +### +# Copyright (c) <2013-2017>, Wind River Systems, Inc. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1) Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2) Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# +# 3) Neither the name of Wind River Systems nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +### + +################################################## +## Heartbeat health check options + +## This specifies the interval between heartbeats in milliseconds between the +## guest-client heartbeat and the Titanium Cloud Compute Services on the +## compute node host. +HB_INTERVAL=1000 + +## This specifies the corrective action against the VM in the case of a +## heartbeat failure between the guest-client and Titanium Cloud Compute +## Services on the compute node host and also when the health script +## configured below fails. +## +## Your options are: +## "log" Only a log is issued. +## "reboot" Issue a reboot against this VM. +## "stop" Issue a stop against this VM. +## +CORRECTIVE_ACTION="reboot" + + +################################################## +## Heartbeat event notification and voting options + +## The overall time to vote in seconds regardless of the event being voted +## upon. It should reflect the slowest of all expected voters when in a sane +## and healthy condition, plus some allowance for scheduling and messaging. +VOTE=8 + +## The overall time to handle a stop or reboot notification in seconds. +## It should reflect the slowest of all expected notification handlers +## when in a sane and healthy condition, plus some allowance for scheduling +## and messaging. +SHUTDOWN_NOTICE=8 + +## The overall time to handle a pause, suspend or migrate-begin notification +## in seconds. It should reflect the slowest of all expected notification +## handlers when in a sane and healthy condition, plus some allowance for +## scheduling and messaging. +SUSPEND_NOTICE=8 + +## The overall time to handle an unpause, resume or migrate-end notification +## in seconds. It should reflect the slowest of all expected notification +## handlers when in a sane and healthy condition, plus some allowance for +## scheduling and messaging. It does not include reboot time. +RESUME_NOTICE=13 + +## The overall time to reboot, up to the point the guest-client heartbeat +## starts in seconds. Allow for some I/O contention. +RESTART=600 + + +################################################## +## The Path to the health check script. This is optional. +## The script will be called periodically to check for the health of the VM. +## The health check interval is specified in seconds. +HEALTH_CHECK_INTERVAL=30 +HEALTH_CHECK_SCRIPT="/etc/guest-client/heartbeat/sample_health_check_script" + + +################################################## +## The Path to the event notification script. This is optional. +## The script will be called when an action is initiated that will impact +## the VM. +## +## The event handling script is invoked with two parameters: +## +## event_handling_script +## +## MSG_TYPE is one of: +## 'revocable' Indicating a vote is called for. Return zero to accept, +## non-zero to reject. For a rejection, the first line of +## stdout emitted by the script will be captured and logged +## logged indicating why the event was rejected. +## +## 'irrevocable' Indicating this is a notification only. Take preparatory +## actions and return zero if successful, or non-zero on +## failure. For a failure, the first line of stdout +## emitted by the script will be captured and logged +## indicating the cause of the failure. +## +## EVENT is one of: ( 'stop', 'reboot', 'pause', 'unpause', 'suspend', +## 'resume', 'live_migrate_begin', +## 'live_migrate_end', 'cold_migrate_begin', +## 'cold_migrate_end' ) +## +EVENT_NOTIFICATION_SCRIPT="/etc/guest-client/heartbeat/sample_event_handling_script" diff --git a/guest-client/guest-client-3.0.1/guest_client/scripts/sample_event_handling_script b/guest-client/guest-client-3.0.1/guest_client/scripts/sample_event_handling_script new file mode 100755 index 00000000..3113999c --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/scripts/sample_event_handling_script @@ -0,0 +1,165 @@ +#!/bin/sh +# +# Copyright(c) 2013-2016, Wind River Systems, Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Wind River Systems nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +MSG_TYPE=$1 +EVENT=$2 + +FILE="/tmp/event_timeout" +if [ -f $FILE ]; +then + echo "File $FILE exists." + logger "File $FILE exists. Run slowly to trigger timeout" + sleep 300 + logger "$FILE induced delay ends" +else + logger "File $FILE does not exist." +fi + +if [ "$MSG_TYPE" == "revocable" ] +then + # Vote on the proposed action ( 0 == agree, 1 == reject ) + # Take no actions yet. + # If no other parties have rejected the proposed action, an "irrevocable" + # notification will soon follow. + + if [ $EVENT == "stop" ] + then + FILE="/tmp/vote_no_to_stop" + + elif [ "$EVENT" == "reboot" ] + then + FILE="/tmp/vote_no_to_reboot" + + elif [ "$EVENT" == "suspend" ] || [ "$EVENT" == "pause" ] + then + FILE="/tmp/vote_no_to_suspend" + + elif [ "$EVENT" == "live_migrate_begin" ] || [ "$EVENT" == "cold_migrate_begin" ] + then + FILE="/tmp/vote_no_to_migrate" + + elif [ "$EVENT" == "resize_begin" ] + then + FILE="/tmp/vote_no_to_resize" + + elif [ "$EVENT" == "live_migrate_end" ] || [ "$EVENT" == "cold_migrate_end" ] || + [ "$EVENT" == "resize_end" ] || [ "$EVENT" == "unpause" ] || + [ "$EVENT" == "resume" ] + then + logger "Should never be asked to vote on $EVENT" + echo "Should never be asked to vote on $EVENT" + exit 1 + fi + + if [ -f $FILE ]; + then + echo "File $FILE exists." + logger "File $FILE exists." + logger "Vote against $EVENT at this time." + + # Vote no to shutdown. + exit 1 + else + echo "File $FILE does not exist." + logger "File $FILE does not exists." + logger "Vote to allow $EVENT" + + # Vote yes to shutdown. + exit 0 + fi + +elif [ "$MSG_TYPE" == "irrevocable" ] +then + + if [ $EVENT == "stop" ] + then + logger "Notification of pending $EVENT received." + # Place clean shutdown actions here. Idea is to hand off activity and + # cleanly stop the payload application. It is not required to stop the + # kernel, an ACPI power-off event will follow. + + elif [ "$EVENT" == "reboot" ] + then + logger "Notification of pending $EVENT received." + # Place clean shutdown actions here. Idea is to hand off activity and + # cleanly stop the application. It is not required to stop/reboot the + # kernel, an ACPI power-off event will follow, and then a power-on + + elif [ "$EVENT" == "pause" ] || [ "$EVENT" == "suspend" ] + then + logger "Notification of pending $EVENT received." + # This VM will be paused or suspended but not rebooted. + # Hand off activity and cleanly stop the application if required. + + elif [ "$EVENT" == "unpause" ] || [ "$EVENT" == "resume" ] + then + logger "Notification of $EVENT completion received." + # This VM has resumed from a paused or suspended state. + # Restart your application and take activity as required. + + elif [ "$EVENT" == "resize_begin" ] + then + logger "Notification of pending resize received. ($EVENT)" + # This VM will soon be resized. + # Hand off activity and cleanly stop the payload application if required. + + elif [ "$EVENT" == "resize_end" ] + then + logger "Notification of resize complete received." + # This VM has been resized. + # Restart your application and/or re-establish your network connectivity + # as required. + + elif [ "$EVENT" == "live_migrate_begin" ] || [ "$EVENT" == "cold_migrate_begin" ] + then + logger "Notification of pending migration received. ($EVENT)" + # This VM will soon relocate to a different host. + # Network connectivity may change. + # Hand off activity and cleanly stop the payload application if required. + + elif [ "$EVENT" == "live_migrate_end" ] || [ "$EVENT" == "cold_migrate_end" ] + then + logger "Notification of migration complete received." + # This VM has finished relocation to a new host. + # Network connectivity may have changed. + # Restart your application and/or re-establish your network connectivity + # as required. + fi + + sleep 1 + exit 0 + +else + echo "Unknown message type '$MSG_TYPE'" + logger "Unknown message type '$MSG_TYPE'" + sleep 1 + exit -1 +fi diff --git a/guest-client/guest-client-3.0.1/guest_client/scripts/sample_health_check_script b/guest-client/guest-client-3.0.1/guest_client/scripts/sample_health_check_script new file mode 100755 index 00000000..72924a48 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/scripts/sample_health_check_script @@ -0,0 +1,42 @@ +#!/bin/sh +# +# Copyright(c) 2013-2016, Wind River Systems, Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Wind River Systems nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +FILE="/tmp/unhealthy" +if [ -f $FILE ]; +then + echo "File $FILE exists." + rm -f $FILE + exit 1 +else + echo "File $FILE does not exist." +fi + +exit 0 diff --git a/guest-client/guest-client-3.0.1/guest_client/src/Makefile b/guest-client/guest-client-3.0.1/guest_client/src/Makefile new file mode 100755 index 00000000..9b3ab8e3 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/Makefile @@ -0,0 +1,94 @@ +# +# Copyright(c) 2013-2016, Wind River Systems, Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Wind River Systems nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +SHELL = /bin/sh + +## Configuration Directory Variables +prefix := /usr/local +exec_prefix := $(prefix) +sysconfdir := $(prefix)/etc +includedir := $(prefix)/include +libdir := $(exec_prefix)/lib +bindir := $(exec_prefix)/bin + +MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) +CURRENT_DIR := $(patsubst %/,%,$(dir $(MAKEFILE_PATH))) + +ifndef BUILD_DIR +$(error BUILD_DIR undefined) +endif + +PACKAGE_DIR := $(BUILD_DIR)/package +PACKAGE_ROOT_DIR := $(PACKAGE_DIR)/rootdir + +.SUFFIXES: +.SUFFIXES: .c .o + +.PHONY: all build heartbeat clean distclean package + +heartbeat_C_SRCS := $(wildcard $(CURRENT_DIR)/heartbeat/*.c) +heartbeat_C_SRCS := $(subst $(CURRENT_DIR)/heartbeat/,,$(heartbeat_C_SRCS)) +heartbeat_C_OBJS := ${heartbeat_C_SRCS:.c=.o} + +program_NAME := guest-client +program_C_INCLUDES := -I$(CURRENT_DIR) -I$(CURRENT_DIR)/heartbeat +program_C_INCLUDES += -I$(CURRENT_DIR)/../../include +program_C_SRCS := $(wildcard *.c) +program_C_OBJS := ${program_C_SRCS:.c=.o} +program_LDLIBS := -lrt +program_BUILD_OBJS := $(addprefix $(BUILD_DIR)/, $(heartbeat_C_OBJS)) +program_BUILD_OBJS += $(addprefix $(BUILD_DIR)/, $(program_C_OBJS)) + +CFLAGS = -g -O2 -Wall -Werror -Wformat -DSYSCONFDIR=$(sysconfdir) + +all: build + +heartbeat: + @(cd heartbeat; make --no-print-directory build \ + sysconfdir=$(sysconfdir) BUILD_DIR=$(BUILD_DIR)) + +%.o: %.c + $(CC) $(CFLAGS) $(program_C_INCLUDES) -c $^ -o $(BUILD_DIR)/$@ + +$(program_NAME): heartbeat $(program_C_OBJS) + $(CC) $(CFLAGS) $(program_BUILD_OBJS) $(program_LDLIBS) \ + -o $(BUILD_DIR)/$(program_NAME) -ljson-c + +build: $(program_NAME) + +clean: + @-($(RM) -Rf $(BUILD_DIR)/*) + +distclean: clean + +package: + @(mkdir -p --mode 755 $(PACKAGE_ROOT_DIR)/$(bindir)) + @(echo "Packaging $(program_NAME) in $(PACKAGE_ROOT_DIR)/$(bindir)") + @(cp $(BUILD_DIR)/$(program_NAME) $(PACKAGE_ROOT_DIR)/$(bindir)/$(program_NAME)) + @(chmod 755 $(PACKAGE_ROOT_DIR)/$(bindir)/$(program_NAME)) diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_channel.c b/guest-client/guest-client-3.0.1/guest_client/src/guest_channel.c new file mode 100755 index 00000000..557baabd --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_channel.c @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_channel.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "guest_limits.h" +#include "guest_types.h" +#include "guest_debug.h" +#include "guest_unix.h" + +typedef struct { + bool inuse; + bool char_device; + int sock; + char dev_name[GUEST_DEVICE_NAME_MAX_CHAR]; +} GuestChannelT; + +static GuestChannelT _channel[GUEST_MAX_CONNECTIONS]; + +// **************************************************************************** +// Guest Channel - Find Empty +// ========================== +static GuestChannelIdT guest_channel_find_empty( void ) +{ + GuestChannelT* channel = NULL; + + unsigned int channel_id; + for (channel_id=0; GUEST_MAX_CONNECTIONS > channel_id; ++channel_id) + { + channel = &(_channel[channel_id]); + if (!channel->inuse) + return channel_id; + } + + return GUEST_CHANNEL_ID_INVALID; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Channel - Find +// ==================== +static GuestChannelT* guest_channel_find( GuestChannelIdT channel_id ) +{ + GuestChannelT* channel = NULL; + + if ((0 <= channel_id)&&(GUEST_MAX_CONNECTIONS > channel_id)) + { + channel = &(_channel[channel_id]); + if (channel->inuse) + return channel; + } + + return NULL; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Channel - Send +// ==================== +GuestErrorT guest_channel_send( + GuestChannelIdT channel_id, void* msg, int msg_size ) +{ + GuestChannelT* channel; + ssize_t result; + + channel = guest_channel_find(channel_id); + if (NULL == channel) + { + DPRINTFE("Invalid channel identifier, channel_id=%i.", channel_id); + return GUEST_FAILED; + } + + result = write(channel->sock, msg, msg_size); + if (0 > result) + { + if (ENODEV == errno) + { + DPRINTFI("Channel %i on device %s disconnected.", channel_id, + channel->dev_name); + return GUEST_OKAY; + } else { + DPRINTFE("Failed to write to channel on device %s, error=%s.", + channel->dev_name, strerror(errno)); + return GUEST_FAILED; + } + } + + DPRINTFV("Sent message over channel on device %s.", channel->dev_name); + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Channel - Receive +// ======================= +GuestErrorT guest_channel_receive( + GuestChannelIdT channel_id, char* msg_buf, int msg_buf_size, + int* msg_size ) +{ + GuestChannelT* channel; + ssize_t result; + + channel = guest_channel_find(channel_id); + if (NULL == channel) + { + DPRINTFE("Invalid channel identifier, channel_id=%i.", channel_id); + return GUEST_FAILED; + } + + result = read(channel->sock, msg_buf, msg_buf_size); + if (0 > result) + { + if (EINTR == errno) + { + DPRINTFD("Interrupted on socket read, error=%s.", strerror(errno)); + return GUEST_INTERRUPTED; + + } else if (ENODEV == errno) { + DPRINTFI("Channel %i on device %s disconnected.", channel_id, + channel->dev_name); + *msg_size = 0; + return GUEST_OKAY; + + } else { + DPRINTFE("Failed to read from socket, error=%s.", strerror(errno)); + return GUEST_FAILED; + } + } else if (0 == result) { + DPRINTFD("No message received from socket."); + *msg_size = 0; + return GUEST_OKAY; + + } else { + DPRINTFV("Received message, msg_size=%i.", result); + *msg_size = result; + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Channel - Open +// ==================== +GuestErrorT guest_channel_open( char dev_name[], GuestChannelIdT* channel_id ) +{ + int fd; + int result; + struct stat stat_data; + GuestChannelIdT empty_channel_id; + GuestChannelT* channel; + GuestErrorT error; + + empty_channel_id = guest_channel_find_empty(); + if (GUEST_CHANNEL_ID_INVALID == empty_channel_id) + { + DPRINTFE("Allocation of channel failed, no free resources."); + return GUEST_FAILED; + } + + channel = &(_channel[empty_channel_id]); + memset(channel, 0, sizeof(GuestChannelT)); + + result = stat(dev_name, &stat_data); + if (0 > result) + { + int err = errno; + if (err == ENOENT) + { + DPRINTFI("Failed to stat, error=%s.", strerror(err)); + DPRINTFI("%s file does not exist, guest heartbeat not configured.", + dev_name); + return GUEST_NOT_CONFIGURED; + } + else { + DPRINTFE("Failed to stat, error=%s.", strerror(err)); + return GUEST_FAILED; + } + } + + if (S_ISCHR(stat_data.st_mode)) + { + fd = open(dev_name, O_RDWR); + if (0 > fd) + { + DPRINTFE("Failed to open device %s, error=%s.", dev_name, + strerror(errno)); + return GUEST_FAILED; + } + + result = fcntl(fd, F_SETFD, FD_CLOEXEC); + if (0 > result) + { + DPRINTFE("Failed to set to close on exec, error=%s.", + strerror(errno)); + close(fd); + return GUEST_FAILED; + } + + result = fcntl(fd, F_SETOWN, getpid()); + if (0 > result) + { + DPRINTFE("Failed to set socket ownership, error=%s.", + strerror(errno)); + close(fd); + return GUEST_FAILED; + } + + result = fcntl(fd, F_GETFL); + if (0 > result) + { + DPRINTFE("Failed to get socket options, error=%s.", + strerror(errno)); + close(fd); + return GUEST_FAILED; + } + + result = fcntl(fd, F_SETFL, result | O_NONBLOCK | O_ASYNC); + if (0 > result) + { + DPRINTFE("Failed to set socket options, error=%s.", + strerror(errno)); + close(fd); + return GUEST_FAILED; + } + + DPRINTFI("Opened character device %s", dev_name); + + } else { + error = guest_unix_open(&fd); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to open unix socket %s, error=%s.", + dev_name, guest_error_str(error)); + return error; + } + error = guest_unix_connect(fd, dev_name); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to connect unix socket %s, error=%s.", + dev_name, guest_error_str(error)); + close(fd); + return error; + } + + DPRINTFI("Opened unix socket %s", dev_name); + } + + channel->inuse = true; + snprintf(channel->dev_name, sizeof(channel->dev_name), "%s", dev_name); + channel->char_device = S_ISCHR(stat_data.st_mode); + channel->sock = fd; + *channel_id = empty_channel_id; + + DPRINTFD("Opened channel over device %s.", channel->dev_name); + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Channel - Close +// ===================== +GuestErrorT guest_channel_close( GuestChannelIdT channel_id ) +{ + GuestChannelT* channel; + + channel = guest_channel_find(channel_id); + if (NULL != channel) + { + if (channel->inuse) + { + if (0 <= channel->sock) + close(channel->sock); + + DPRINTFD("Closed channel over device %s.", channel->dev_name); + memset(channel, 0, sizeof(GuestChannelT)); + } + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Channel - Get Selection Object +// ==================================== +int guest_channel_get_selobj( GuestChannelIdT channel_id ) +{ + GuestChannelT *channel; + + channel = guest_channel_find(channel_id); + if (NULL != channel) + return channel->sock; + + return -1; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Channel - Initialize +// ========================== +GuestErrorT guest_channel_initialize( void ) +{ + memset(_channel, 0, sizeof(_channel)); + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Channel - Finalize +// ======================== +GuestErrorT guest_channel_finalize( void ) +{ + memset(_channel, 0, sizeof(_channel)); + return GUEST_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_channel.h b/guest-client/guest-client-3.0.1/guest_client/src/guest_channel.h new file mode 100755 index 00000000..a317cea0 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_channel.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_CHANNEL_H__ +#define __GUEST_CHANNEL_H__ + +#include + +#include "guest_limits.h" +#include "guest_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define GUEST_CHANNEL_ID_INVALID -1 + +typedef int GuestChannelIdT; + +// **************************************************************************** +// Guest Channel - Send +// ==================== +extern GuestErrorT guest_channel_send( + GuestChannelIdT channel_id, void* msg, int msg_size ); +// **************************************************************************** + +// **************************************************************************** +// Guest Channel - Receive +// ======================= +extern GuestErrorT guest_channel_receive( + GuestChannelIdT channel_id, char* msg_buf, int msg_buf_size, + int* msg_size ); +// **************************************************************************** + +// **************************************************************************** +// Guest Channel - Open +// ==================== +extern GuestErrorT guest_channel_open( + char dev_name[], GuestChannelIdT* channel_id ); +// **************************************************************************** + +// **************************************************************************** +// Guest Channel - Close +// ===================== +extern GuestErrorT guest_channel_close( GuestChannelIdT channel_id ); +// **************************************************************************** + +// **************************************************************************** +// Guest Channel - Get Selection Object +// ==================================== +extern int guest_channel_get_selobj( GuestChannelIdT channel_id ); +// **************************************************************************** + +// **************************************************************************** +// Guest Channel - Initialize +// ========================== +extern GuestErrorT guest_channel_initialize( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Channel - Finalize +// ======================== +extern GuestErrorT guest_channel_finalize( void ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_CHANNEL_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_child_death.c b/guest-client/guest-client-3.0.1/guest_client/src/guest_child_death.c new file mode 100755 index 00000000..01ae812a --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_child_death.c @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_child_death.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "guest_limits.h" +#include "guest_types.h" +#include "guest_debug.h" +#include "guest_selobj.h" + +#define GUEST_CHILD_DEATH_MAX_DISPATCH 32 + +typedef struct { + bool valid; + pid_t pid; + int exit_code; +} GuestChildDeathInfoT; + +typedef struct { + bool valid; + pid_t pid; + GuestChildDeathCallbackT death_callback; +} GuestChildDeathCallbackInfoT; + +static int _child_death_fd = -1; +static GuestChildDeathCallbackInfoT _callbacks[GUEST_CHILD_PROCESS_MAX]; +static GuestChildDeathInfoT _child_deaths[GUEST_CHILD_PROCESS_MAX]; +static uint64_t _child_death_count = 0; + +// **************************************************************************** +// Guest Child Death - Register +// ============================ +GuestErrorT guest_child_death_register( + pid_t pid, GuestChildDeathCallbackT callback ) +{ + GuestChildDeathCallbackInfoT* callback_info = NULL; + + unsigned int callbacks_i; + for (callbacks_i=0; GUEST_CHILD_PROCESS_MAX > callbacks_i; ++callbacks_i) + { + callback_info = &(_callbacks[callbacks_i]); + + if (callback_info->valid) + { + if (pid == callback_info->pid) + { + callback_info->death_callback = callback; + break; + } + } else { + callback_info->valid = true; + callback_info->pid = pid; + callback_info->death_callback = callback; + break; + } + } + + if (GUEST_CHILD_PROCESS_MAX <= callbacks_i) + { + DPRINTFE("Failed to register child death callback for pid (%i).", + (int) pid); + return GUEST_FAILED; + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Child Death - Deregister +// ============================== +GuestErrorT guest_child_death_deregister( pid_t pid ) +{ + GuestChildDeathCallbackInfoT* callback_info = NULL; + + unsigned int callbacks_i; + for (callbacks_i=0; GUEST_CHILD_PROCESS_MAX > callbacks_i; ++callbacks_i) + { + callback_info = &(_callbacks[callbacks_i]); + + if (!callback_info->valid) + continue; + + if (pid != callback_info->pid) + continue; + + callback_info->valid = 0; + callback_info->pid = 0; + callback_info->death_callback = NULL; + break; + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Child Death - Save +// ======================== +GuestErrorT guest_child_death_save( pid_t pid, int exit_code ) +{ + uint64_t child_death_count = ++_child_death_count; + GuestChildDeathInfoT* info = NULL; + int result; + + result = write(_child_death_fd, &child_death_count, + sizeof(child_death_count)); + if (0 > result) + DPRINTFE("Failed to signal child death, error=%s", strerror(errno)); + + DPRINTFD("Child process (%i) died.", (int) pid); + + unsigned int death_i; + for (death_i=0; GUEST_CHILD_PROCESS_MAX > death_i; ++death_i) + { + info = &(_child_deaths[death_i]); + + if (info->valid) + { + if (pid == info->pid) + { + info->exit_code = exit_code; + break; + } + } else { + info->valid = true; + info->pid = pid; + info->exit_code = exit_code; + break; + } + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Child Death - Dispatch +// ============================ +static void guest_child_death_dispatch( int selobj ) +{ + static unsigned int _last_entry = 0; + + uint64_t child_death_count; + GuestChildDeathInfoT* info = NULL; + GuestChildDeathCallbackInfoT* callback_info = NULL; + unsigned int num_child_death_dispatched = 0; + int result; + + result = read(_child_death_fd, &child_death_count, sizeof(child_death_count)); + if (0 > result) + { + if (EINTR == errno) + { + DPRINTFD("Interrupted on read, error=%s.", strerror(errno)); + } else { + DPRINTFE("Failed to dispatch, error=%s.", strerror(errno)); + } + } + + unsigned int death_i; + for( death_i=_last_entry; GUEST_CHILD_PROCESS_MAX > death_i; ++death_i ) + { + info = &(_child_deaths[death_i]); + + if (!info->valid) + continue; + + if (0 == info->pid) + continue; + + DPRINTFD("Child process (%i) exited with %i.", (int) info->pid, + info->exit_code); + + unsigned int callbacks_i; + for (callbacks_i=0; GUEST_CHILD_PROCESS_MAX > callbacks_i; ++callbacks_i) + { + callback_info = &(_callbacks[callbacks_i]); + + if (callback_info->valid) + { + if (info->pid == callback_info->pid) + { + if (NULL != callback_info->death_callback) + { + callback_info->death_callback(info->pid, info->exit_code); + callback_info->valid = false; + } + } + } + } + + info->valid = false; + + if (GUEST_CHILD_DEATH_MAX_DISPATCH <= ++num_child_death_dispatched) + DPRINTFD("Maximum child process death dispatches (%i) reached.", + GUEST_CHILD_DEATH_MAX_DISPATCH); + } + + if (GUEST_CHILD_PROCESS_MAX <= death_i) + _last_entry = 0; + else + _last_entry = death_i; + + // Check for outstanding child process deaths to handle. + for (death_i=0; GUEST_CHILD_PROCESS_MAX > death_i; ++death_i) + { + info = &(_child_deaths[death_i]); + + if (!info->valid) + continue; + + if (0 == info->pid) + continue; + + result = write(_child_death_fd, &child_death_count, + sizeof(child_death_count)); + if (0 > result) + DPRINTFE("Failed to signal child process death, error=%s", + strerror(errno)); + break; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Child Death - Initialize +// ============================== +GuestErrorT guest_child_death_initialize( void ) +{ + GuestSelObjCallbacksT callbacks; + GuestErrorT error; + + _child_death_fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + if (0 > _child_death_fd) + { + DPRINTFE("Failed to open child death file descriptor,error=%s.", + strerror(errno)); + return GUEST_FAILED; + } + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.read_callback = guest_child_death_dispatch; + + error = guest_selobj_register(_child_death_fd, &callbacks); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to register selection object, error=%s.", + guest_error_str(error)); + close(_child_death_fd); + _child_death_fd = -1; + return error; + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Child Death - Finalize +// ============================ +GuestErrorT guest_child_death_finalize( void ) +{ + GuestErrorT error; + + if (0 <= _child_death_fd) + { + error = guest_selobj_deregister(_child_death_fd); + if (GUEST_OKAY != error) + DPRINTFE("Failed to deregister selection object, error=%s.", + guest_error_str(error)); + + close(_child_death_fd); + _child_death_fd = -1; + } + + return GUEST_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_child_death.h b/guest-client/guest-client-3.0.1/guest_client/src/guest_child_death.h new file mode 100755 index 00000000..718a2941 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_child_death.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_CHILD_DEATH_H__ +#define __GUEST_CHILD_DEATH_H__ + +#include + +#include "guest_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define GUEST_CHILD_FAILED -65536 + +typedef void (*GuestChildDeathCallbackT) (pid_t pid, int exit_code); + +// **************************************************************************** +// Guest Child Death - Register +// ============================ +extern GuestErrorT guest_child_death_register( + pid_t pid, GuestChildDeathCallbackT callback ); +// **************************************************************************** + +// **************************************************************************** +// Guest Child Death - Deregister +// ============================== +extern GuestErrorT guest_child_death_deregister( pid_t pid ); +// **************************************************************************** + +// **************************************************************************** +// Guest Child Death - Save +// ======================== +extern GuestErrorT guest_child_death_save( pid_t pid, int exit_code ); +// **************************************************************************** + +// **************************************************************************** +// Guest Child Death - Initialize +// ============================== +extern GuestErrorT guest_child_death_initialize( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Child Death - Finalize +// ============================ +extern GuestErrorT guest_child_death_finalize( void ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_CHILD_DEATH_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_client.c b/guest-client/guest-client-3.0.1/guest_client/src/guest_client.c new file mode 100755 index 00000000..75c137ea --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_client.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include + +#include "guest_types.h" +#include "guest_debug.h" +#include "guest_process.h" + +// **************************************************************************** +// Guest Client - Main +// =================== +int main( int argc, char *argv[], char *envp[] ) +{ + GuestErrorT error; + + error = guest_debug_initialize("Guest-Client"); + if (GUEST_OKAY != error) + { + printf("Debug initialization failed, error=%s.\n", + guest_error_str(error)); + return EXIT_FAILURE; + } + + error = guest_process_main(argc, argv, envp); + if (GUEST_OKAY != error) + { + printf("Process failure, error=%s.\n", guest_error_str(error)); + return error; + } + + error = guest_debug_finalize(); + if (GUEST_OKAY != error) + { + printf("Debug finalization failed, error=%s.\n", + guest_error_str(error)); + } + + return EXIT_SUCCESS; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_config.c b/guest-client/guest-client-3.0.1/guest_client/src/guest_config.c new file mode 100755 index 00000000..91a63ffa --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_config.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "guest_types.h" +#include "guest_debug.h" + +#define GUEST_DEFAULT_COMM_DEVICE "/dev/vport1p1" + +static GuestConfigT _config; + +// **************************************************************************** +// Guest Configuration - Get +// ========================= +GuestConfigT* guest_config_get( void ) +{ + return &_config; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Configuration - Show Usage +// ================================ +void guest_config_show_usage( void ) +{ + printf("guest-client [ARGS]\n"); + printf(" where ARGS may be any of: \n"); + printf(" --name Override the name of the instance\n"); + printf(" --device Override default communication channel device\n"); + printf("\n"); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Configuration - Dump +// ========================== +static void guest_config_dump( void ) +{ + DPRINTFI("Guest-Client Configuration:"); + DPRINTFI(" name: %s", _config.name); + DPRINTFI(" device: %s", _config.comm_device); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Configuration - Parse Arguments +// ===================================== +static GuestErrorT guest_config_parse_args( int argc, char *argv[] ) +{ + unsigned int arg_i; + for (arg_i=1; arg_i < argc; ++arg_i) + { + if (0 == strcmp("--name", argv[arg_i])) + { + arg_i++; + if (arg_i < argc) + snprintf(_config.name, sizeof(_config.name), "%s", argv[arg_i]); + + } else if (0 == strcmp("--device", argv[arg_i])) { + arg_i++; + if (arg_i < argc) + snprintf(_config.comm_device, sizeof(_config.comm_device), + "%s", argv[arg_i]); + } + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Configuration - Parse Environment +// ======================================= +static GuestErrorT guest_config_parse_env( char *envp[] ) +{ + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Configuration - Initialize +// ================================ +GuestErrorT guest_config_initialize( int argc, char *argv[], char *envp[] ) +{ + char name[GUEST_NAME_MAX_CHAR]; + GuestErrorT error; + int result; + + result = gethostname(name, sizeof(name)); + if (0 > result) + { + DPRINTFE("Failed to get hostname, error=%s.", strerror(errno)); + return GUEST_FAILED; + } + DPRINTFI("hostname=%s.", name); + memset(&_config, 0, sizeof(GuestConfigT)); + snprintf(_config.name, sizeof(_config.name), "%s", name); + snprintf(_config.comm_device, sizeof(_config.comm_device), "%s", + GUEST_DEFAULT_COMM_DEVICE); + + error = guest_config_parse_args(argc, argv); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to parse arguments, error=%s.", + guest_error_str(error)); + return error; + } + + error = guest_config_parse_env(envp); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to parse environment, error=%s.", + guest_error_str(error)); + return error; + } + + guest_config_dump(); + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Configuration - Finalize +// ============================== +GuestErrorT guest_config_finalize( void ) +{ + return GUEST_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_config.h b/guest-client/guest-client-3.0.1/guest_client/src/guest_config.h new file mode 100755 index 00000000..b32a9245 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_config.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_CONFIGURATION_H__ +#define __GUEST_CONFIGURATION_H__ + +#include "guest_limits.h" +#include "guest_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + char name[GUEST_NAME_MAX_CHAR]; + char comm_device[GUEST_DEVICE_NAME_MAX_CHAR]; +} GuestConfigT; + +// **************************************************************************** +// Guest Configuration - Get +// ========================= +extern GuestConfigT* guest_config_get( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Configuration - Show Usage +// ================================ +extern void guest_config_show_usage( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Configuration - Initialize +// ================================ +extern GuestErrorT guest_config_initialize( + int argc, char *argv[], char *envp[] ); +// **************************************************************************** + +// **************************************************************************** +// Guest Configuration - Finalize +// ============================== +extern GuestErrorT guest_config_finalize( void ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_CONFIGURATION_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_debug.c b/guest-client/guest-client-3.0.1/guest_client/src/guest_debug.c new file mode 100755 index 00000000..4b55d3a0 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_debug.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_debug.h" + +#include +#include +#include + +#include "guest_types.h" + +#define GUEST_DEBUG_WANT_SYSLOG +#ifdef GUEST_DEBUG_WANT_SYSLOG +#include +#endif + +static char _process_name[30]; +static GuestDebugLogLevelT _log_level = GUEST_DEBUG_LOG_LEVEL_INFO; + +// **************************************************************************** +// Guest Debug - Log Level String +// ============================== +const char* guest_debug_log_level_str( GuestDebugLogLevelT level ) +{ + switch (level) { + case GUEST_DEBUG_LOG_LEVEL_ERROR: return "error"; + case GUEST_DEBUG_LOG_LEVEL_INFO: return " info"; + case GUEST_DEBUG_LOG_LEVEL_DEBUG: return "debug"; + case GUEST_DEBUG_LOG_LEVEL_VERBOSE: return " verb"; + default: + return "???"; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Debug - Set Log Level +// =========================== +void guest_debug_set_log_level( GuestDebugLogLevelT level ) +{ + _log_level = level; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Debug - Want Log +// ====================== +bool guest_debug_want_log( GuestDebugLogLevelT level ) +{ + return (level <= _log_level); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Debug - Log +// ================= +void guest_debug_log( const char* format, ... ) +{ + char time_str[80]; + char date_str[32]; + struct tm t_real; + struct timespec ts_real; + va_list arguments; + char log_data[512]; + + va_start(arguments, format); + vsnprintf(log_data, sizeof(log_data), format, arguments); + va_end(arguments); + + clock_gettime(CLOCK_REALTIME, &ts_real); + + if (NULL == localtime_r(&(ts_real.tv_sec), &t_real)) + { + snprintf( time_str, sizeof(time_str), + "YYYY:MM:DD HH:MM:SS.xxx" ); + } else { + strftime( date_str, sizeof(date_str), "%b %e %H:%M:%S", + &t_real ); + snprintf( time_str, sizeof(time_str), "%s.%03ld", date_str, + ts_real.tv_nsec/1000000 ); + } + +#ifdef GUEST_DEBUG_WANT_SYSLOG + syslog(LOG_DEBUG, "%s", log_data); +#else + printf("%s %s: %s\n", time_str, _process_name, log_data); +#endif +} +// **************************************************************************** + +// **************************************************************************** +// Guest Debug - Initialize +// ======================== +GuestErrorT guest_debug_initialize( char process_name[] ) +{ + _log_level = GUEST_DEBUG_LOG_LEVEL_INFO; + snprintf(_process_name, sizeof(_process_name), "%s", process_name); + +#ifdef GUEST_DEBUG_WANT_SYSLOG + openlog(_process_name, LOG_PID | LOG_NDELAY, LOG_DAEMON); +#endif + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Debug - Finalize +// ====================== +GuestErrorT guest_debug_finalize( void ) +{ + _log_level = GUEST_DEBUG_LOG_LEVEL_INFO; + _process_name[0] = '\0'; + +#ifdef GUEST_DEBUG_WANT_SYSLOG + closelog(); +#endif + + return GUEST_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_debug.h b/guest-client/guest-client-3.0.1/guest_client/src/guest_debug.h new file mode 100755 index 00000000..09a7d99a --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_debug.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_DEBUG_H__ +#define __GUEST_DEBUG_H__ + +#include + +#include "guest_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + GUEST_DEBUG_LOG_LEVEL_ERROR, + GUEST_DEBUG_LOG_LEVEL_INFO, + GUEST_DEBUG_LOG_LEVEL_DEBUG, + GUEST_DEBUG_LOG_LEVEL_VERBOSE, +} GuestDebugLogLevelT; + +#define DPRINTF(level, format, args...) \ + if (guest_debug_want_log(level)) \ + guest_debug_log("%s: %s(%i): " format, \ + guest_debug_log_level_str(level), \ + __FILE__, __LINE__, ##args) + +#define DPRINTFE(format, args...) \ + DPRINTF(GUEST_DEBUG_LOG_LEVEL_ERROR, format, ##args) +#define DPRINTFI(format, args...) \ + DPRINTF(GUEST_DEBUG_LOG_LEVEL_INFO, format, ##args) +#define DPRINTFD(format, args...) \ + DPRINTF(GUEST_DEBUG_LOG_LEVEL_DEBUG, format, ##args) +#define DPRINTFV(format, args... ) \ + DPRINTF(GUEST_DEBUG_LOG_LEVEL_VERBOSE, format, ##args) + +// **************************************************************************** +// Guest Debug - Log Level String +// ============================== +extern const char* guest_debug_log_level_str( GuestDebugLogLevelT level ); +// **************************************************************************** + +// **************************************************************************** +// Guest Debug - Set Log Level +// =========================== +extern void guest_debug_set_log_level( GuestDebugLogLevelT level ); +// **************************************************************************** + +// **************************************************************************** +// Guest Debug - Want Log +// ====================== +extern bool guest_debug_want_log( GuestDebugLogLevelT level ); +// **************************************************************************** + +// **************************************************************************** +// Guest Debug - Log +// ================= +extern void guest_debug_log( const char* format, ... ); +// **************************************************************************** + +// **************************************************************************** +// Guest Debug - Initialize +// ======================== +extern GuestErrorT guest_debug_initialize( char process_name[] ); +// **************************************************************************** + +// **************************************************************************** +// Guest Debug - Finalize +// ====================== +extern GuestErrorT guest_debug_finalize( void ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_DEBUG_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_limits.h b/guest-client/guest-client-3.0.1/guest_client/src/guest_limits.h new file mode 100755 index 00000000..29953aed --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_limits.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_LIMITS_H__ +#define __GUEST_LIMITS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define GUEST_NAME_MAX_CHAR 64 +#define GUEST_DEVICE_NAME_MAX_CHAR 255 +#define GUEST_MIN_TICK_INTERVAL_IN_MS 25 +#define GUEST_TICK_INTERVAL_IN_MS 300 +#define GUEST_SCHEDULING_MAX_DELAY_IN_MS 800 +#define GUEST_SCHEDULING_DELAY_DEBOUNCE_IN_MS 2000 +#define GUEST_TIMERS_MAX 128 +#define GUEST_MAX_TIMERS_PER_TICK GUEST_TIMERS_MAX / 4 +#define GUEST_SELECT_OBJS_MAX 128 +#define GUEST_MAX_SIGNALS 32 +#define GUEST_MAX_CONNECTIONS 32 +#define GUEST_CHILD_PROCESS_MAX 16 +#define GUEST_APPLICATIONS_MAX 16 +#define GUEST_HEARTBEAT_MIN_INTERVAL_MS 400 + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_LIMITS_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_process.c b/guest-client/guest-client-3.0.1/guest_client/src/guest_process.c new file mode 100755 index 00000000..4f46fab4 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_process.c @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_process.h" + +#include +#include +#include +#include +#include + +#include "guest_limits.h" +#include "guest_types.h" +#include "guest_debug.h" +#include "guest_signal.h" +#include "guest_config.h" +#include "guest_selobj.h" +#include "guest_timer.h" +#include "guest_channel.h" +#include "guest_stream.h" +#include "guest_unix.h" +#include "guest_script.h" +#include "guest_heartbeat.h" +#include "guest_child_death.h" + +static sig_atomic_t _stay_on = 1; +static sig_atomic_t _reload = 0; +static sig_atomic_t _reap_children = 0; + +// **************************************************************************** +// Guest Process - Reload +// ====================== +static void guest_process_reload( void ) +{ + int result; + + DPRINTFI("Reload signal handled."); + _reload = 0; + + result = access("/tmp/guest_debug_debug", F_OK); + if (0 == result) + { + DPRINTFI("Debug log level set to debug."); + guest_debug_set_log_level(GUEST_DEBUG_LOG_LEVEL_DEBUG); + return; + } + + result = access("/tmp/guest_debug_verbose", F_OK); + if (0 == result) + { + DPRINTFI("Debug log level set to verbose."); + guest_debug_set_log_level(GUEST_DEBUG_LOG_LEVEL_VERBOSE); + return; + } + + DPRINTFI("Debug log level set to info."); + guest_debug_set_log_level(GUEST_DEBUG_LOG_LEVEL_INFO); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Process - Reap Children +// ============================= +static void guest_process_reap_children( void ) +{ + pid_t pid; + int status; + + if (_reap_children) + { + _reap_children = 0; + + while (0 < (pid = waitpid(-1, &status, WNOHANG | WUNTRACED))) + { + if (WIFEXITED(status)) + guest_child_death_save(pid, WEXITSTATUS(status)); + else + guest_child_death_save(pid, GUEST_CHILD_FAILED); + } + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Process - Signal Handler +// ============================== +static void guest_process_signal_handler( int signum ) +{ + switch (signum) + { + case SIGINT: + case SIGTERM: + case SIGQUIT: + _stay_on = 0; + break; + + case SIGHUP: + _reload = 1; + break; + + case SIGCHLD: + _reap_children = 1; + break; + + case SIGCONT: + DPRINTFD("Ignoring signal SIGCONT (%i).", signum); + break; + + case SIGPIPE: + DPRINTFD("Ignoring signal SIGPIPE (%i).", signum); + break; + + default: + DPRINTFD("Signal (%i) ignored.", signum); + break; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Process - Initialize +// ========================== +static GuestErrorT guest_process_initialize( + int argc, char *argv[], char *envp[] ) +{ + GuestConfigT* config = NULL; + GuestErrorT error; + + error = guest_config_initialize(argc, argv, envp); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to initialize configuration module, error=%s.", + guest_error_str(error)); + guest_config_show_usage(); + return error; + } + + error = guest_selobj_initialize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to initialize selection object module, error=%s.", + guest_error_str(error)); + return GUEST_FAILED; + } + + error = guest_timer_initialize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to initialize timer module, error=%s.", + guest_error_str(error)); + return GUEST_FAILED; + } + + error = guest_child_death_initialize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to initialize child death module, error=%s.", + guest_error_str(error)); + return GUEST_FAILED; + } + + error = guest_unix_initialize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to initialize unix module, error=%s.", + guest_error_str(error)); + return GUEST_FAILED; + } + + error = guest_channel_initialize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to initialize channel module, error=%s.", + guest_error_str(error)); + return GUEST_FAILED; + } + + error = guest_stream_initialize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to initialize stream module, error=%s.", + guest_error_str(error)); + return GUEST_FAILED; + } + + error = guest_script_initialize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to initialize script module, error=%s.", + guest_error_str(error)); + return GUEST_FAILED; + } + + config = guest_config_get(); + + error = guest_heartbeat_initialize(config->comm_device); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to initialize heartbeat module, error=%s.", + guest_error_str(error)); + return error; + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Process - Finalize +// ======================== +static GuestErrorT guest_process_finalize( void ) +{ + GuestErrorT error; + + error = guest_heartbeat_finalize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to finalize heartbeat module, error=%s.", + guest_error_str(error)); + } + + error = guest_script_finalize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to finalize script module, error=%s.", + guest_error_str(error)); + } + + error = guest_stream_finalize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to finalize stream module, error=%s.", + guest_error_str(error)); + } + + error = guest_channel_finalize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to finalize channel module, error=%s.", + guest_error_str(error)); + } + + error = guest_unix_finalize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to finalize unix module, error=%s.", + guest_error_str(error)); + } + + error = guest_child_death_finalize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to finalize child death module, error=%s.", + guest_error_str(error)); + } + + error = guest_timer_finalize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to finalize timer module, error=%s.", + guest_error_str(error)); + } + + error = guest_selobj_finalize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to finialize selection object module, error=%s.", + guest_error_str(error)); + } + + error = guest_config_finalize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to finialize configuration module, error=%s.", + guest_error_str(error)); + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Process - Main +// ==================== +GuestErrorT guest_process_main( int argc, char *argv[], char *envp[] ) +{ + unsigned int next_interval_in_ms; + GuestErrorT error; + + DPRINTFI("Starting."); + + guest_signal_register_handler(SIGINT, guest_process_signal_handler); + guest_signal_register_handler(SIGTERM, guest_process_signal_handler); + guest_signal_register_handler(SIGQUIT, guest_process_signal_handler); + guest_signal_register_handler(SIGHUP, guest_process_signal_handler); + guest_signal_register_handler(SIGCHLD, guest_process_signal_handler); + guest_signal_register_handler(SIGCONT, guest_process_signal_handler); + guest_signal_register_handler(SIGPIPE, guest_process_signal_handler); + guest_signal_ignore(SIGIO); + + error = guest_process_initialize(argc, argv, envp); + if (GUEST_OKAY != error) + { + if (error != GUEST_NOT_CONFIGURED) + { + DPRINTFE("Failed initialize process restarting in 20 seconds," + "error=%s.", guest_error_str(error)); + sleep(20); + } + else { + DPRINTFI("Application is not configured, will be not restarted," + " exit code=%s", guest_error_str(error)); + } + return error; + } + + DPRINTFI("Started."); + + while (_stay_on) + { + next_interval_in_ms = guest_timer_schedule(); + + error = guest_selobj_dispatch(next_interval_in_ms); + if (GUEST_OKAY != error) + { + DPRINTFE("Selection object dispatch failed, error=%s.", + guest_error_str(error)); + break; + } + + guest_process_reap_children(); + + if (_reload) + guest_process_reload(); + } + + DPRINTFI("Shutting down."); + + error = guest_process_finalize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed finalize process, error=%s.", + guest_error_str(error) ); + } + + DPRINTFI("Shutdown complete."); + + return GUEST_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_process.h b/guest-client/guest-client-3.0.1/guest_client/src/guest_process.h new file mode 100755 index 00000000..0a380d22 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_process.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_PROCESS_H__ +#define __GUEST_PROCESS_H__ + +#include + +#include "guest_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// **************************************************************************** +// Guest Process - Main +// ==================== +extern GuestErrorT guest_process_main( int argc, char *argv[], char *envp[] ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_PROCESS_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_script.c b/guest-client/guest-client-3.0.1/guest_client/src/guest_script.c new file mode 100755 index 00000000..2692e6de --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_script.c @@ -0,0 +1,410 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_script.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "guest_limits.h" +#include "guest_types.h" +#include "guest_debug.h" +#include "guest_selobj.h" +#include "guest_utils.h" +#include "guest_child_death.h" + +#define GUEST_SCRIPT_SETUP_FAILURE -65535 + +typedef struct { + bool inuse; + int pid; + int fd; + int log_end_ptr; + char log_msg[256]; + GuestScriptIdT script_id; + GuestScriptCallbackT callback; +} GuestScriptDataT; + +static GuestScriptDataT _scripts[GUEST_CHILD_PROCESS_MAX]; + +// **************************************************************************** +// Guest Script - Abort +// ==================== +void guest_script_abort( GuestScriptIdT script_id ) +{ + int result; + GuestScriptDataT* entry; + GuestErrorT error; + + if (GUEST_SCRIPT_ID_INVALID == script_id) + return; + + if (GUEST_CHILD_PROCESS_MAX <= script_id) + return; + + entry = &(_scripts[script_id]); + + if (entry->inuse) + { + if (-1 != entry->pid) + { + error = guest_child_death_deregister(entry->pid); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to deregister for child death %i, error=%s.", + entry->pid, guest_error_str(error)); + } + + result = kill(entry->pid, SIGKILL); + if (0 > result) + { + if (ESRCH == errno) + { + DPRINTFV("Script pid (%i) not running.", entry->pid); + } else { + DPRINTFE("Failed to send kill signal to script pid %i, " + "error=%s.", entry->pid, strerror(errno)); + } + } else { + DPRINTFD("Script pid (%i) killed.", entry->pid); + } + } + + if (-1 != entry->fd) + { + error = guest_selobj_deregister(entry->fd); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to deregister selection object %i, error=%s.", + entry->fd, guest_error_str(error)); + } + + close(entry->fd); + } + + memset(entry, 0, sizeof(GuestScriptDataT)); + entry->pid = -1; + entry->fd = -1; + entry->script_id = GUEST_SCRIPT_ID_INVALID; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Script - Dispatch +// ======================= +static void guest_script_dispatch( int selobj ) +{ + int bytes_avail; + int result; + GuestScriptDataT* entry; + + unsigned int script_i; + for (script_i=0; GUEST_CHILD_PROCESS_MAX > script_i; ++script_i) + { + entry = &(_scripts[script_i]); + if (entry->inuse) + if (selobj == entry->fd) + break; + } + + if (GUEST_CHILD_PROCESS_MAX <= script_i) + return; + + bytes_avail = sizeof(entry->log_msg) - entry->log_end_ptr; + + result = read(selobj, &(entry->log_msg[entry->log_end_ptr]), bytes_avail); + if (0 > result) + { + if (EINTR == errno) { + DPRINTFD("Interrupted on read, error=%s.", strerror(errno)); + return; + + } else { + DPRINTFE("Failed to read, error=%s.", strerror(errno)); + return; + } + } else if (0 == result) { + DPRINTFD("No message received."); + return; + + } else { + DPRINTFD("Received message, msg_size=%i.", result); + entry->log_end_ptr += result; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Script - Callback +// ======================= +static void guest_script_callback( pid_t pid, int exit_code ) +{ + GuestScriptDataT* entry; + + unsigned int script_i; + for (script_i=0; GUEST_CHILD_PROCESS_MAX > script_i; ++script_i) + { + entry = &(_scripts[script_i]); + if (entry->inuse) + if ((int) pid == entry->pid) + break; + } + + if (GUEST_CHILD_PROCESS_MAX <= script_i) + return; + + DPRINTFD("PID %i exited with %i", (int) pid, exit_code); + + if (NULL != entry->callback) + entry->callback(entry->script_id, exit_code, entry->log_msg); + + guest_script_abort(entry->script_id); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Script - Invoke +// ===================== +GuestErrorT guest_script_invoke( + char script[], char* script_argv[], GuestScriptCallbackT callback, + GuestScriptIdT* script_id ) +{ + int fd[2]; + pid_t pid; + struct stat stat_data; + char* script_name = guest_utils_basename(script); + char* script_exec = script; + int result; + GuestScriptDataT* entry; + GuestSelObjCallbacksT callbacks; + GuestErrorT error; + + *script_id = GUEST_SCRIPT_ID_INVALID; + + unsigned int script_i; + for (script_i=1; GUEST_CHILD_PROCESS_MAX > script_i; ++script_i) + { + entry = &(_scripts[script_i]); + if (!entry->inuse) + break; + } + + if (GUEST_CHILD_PROCESS_MAX <= script_i) + { + DPRINTFE("Failed to allocate script data."); + return GUEST_FAILED; + } + + memset(entry, 0, sizeof(GuestScriptDataT)); + entry->script_id = script_i; + entry->callback = callback; + entry->pid = -1; + entry->fd = -1; + + result = access(script_exec, F_OK | X_OK); + if (0 > result) + { + DPRINTFE("Script %s access failed, error=%s.", script_exec, + strerror(errno)); + return GUEST_FAILED; + } + + result = stat(script_exec, &stat_data); + if (0 > result) + { + DPRINTFE("Script %s stat failed, error=%s.", script_exec, + strerror( errno ) ); + return GUEST_FAILED; + } + + if (0 >= stat_data.st_size) + { + DPRINTFE("Script %s has zero size.", script_exec); + return GUEST_FAILED; + } + + result = pipe(fd); + if (0 > result) + { + DPRINTFE("Script %s pipe creation failed, error=%s.", script_exec, + strerror(errno)); + return GUEST_FAILED; + } + + result = fcntl(fd[0], F_SETFL, O_NONBLOCK); + if (0 > result) + { + DPRINTFE("Script %s pipe failed to make read end non-blocking, " + "error=%s.", script_exec, strerror(errno)); + close(fd[0]); + close(fd[1]); + return GUEST_FAILED; + } + + pid = fork(); + if (0 > pid) + { + DPRINTFE("Failed to fork process for script %s, error=%s.", + script_exec, strerror(errno)); + close(fd[0]); + close(fd[1]); + return GUEST_FAILED; + + } else if (0 == pid) { + // Child process. + struct rlimit file_limits; + + close(fd[0]); // close read end of pipe + + result = setpgid(0, 0); + if (0 > result) + { + DPRINTFE("Failed to set process group id for script %s, " + "error=%s.", script_exec, strerror( errno ) ); + exit(GUEST_SCRIPT_SETUP_FAILURE); + } + + result = getrlimit(RLIMIT_NOFILE, &file_limits); + if (0 > result) + { + DPRINTFE("Failed to get file limits for script %s, error=%s.", + script_exec, strerror(errno)); + exit(GUEST_SCRIPT_SETUP_FAILURE); + } + + unsigned int fd_i; + for (fd_i=0; fd_i < file_limits.rlim_cur; ++fd_i) + if (fd_i != fd[1]) + close(fd_i); + + result = dup2(fd[1], 1); // make stdout into writable end of pipe + if (0 > result) + { + DPRINTFE("Failed to make stdout into writable end of pipe for " + "script %s, error=%s.", script_exec, strerror(errno)); + exit(GUEST_SCRIPT_SETUP_FAILURE); + } + + result = execv(script_exec, (char**) script_argv); + if (0 > result) + DPRINTFE("Failed to exec command for script %s, error=%s.", + script_exec, strerror(errno)); + + exit(GUEST_SCRIPT_SETUP_FAILURE); + + } else { + // Parent process. + close(fd[1]); // close write end of pipe + entry->pid = (int) pid; + entry->fd = fd[0]; + entry->inuse = true; + + DPRINTFD("Child process %i created for script %s, script_id=%i.", + entry->pid, script_name, entry->script_id); + + error = guest_child_death_register(pid, guest_script_callback); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to register for child death %i, error=%s.", + entry->pid, guest_error_str(error)); + guest_script_abort(entry->script_id); + return error; + } + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.read_callback = guest_script_dispatch; + + error = guest_selobj_register(entry->fd, &callbacks); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to register selection object %i, error=%s.", + entry->fd, guest_error_str(error)); + guest_script_abort(entry->script_id); + return error; + } + + *script_id = entry->script_id; + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Script - Initialize +// ========================= +GuestErrorT guest_script_initialize( void ) +{ + GuestScriptDataT* entry; + + memset(_scripts, 0, sizeof(_scripts)); + + unsigned int script_i; + for (script_i=0; GUEST_CHILD_PROCESS_MAX > script_i; ++script_i) + { + entry = &(_scripts[script_i]); + + entry->pid = -1; + entry->fd = -1; + entry->script_id = GUEST_SCRIPT_ID_INVALID; + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Script - Finalize +// ======================= +GuestErrorT guest_script_finalize( void ) +{ + GuestScriptDataT* entry; + + unsigned int script_i; + for (script_i=0; GUEST_CHILD_PROCESS_MAX > script_i; ++script_i) + { + entry = &(_scripts[script_i]); + if (entry->inuse) + guest_script_abort(entry->script_id); + } + + memset(_scripts, 0, sizeof(_scripts)); + return GUEST_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_script.h b/guest-client/guest-client-3.0.1/guest_client/src/guest_script.h new file mode 100755 index 00000000..e1a74c8e --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_script.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_SCRIPT_H__ +#define __GUEST_SCRIPT_H__ + +#include "guest_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define GUEST_SCRIPT_ID_INVALID -1 + +typedef int GuestScriptIdT; + +typedef void (*GuestScriptCallbackT) + (GuestScriptIdT script_id,int exit_code, char* log_msg); + +// **************************************************************************** +// Guest Script - Abort +// ==================== +extern void guest_script_abort( GuestScriptIdT script_id ); +// **************************************************************************** + +// **************************************************************************** +// Guest Script - Invoke +// ===================== +extern GuestErrorT guest_script_invoke( + char script[], char* script_argv[], GuestScriptCallbackT callback, + GuestScriptIdT* script_id ); +// **************************************************************************** + +// **************************************************************************** +// Guest Script - Initialize +// ========================= +extern GuestErrorT guest_script_initialize( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Script - Finalize +// ======================= +extern GuestErrorT guest_script_finalize( void ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_SCRIPT_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_selobj.c b/guest-client/guest-client-3.0.1/guest_client/src/guest_selobj.c new file mode 100755 index 00000000..e8ffafa4 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_selobj.c @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_selobj.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "guest_limits.h" +#include "guest_types.h" +#include "guest_debug.h" + +typedef struct { + bool inuse; + int selobj; + GuestSelObjCallbacksT callbacks; +} GuestSelObjEntryT; + +typedef GuestSelObjEntryT GuestSelObjTableT[GUEST_SELECT_OBJS_MAX]; + +static int _num_poll_fds = 0; +static struct pollfd _poll_fds[GUEST_SELECT_OBJS_MAX]; +static GuestSelObjTableT _select_objs; + +// **************************************************************************** +// Guest Selection Object - Find Selection Object +// ============================================== +static GuestSelObjEntryT* guest_selobj_find( int selobj ) +{ + GuestSelObjEntryT* entry; + + unsigned int entry_i; + for (entry_i=0; GUEST_SELECT_OBJS_MAX > entry_i; ++entry_i) + { + entry = &(_select_objs[entry_i]); + if (entry->inuse) + if (selobj == entry->selobj) + return entry; + } + return NULL; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Selection Object - Register +// ================================= +GuestErrorT guest_selobj_register ( + int selobj, GuestSelObjCallbacksT* callbacks ) +{ + GuestSelObjEntryT* entry; + + entry = guest_selobj_find(selobj); + if (NULL == entry) + { + unsigned int entry_i; + for (entry_i=0; GUEST_SELECT_OBJS_MAX > entry_i; ++entry_i) + { + entry = &(_select_objs[entry_i]); + if (!entry->inuse ) + { + entry->inuse = true; + entry->selobj = selobj; + memcpy(&(entry->callbacks), callbacks, + sizeof(GuestSelObjCallbacksT)); + break; + } + } + + // Rebuild polling file descriptors. + _num_poll_fds =0; + + for (entry_i=0; GUEST_SELECT_OBJS_MAX > entry_i; ++entry_i) + { + entry = &(_select_objs[entry_i]); + if (entry->inuse) + { + memset(&_poll_fds[_num_poll_fds], 0, sizeof(struct pollfd)); + + _poll_fds[_num_poll_fds].fd = entry->selobj; + + if (NULL != entry->callbacks.read_callback) + _poll_fds[_num_poll_fds].events |= POLLIN; + + if (NULL != entry->callbacks.write_callback) + _poll_fds[_num_poll_fds].events |= POLLOUT; + + if (NULL != entry->callbacks.hangup_callback) + _poll_fds[_num_poll_fds].events |= POLLHUP; + + ++_num_poll_fds; + } + } + } else { + memcpy(&(entry->callbacks), callbacks, sizeof(GuestSelObjCallbacksT)); + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Selection Object - Deregister +// =================================== +GuestErrorT guest_selobj_deregister( int selobj ) +{ + GuestSelObjEntryT* entry; + + entry = guest_selobj_find(selobj); + if (NULL != entry) + memset(entry, 0, sizeof(GuestSelObjEntryT)); + + // Rebuild polling file descriptors. + _num_poll_fds =0; + + unsigned int entry_i; + for (entry_i=0; GUEST_SELECT_OBJS_MAX > entry_i; ++entry_i) + { + entry = &(_select_objs[entry_i]); + if (entry->inuse) + { + memset(&_poll_fds[_num_poll_fds], 0, sizeof(struct pollfd)); + + _poll_fds[_num_poll_fds].fd = entry->selobj; + + if (NULL != entry->callbacks.read_callback) + _poll_fds[_num_poll_fds].events |= POLLIN; + + if (NULL != entry->callbacks.write_callback) + _poll_fds[_num_poll_fds].events |= POLLOUT; + + if (NULL != entry->callbacks.hangup_callback) + _poll_fds[_num_poll_fds].events |= POLLHUP; + + ++_num_poll_fds; + } + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Selection Object - Dispatch +// ================================= +GuestErrorT guest_selobj_dispatch( unsigned int timeout_in_ms ) +{ + struct pollfd* poll_entry; + GuestSelObjEntryT* entry; + int result; + + result = poll(_poll_fds, _num_poll_fds, timeout_in_ms); + if (0 > result) + { + if (errno == EINTR) + { + DPRINTFD("Interrupted by a signal."); + return GUEST_OKAY; + } else { + DPRINTFE("Select failed, error=%s.", strerror(errno)); + return GUEST_FAILED; + } + } else if (0 == result) { + DPRINTFV("Nothing selected."); + return GUEST_OKAY; + } + + unsigned int entry_i; + for (entry_i=0; _num_poll_fds > entry_i; ++entry_i) + { + poll_entry = &(_poll_fds[entry_i]); + + entry = guest_selobj_find(poll_entry->fd); + if (NULL != entry) + { + if (0 != (poll_entry->revents & POLLIN)) + if (NULL != entry->callbacks.read_callback) + { + DPRINTFD("Read on selection object %i", poll_entry->fd); + entry->callbacks.read_callback(entry->selobj); + } + + if (0 != (poll_entry->revents & POLLOUT)) + if (NULL != entry->callbacks.write_callback) + { + DPRINTFD("Write on selection object %i", poll_entry->fd); + entry->callbacks.write_callback(entry->selobj); + } + + if (0 != (poll_entry->revents & POLLHUP)) + if (NULL != entry->callbacks.hangup_callback) + { + DPRINTFD("Hangup on selection object %i", poll_entry->fd); + entry->callbacks.hangup_callback(entry->selobj); + } + } + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Selection Object - Initialize +// =================================== +GuestErrorT guest_selobj_initialize( void ) +{ + _num_poll_fds = 0; + memset(_poll_fds, 0, sizeof(_poll_fds)); + memset(_select_objs, 0, sizeof(GuestSelObjTableT)); + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Selection Object - Finalize +// ================================= +GuestErrorT guest_selobj_finalize( void ) +{ + _num_poll_fds = 0; + memset(_poll_fds, 0, sizeof(_poll_fds)); + memset(_select_objs, 0, sizeof(GuestSelObjTableT)); + return GUEST_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_selobj.h b/guest-client/guest-client-3.0.1/guest_client/src/guest_selobj.h new file mode 100755 index 00000000..46bae5ed --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_selobj.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_SELECTION_OBJECT_H__ +#define __GUEST_SELECTION_OBJECT_H__ + +#include "guest_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*GuestSelObjReadCallbackT) (int selobj); +typedef void (*GuestSelObjWriteCallbackT) (int selobj); +typedef void (*GuestSelObjHangupCallbackT) (int selobj); + +typedef struct { + GuestSelObjReadCallbackT read_callback; + GuestSelObjWriteCallbackT write_callback; + GuestSelObjHangupCallbackT hangup_callback; +} GuestSelObjCallbacksT; + +// **************************************************************************** +// Guest Selection Object - Register +// ================================= +extern GuestErrorT guest_selobj_register( + int selobj, GuestSelObjCallbacksT* callbacks ); +// **************************************************************************** + +// **************************************************************************** +// Guest Selection Object - Deregister +// =================================== +extern GuestErrorT guest_selobj_deregister( int selobj ); +// **************************************************************************** + +// **************************************************************************** +// Guest Selection Object - Dispatch +// ================================= +extern GuestErrorT guest_selobj_dispatch( unsigned int timeout_in_ms ); +// **************************************************************************** + +// **************************************************************************** +// Guest Selection Object - Initialize +// =================================== +extern GuestErrorT guest_selobj_initialize( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Selection Object - Finalize +// ================================= +extern GuestErrorT guest_selobj_finalize( void ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_SELECTION_OBJECT_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_signal.c b/guest-client/guest-client-3.0.1/guest_client/src/guest_signal.c new file mode 100755 index 00000000..9cc58c4b --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_signal.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_signal.h" + +#include +#include +#include + +#include "guest_limits.h" +#include "guest_types.h" +#include "guest_debug.h" + +typedef struct { + bool inuse; + int signum; + GuestSignalHandlerT handler; +} GuestSignalT; + +static GuestSignalT _signal[GUEST_MAX_SIGNALS]; + +// **************************************************************************** +// Guest Signal - Map +// ================== +static GuestSignalT* guest_signal_map( int signum ) +{ + switch (signum) + { + case SIGINT: return &(_signal[0]); + case SIGTERM: return &(_signal[1]); + case SIGQUIT: return &(_signal[2]); + case SIGHUP: return &(_signal[3]); + case SIGCHLD: return &(_signal[4]); + case SIGCONT: return &(_signal[5]); + case SIGPIPE: return &(_signal[6]); + case SIGIO: return &(_signal[7]); + default: + DPRINTFE("Mapping for signal %i missing.", signum); + } + + return NULL; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Signal - Handler +// ====================== +static void guest_signal_handler( int signum ) +{ + GuestSignalT* entry; + + DPRINTFD("Signal %i received.", signum); + + entry = guest_signal_map(signum); + if (NULL != entry) + if (entry->inuse) + if (NULL != entry->handler) + entry->handler(signum); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Signal - Register Handler +// =============================== +void guest_signal_register_handler( int signum, GuestSignalHandlerT handler ) +{ + GuestSignalT* entry; + + entry = guest_signal_map(signum); + if (NULL != entry) + { + entry->inuse = true; + entry->signum = signum; + entry->handler = handler; + signal(signum, guest_signal_handler); + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Signal - Deregister Handler +// ================================= +void guest_signal_deregister_handler( int signum ) +{ + GuestSignalT* entry; + + entry = guest_signal_map(signum); + if (NULL != entry) + { + memset(entry, 0, sizeof(GuestSignalT)); + signal(signum, SIG_DFL); + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Signal - Ignore +// ===================== +void guest_signal_ignore( int signum ) +{ + guest_signal_deregister_handler(signum); + signal(signum, SIG_IGN); +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_signal.h b/guest-client/guest-client-3.0.1/guest_client/src/guest_signal.h new file mode 100755 index 00000000..5c229286 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_signal.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_SIGNAL_H__ +#define __GUEST_SIGNAL_H__ + +#include "guest_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*GuestSignalHandlerT) (int signum); + +// **************************************************************************** +// Guest Signal - Register Handler +// =============================== +extern void guest_signal_register_handler( + int signum, GuestSignalHandlerT handler); +// **************************************************************************** + +// **************************************************************************** +// Guest Signal - Deregister Handler +// ================================= +extern void guest_signal_deregister_handler( int signum ); +// **************************************************************************** + +// **************************************************************************** +// Guest Signal - Ignore +// ===================== +extern void guest_signal_ignore( int signum ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_SIGNAL_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_stream.c b/guest-client/guest-client-3.0.1/guest_client/src/guest_stream.c new file mode 100755 index 00000000..92521065 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_stream.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_stream.h" + +#include +#include +#include + +#include "guest_types.h" +#include "guest_debug.h" + +// **************************************************************************** +// Guest Stream - Get +// ================== +int guest_stream_get( GuestStreamT* stream ) +{ + char* byte_ptr; + int delimiter_i = 0; + + if (stream->delimiter_size > stream->size) + return -1; + + for (byte_ptr = stream->bytes; byte_ptr != stream->end_ptr; ++byte_ptr) + { + if (stream->delimiter[delimiter_i] == *byte_ptr) + { + ++delimiter_i; + if (delimiter_i == stream->delimiter_size) + { + return (byte_ptr - stream->bytes); + } + } else { + delimiter_i = 0; + } + } + + return -1; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Stream - Get Next +// ======================= +bool guest_stream_get_next( GuestStreamT* stream ) +{ + char* byte_ptr; + int delimiter_i = 0; + + if (stream->delimiter_size > stream->size) + return false; + + for (byte_ptr = stream->bytes; byte_ptr != stream->end_ptr; ++byte_ptr) + { + --stream->size; + if (stream->delimiter[delimiter_i] == *byte_ptr) + { + ++delimiter_i; + if (delimiter_i == stream->delimiter_size) + { + byte_ptr -= (stream->delimiter_size-1); + stream->size += stream->delimiter_size; + memmove(stream->bytes, byte_ptr, stream->size); + stream->avail = stream->max_size - stream->size; + stream->end_ptr = stream->bytes + stream->size; + break; + } + } else { + delimiter_i = 0; + } + } + + if (byte_ptr == stream->end_ptr) + { + // Empty the stream + memset(stream->bytes, 0, stream->max_size); + stream->avail = stream->max_size; + stream->size = 0; + stream->end_ptr = stream->bytes; + return false; + } + + return true; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Stream - Advance +// ====================== +void guest_stream_advance( int adv, GuestStreamT* stream ) +{ + stream->size -= adv; + memmove(stream->bytes, stream->bytes+adv, stream->size); + stream->avail = stream->max_size - stream->size; + stream->end_ptr = stream->bytes + stream->size; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Stream - Reset +// ==================== +void guest_stream_reset( GuestStreamT* stream ) +{ + memset(stream->bytes, 0, stream->max_size); + stream->avail = stream->max_size; + stream->size = 0; + stream->end_ptr = stream->bytes; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Stream - Setup +// ==================== +GuestErrorT guest_stream_setup( + const char* delimiter, int delimiter_size, int stream_size, + GuestStreamT* stream ) +{ + stream->delimiter = malloc(delimiter_size); + if (NULL == stream->delimiter) + { + DPRINTFE("Failed to allocated delimiter storage, needed=%i.", + delimiter_size); + return GUEST_FAILED; + } + + stream->bytes = malloc(stream_size); + if (NULL == stream->delimiter) + { + DPRINTFE("Failed to allocated stream storage, needed=%i.", stream_size); + free(stream->delimiter); + return GUEST_FAILED; + } + + memcpy(stream->delimiter, delimiter, delimiter_size); + stream->delimiter_size = delimiter_size; + memset(stream->bytes, 0, stream_size); + stream->end_ptr = stream->bytes; + stream->avail = stream_size; + stream->size = 0; + stream->max_size = stream_size; + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Stream - Release +// ====================== +GuestErrorT guest_stream_release( GuestStreamT* stream ) +{ + if (NULL != stream->delimiter) + free(stream->delimiter); + + if (NULL != stream->bytes) + free(stream->bytes); + + memset(stream, 0, sizeof(GuestStreamT)); + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Stream - Initialize +// ========================= +GuestErrorT guest_stream_initialize( void ) +{ + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Stream - Finalize +// ======================= +GuestErrorT guest_stream_finalize( void ) +{ + return GUEST_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_stream.h b/guest-client/guest-client-3.0.1/guest_client/src/guest_stream.h new file mode 100755 index 00000000..63739855 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_stream.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_STREAM_H__ +#define __GUEST_STREAM_H__ + +#include + +#include "guest_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + char* delimiter; + int delimiter_size; + char* end_ptr; + char* bytes; + int avail; + int size; + int max_size; +} GuestStreamT; + +// **************************************************************************** +// Guest Stream - Get +// ================== +extern int guest_stream_get( GuestStreamT* stream ); +// **************************************************************************** + +// **************************************************************************** +// Guest Stream - Get Next +// ======================= +extern bool guest_stream_get_next( GuestStreamT* stream ); +// **************************************************************************** + +// **************************************************************************** +// Guest Stream - Advance +// ====================== +extern void guest_stream_advance( int adv, GuestStreamT* stream ); +// **************************************************************************** + +// **************************************************************************** +// Guest Stream - Reset +// ==================== +extern void guest_stream_reset( GuestStreamT* stream ); +// **************************************************************************** + +// **************************************************************************** +// Guest Stream - Setup +// ==================== +extern GuestErrorT guest_stream_setup( + const char* delimiter, int delimiter_size, int stream_size, + GuestStreamT* stream ); +// **************************************************************************** + +// **************************************************************************** +// Guest Stream - Release +// ====================== +extern GuestErrorT guest_stream_release( GuestStreamT* stream ); +// **************************************************************************** + +// **************************************************************************** +// Guest Stream - Initialize +// ========================= +extern GuestErrorT guest_stream_initialize( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Stream - Finalize +// ======================= +extern GuestErrorT guest_stream_finalize( void ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_STREAM_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_time.c b/guest-client/guest-client-3.0.1/guest_client/src/guest_time.c new file mode 100755 index 00000000..cf226cf8 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_time.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_time.h" + +#include + +// **************************************************************************** +// Guest Time - Get +// ================ +void guest_time_get( GuestTimeT* time ) +{ +#ifdef CLOCK_MONOTONIC_RAW + clock_gettime(CLOCK_MONOTONIC_RAW, time); +#else + clock_gettime(CLOCK_MONOTONIC, time); +#endif +} +// **************************************************************************** + +// **************************************************************************** +// Guest Time - Get Elapsed Milliseconds +// ===================================== +long guest_time_get_elapsed_ms( GuestTimeT* time ) +{ + GuestTimeT now; + + guest_time_get(&now); + + if (NULL == time) + return ((now.tv_sec*1000) + (now.tv_nsec/1000000)); + else + return (guest_time_delta_in_ms(&now, time)); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Time - Delta in Milliseconds +// ================================== +long guest_time_delta_in_ms( GuestTimeT* end, GuestTimeT* start ) +{ + long start_in_ms = (start->tv_sec*1000) + (start->tv_nsec/1000000); + long end_in_ms = (end->tv_sec*1000) + (end->tv_nsec/1000000); + + return (end_in_ms - start_in_ms); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Time - Convert Milliseconds +// ================================= +void guest_time_convert_ms( long ms, GuestTimeT* time ) +{ + time->tv_sec = ms / 1000; + time->tv_nsec = (ms % 1000) * 1000000; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_time.h b/guest-client/guest-client-3.0.1/guest_client/src/guest_time.h new file mode 100755 index 00000000..61b73d58 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_time.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_TIME_H__ +#define __GUEST_TIME_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct timespec GuestTimeT; + +// **************************************************************************** +// Guest Time - Get +// ================ +extern void guest_time_get( GuestTimeT* time ); +// **************************************************************************** + +// **************************************************************************** +// Guest Time - Get Elapsed Milliseconds +// ===================================== +extern long guest_time_get_elapsed_ms( GuestTimeT* time ); +// **************************************************************************** + +// **************************************************************************** +// Guest Time - Delta in Milliseconds +// ================================== +extern long guest_time_delta_in_ms( GuestTimeT* end, GuestTimeT* start ); +// **************************************************************************** + +// **************************************************************************** +// Guest Time - Convert Milliseconds +// ================================= +extern void guest_time_convert_ms( long ms, GuestTimeT* time ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_TIME_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_timer.c b/guest-client/guest-client-3.0.1/guest_client/src/guest_timer.c new file mode 100755 index 00000000..b02e62c3 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_timer.c @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_timer.h" + +#include +#include +#include +#include + +#include "guest_limits.h" +#include "guest_types.h" +#include "guest_debug.h" +#include "guest_time.h" + +typedef uint64_t GuestTimerInstanceT; + +typedef struct { + bool inuse; + GuestTimerInstanceT timer_instance; + GuestTimerIdT timer_id; + unsigned int ms_interval; + GuestTimeT arm_timestamp; + GuestTimerCallbackT callback; +} GuestTimerEntryT; + +typedef GuestTimerEntryT GuestTimerTableT[GUEST_TIMERS_MAX]; + +static bool _scheduling_on_time = true; +static GuestTimerInstanceT _timer_instance = 0; +static GuestTimerIdT _last_timer_dispatched = 0; +static GuestTimerTableT _timers; +static GuestTimeT _delay_timestamp; +static GuestTimeT _schedule_timestamp; + +// **************************************************************************** +// Guest Timer - Scheduling On Time +// ================================ +bool guest_timer_scheduling_on_time( void ) +{ + return _scheduling_on_time; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Timer - Scheduling On Time Within +// ======================================= +bool guest_timer_scheduling_on_time_within( unsigned int period_in_ms ) +{ + long ms_expired; + + ms_expired = guest_time_get_elapsed_ms(&_delay_timestamp); + return (period_in_ms < ms_expired); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Timer - Reset +// =================== +GuestErrorT guest_timer_reset( GuestTimerIdT timer_id ) +{ + GuestTimerEntryT* timer_entry = NULL; + + if ((GUEST_TIMER_ID_INVALID == timer_id)||(GUEST_TIMERS_MAX <= timer_id)) + return GUEST_FAILED; + + timer_entry = &(_timers[timer_id]); + guest_time_get(&timer_entry->arm_timestamp); + + DPRINTFD("Timer (%i) reset.", timer_entry->timer_id); + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Timer - Register +// ====================== +GuestErrorT guest_timer_register( + unsigned int ms, GuestTimerCallbackT callback, GuestTimerIdT* timer_id ) +{ + GuestTimerEntryT* timer_entry; + + *timer_id = GUEST_TIMER_ID_INVALID; + + unsigned int timer_i; + for (timer_i=1; GUEST_TIMERS_MAX > timer_i; ++timer_i) + { + timer_entry = &(_timers[timer_i]); + + if (timer_entry->inuse) + continue; + + memset(timer_entry, 0, sizeof(GuestTimerEntryT)); + + timer_entry->inuse = true; + timer_entry->timer_instance = ++_timer_instance; + timer_entry->timer_id = timer_i; + timer_entry->ms_interval = ms; + guest_time_get(&timer_entry->arm_timestamp); + timer_entry->callback = callback; + break; + } + + if (GUEST_TIMERS_MAX <= timer_i) + { + DPRINTFE("No space available to create timer, exiting..."); + abort(); + } + + *timer_id = timer_i; + + DPRINTFD("Created timer, id=%i.", timer_entry->timer_id); + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Timer - Deregister +// ======================== +GuestErrorT guest_timer_deregister( GuestTimerIdT timer_id ) +{ + GuestTimerEntryT* timer_entry = NULL; + + if ((GUEST_TIMER_ID_INVALID == timer_id)||(GUEST_TIMERS_MAX <= timer_id)) + return GUEST_OKAY; + + timer_entry = &(_timers[timer_id]); + timer_entry->inuse = false; + timer_entry->timer_instance = 0; + + DPRINTFD("Cancelled timer, id=%i.", timer_entry->timer_id); + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Timer - Schedule Next +// =========================== +static unsigned int guest_timer_schedule_next( void ) +{ + GuestTimerEntryT* timer_entry; + long ms_expired, ms_remaining; + unsigned int interval_in_ms = GUEST_TICK_INTERVAL_IN_MS; + + unsigned int timer_i; + for (timer_i=0; GUEST_TIMERS_MAX > timer_i; ++timer_i) + { + timer_entry = &(_timers[timer_i]); + + if (timer_entry->inuse) + { + ms_expired = guest_time_get_elapsed_ms(&timer_entry->arm_timestamp); + if (ms_expired < timer_entry->ms_interval) + { + ms_remaining = timer_entry->ms_interval - ms_expired; + if (ms_remaining < interval_in_ms) + interval_in_ms = ms_remaining; + } else { + interval_in_ms = GUEST_MIN_TICK_INTERVAL_IN_MS; + break; + } + } + } + + if (GUEST_MIN_TICK_INTERVAL_IN_MS > interval_in_ms) + interval_in_ms = GUEST_MIN_TICK_INTERVAL_IN_MS; + + else if (GUEST_TICK_INTERVAL_IN_MS < interval_in_ms) + interval_in_ms = GUEST_TICK_INTERVAL_IN_MS; + + DPRINTFV("Scheduling timers in %d ms.", interval_in_ms); + return interval_in_ms; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Timer - Schedule +// ====================== +unsigned int guest_timer_schedule( void ) +{ + long ms_expired; + GuestTimeT time_prev; + GuestTimerEntryT* timer_entry; + unsigned int total_timers_fired =0; + + ms_expired = guest_time_get_elapsed_ms(&_schedule_timestamp); + if (ms_expired >= GUEST_SCHEDULING_MAX_DELAY_IN_MS) + { + if (_scheduling_on_time) + { + _scheduling_on_time = false; + DPRINTFI("Not scheduling on time, elapsed=%li ms.", ms_expired); + } + guest_time_get(&_delay_timestamp); + + } else if (!_scheduling_on_time) { + ms_expired = guest_time_get_elapsed_ms(&_delay_timestamp); + if (GUEST_SCHEDULING_DELAY_DEBOUNCE_IN_MS < ms_expired) + { + _scheduling_on_time = true; + DPRINTFI("Now scheduling on time."); + } + } + + guest_time_get(&time_prev); + + unsigned int timer_i; + for (timer_i=_last_timer_dispatched; GUEST_TIMERS_MAX > timer_i; ++timer_i) + { + timer_entry = &(_timers[timer_i]); + + if (timer_entry->inuse) + { + ms_expired = guest_time_get_elapsed_ms(&timer_entry->arm_timestamp); + + if (ms_expired >= timer_entry->ms_interval) + { + bool rearm; + GuestTimerInstanceT timer_instance; + + DPRINTFD("Timer %i fire, ms_interval=%d, ms_expired=%li.", + timer_entry->timer_id, timer_entry->ms_interval, + ms_expired); + + timer_instance = timer_entry->timer_instance; + + rearm = timer_entry->callback(timer_entry->timer_id); + + if (timer_instance == timer_entry->timer_instance) + { + if (rearm) + { + guest_time_get(&timer_entry->arm_timestamp); + DPRINTFD("Timer (%i) rearmed.", timer_entry->timer_id); + } else { + timer_entry->inuse = 0; + DPRINTFD("Timer (%i) removed.", timer_entry->timer_id); + } + } else { + DPRINTFD("Timer (%i) instance changed since callback, " + "rearm=%d.", timer_entry->timer_id, (int) rearm); + } + + if (GUEST_MAX_TIMERS_PER_TICK <= ++total_timers_fired) + { + DPRINTFD("Maximum timers per tick (%d) reached.", + GUEST_MAX_TIMERS_PER_TICK); + break; + } + } + } + } + + if (GUEST_TIMERS_MAX <= timer_i) + _last_timer_dispatched = 0; + else + _last_timer_dispatched = timer_i; + + ms_expired = guest_time_get_elapsed_ms(&time_prev); + if (ms_expired >= GUEST_SCHEDULING_MAX_DELAY_IN_MS) + { + _scheduling_on_time = false; + guest_time_get(&_delay_timestamp); + + DPRINTFI("Not scheduling on time, timer callbacks are taking too " + "long to execute, elapsed_time=%li ms.", ms_expired); + } else { + DPRINTFV("Timer callbacks took %li ms.", ms_expired); + } + + guest_time_get(&_schedule_timestamp); + + return guest_timer_schedule_next(); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Timer - Initialize +// ======================== +GuestErrorT guest_timer_initialize( void ) +{ + _scheduling_on_time = true; + _last_timer_dispatched = 0; + memset(_timers, 0, sizeof(GuestTimerTableT)); + guest_time_get(&_schedule_timestamp); + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Timer - Finalize +// ====================== +GuestErrorT guest_timer_finalize( void ) +{ + memset(_timers, 0, sizeof(GuestTimerTableT)); + return GUEST_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_timer.h b/guest-client/guest-client-3.0.1/guest_client/src/guest_timer.h new file mode 100755 index 00000000..b3a82c07 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_timer.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_TIMER_H__ +#define __GUEST_TIMER_H__ + +#include + +#include "guest_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define GUEST_TIMER_ID_INVALID -1 + +typedef int GuestTimerIdT; + +typedef bool (*GuestTimerCallbackT) (GuestTimerIdT timer_id); + +// **************************************************************************** +// Guest Timer - Scheduling On Time +// ================================ +extern bool guest_timer_scheduling_on_time( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Timer - Scheduling On Time Within +// ======================================= +extern bool guest_timer_scheduling_on_time_within( unsigned int period_in_ms ); +// **************************************************************************** + +// **************************************************************************** +// Guest Timer - Reset +// =================== +extern GuestErrorT guest_timer_reset( GuestTimerIdT timer_id ); +// **************************************************************************** + +// **************************************************************************** +// Guest Timer - Register +// ====================== +extern GuestErrorT guest_timer_register( + unsigned int ms, GuestTimerCallbackT callback, GuestTimerIdT* timer_id ); +// **************************************************************************** + +// **************************************************************************** +// Guest Timer - Deregister +// ======================== +extern GuestErrorT guest_timer_deregister( GuestTimerIdT timer_id ); +// **************************************************************************** + +// **************************************************************************** +// Guest Timer - Schedule +// ====================== +extern unsigned int guest_timer_schedule( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Timer - Initialize +// ======================== +extern GuestErrorT guest_timer_initialize( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Timer - Finalize +// ====================== +extern GuestErrorT guest_timer_finalize( void ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_TIMER_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_types.c b/guest-client/guest-client-3.0.1/guest_client/src/guest_types.c new file mode 100755 index 00000000..ce92d6d5 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_types.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_types.h" + +// **************************************************************************** +// Guest Types - Error String +// ========================== +const char* guest_error_str( GuestErrorT error ) +{ + switch (error) + { + case GUEST_OKAY: return "OKAY"; + case GUEST_FAILED: return "FAILED"; + case GUEST_INTERRUPTED: return "INTERRUPTED"; + case GUEST_TRY_AGAIN: return "TRY_AGAIN"; + case GUEST_NOT_CONFIGURED: return "NOT_CONFIGURED"; + default: + return "???"; + } +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_types.h b/guest-client/guest-client-3.0.1/guest_client/src/guest_types.h new file mode 100755 index 00000000..ad569b24 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_types.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_TYPES_H__ +#define __GUEST_TYPES_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + GUEST_OKAY, + GUEST_FAILED, + GUEST_INTERRUPTED, + GUEST_TRY_AGAIN, + GUEST_NOT_CONFIGURED=6, +} GuestErrorT; + +// **************************************************************************** +// Guest Types - Error String +// ========================== +extern const char* guest_error_str( GuestErrorT error ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_TYPES_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_unix.c b/guest-client/guest-client-3.0.1/guest_client/src/guest_unix.c new file mode 100755 index 00000000..9ebd0f40 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_unix.c @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_unix.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "guest_limits.h" +#include "guest_types.h" +#include "guest_debug.h" +#include "guest_selobj.h" + +GuestUnixConnectCallbackT _callback = NULL; + +// **************************************************************************** +// Guest Unix - Connect +// ==================== +GuestErrorT guest_unix_connect( int s, char* address ) +{ + struct sockaddr_un remote; + int len, result; + + memset(&remote, 0, sizeof(remote)); + + remote.sun_family = AF_UNIX; + len = sizeof(remote.sun_family); + len += snprintf(remote.sun_path, sizeof(remote.sun_path), "%s", address); + + result = connect(s, (struct sockaddr*) &remote, sizeof(remote)); + if (0 > result) + { + DPRINTFE("Failed to connect to %s, error=%s.", address, + strerror(errno)); + return GUEST_FAILED; + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Unix - Dispatch Connection +// ================================ +static void guest_unix_dispatch_connection( int s ) +{ + int sock; + struct sockaddr_un remote; + socklen_t len; + int result; + + memset(&remote, 0, sizeof(remote)); + len = sizeof(remote); + + result = accept(s, (struct sockaddr*) &remote, &len); + if (0 > result) + { + DPRINTFE("Failed to accept on socket, error=%s.", strerror(errno)); + return; + } + + sock = result; + + result = fcntl(sock, F_SETFD, FD_CLOEXEC); + if (0 > result) + { + DPRINTFE("Failed to set to close on exec, error=%s.", strerror(errno)); + close(sock); + return; + } + + result = fcntl(sock, F_GETFL); + if (0 > result) + { + DPRINTFE("Failed to get socket options, error=%s.", strerror(errno)); + close(sock); + return; + } + + result = fcntl(sock, F_SETFL, result | O_NONBLOCK); + if (0 > result) + { + DPRINTFE("Failed to set socket options, error=%s.", strerror(errno)); + close(sock); + return; + } + + if (NULL == _callback) + close(sock); + + _callback(sock, remote.sun_path); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Unix - Listen +// =================== +GuestErrorT guest_unix_listen( + int s, char* address, GuestUnixConnectCallbackT callback ) +{ + struct sockaddr_un local; + int len, result; + GuestSelObjCallbacksT callbacks; + GuestErrorT error; + + memset(&local, 0, sizeof(local)); + + local.sun_family = AF_UNIX; + len = sizeof(local.sun_family); + len += snprintf(local.sun_path, sizeof(local.sun_path), "%s", address); + + unlink(address); + + result = bind(s, (struct sockaddr*) &local, len); + if (0 > result) + { + DPRINTFE("Failed to bind socket to address %s, error=%s.", address, + strerror(errno)); + return GUEST_FAILED; + } + + result = listen(s, GUEST_MAX_CONNECTIONS); + if (0 > result) + { + DPRINTFE("Failed to listen on %s, error=%s.", address, + strerror(errno)); + return GUEST_FAILED; + } + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.read_callback = guest_unix_dispatch_connection; + + error = guest_selobj_register(s, &callbacks); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to register selection object, error=%s.", + guest_error_str(error)); + return error; + } + + _callback = callback; + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Unix - Send +// ================= +GuestErrorT guest_unix_send( int s, void* msg, int msg_size ) +{ + int result; + + result = write(s, msg, msg_size); + if (0 > result) + { + DPRINTFE("Failed to write to socket, error=%s.", strerror(errno)); + return GUEST_FAILED; + } + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Unix - Receive +// ==================== +GuestErrorT guest_unix_receive( + int s, void* msg_buf, int msg_buf_size, int* msg_size ) +{ + int result; + + result = read(s, msg_buf, msg_buf_size); + if (0 > result) + { + if (EINTR == errno) + { + DPRINTFD("Interrupted on socket read, error=%s.", strerror(errno)); + return GUEST_INTERRUPTED; + } else if (ECONNRESET == errno) { + DPRINTFD("Peer connection reset, error=%s.", strerror(errno)); + *msg_size = 0; + return GUEST_OKAY; + } else { + DPRINTFE("Failed to read from socket, error=%s.", strerror(errno)); + return GUEST_FAILED; + } + } else if (0 == result) { + DPRINTFD("No message received from socket."); + *msg_size = 0; + return GUEST_OKAY; + } else { + DPRINTFV("Received message, msg_size=%i.", result); + *msg_size = result; + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Unix - Open +// ================= +GuestErrorT guest_unix_open( int* s ) +{ + int sock; + int reuse_addr = 1; + struct sockaddr_un local; + int result; + + *s = -1; + memset(&local, 0, sizeof(local)); + + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (0 > sock) + { + DPRINTFE("Failed to open socket, error=%s.", strerror(errno)); + return GUEST_FAILED; + } + + result = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, + sizeof(reuse_addr)); + if (0 > result) + { + DPRINTFE("Failed to set socket option (REUSEADDR), error=%s.", + strerror(errno)); + close(sock); + return GUEST_FAILED; + } + + result = fcntl(sock, F_SETFD, FD_CLOEXEC); + if (0 > result) + { + DPRINTFE("Failed to set to close on exec, error=%s.", strerror(errno)); + close(sock); + return GUEST_FAILED; + } + + result = fcntl(sock, F_GETFL); + if (0 > result) + { + DPRINTFE("Failed to get socket options, error=%s.", strerror(errno)); + close(sock); + return GUEST_FAILED; + } + + result = fcntl(sock, F_SETFL, result | O_NONBLOCK); + if (0 > result) + { + DPRINTFE("Failed to set socket options, error=%s.", strerror(errno)); + close(sock); + return GUEST_FAILED; + } + + *s = sock; + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Unix - Close +// ================== +GuestErrorT guest_unix_close( int s ) +{ + GuestErrorT error; + + if (0 <= s) + { + error = guest_selobj_deregister(s); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to deregister selection object, error=%s.", + guest_error_str(error)); + } + + close(s); + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Unix - Initialize +// ======================= +GuestErrorT guest_unix_initialize( void ) +{ + _callback = NULL; + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Unix - Finalize +// ===================== +GuestErrorT guest_unix_finalize( void ) +{ + _callback = NULL; + return GUEST_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_unix.h b/guest-client/guest-client-3.0.1/guest_client/src/guest_unix.h new file mode 100755 index 00000000..b06f070c --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_unix.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_UNIX_H__ +#define __GUEST_UNIX_H__ + +#include "guest_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*GuestUnixConnectCallbackT) (int selobj, char* address); + +// **************************************************************************** +// Guest Unix - Connect +// ==================== +extern GuestErrorT guest_unix_connect( int s, char* address ); +// **************************************************************************** + +// **************************************************************************** +// Guest Unix - Listen +// =================== +extern GuestErrorT guest_unix_listen( + int s, char* address, GuestUnixConnectCallbackT callback ); +// **************************************************************************** + +// **************************************************************************** +// Guest Unix - Send +// ================= +extern GuestErrorT guest_unix_send( int s, void* msg, int msg_size ); +// **************************************************************************** + +// **************************************************************************** +// Guest Unix - Receive +// ==================== +extern GuestErrorT guest_unix_receive( + int s, void* msg_buf, int msg_buf_size, int* msg_size ); +// **************************************************************************** + +// **************************************************************************** +// Guest Unix - Open +// ================= +extern GuestErrorT guest_unix_open( int* s ); +// **************************************************************************** + +// **************************************************************************** +// Guest Unix - Close +// ================== +extern GuestErrorT guest_unix_close( int s ); +// **************************************************************************** + +// **************************************************************************** +// Guest Unix - Initialize +// ======================= +extern GuestErrorT guest_unix_initialize( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Unix - Finalize +// ===================== +extern GuestErrorT guest_unix_finalize( void ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_UNIX_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_utils.c b/guest-client/guest-client-3.0.1/guest_client/src/guest_utils.c new file mode 100755 index 00000000..41633482 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_utils.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_utils.h" +#include "guest_debug.h" + +#include +#include + +// **************************************************************************** +// Guest Utilities - Trim +// ====================== +char* guest_utils_trim( char* str, char* discard ) +{ + int len; + int max_len; + + if (NULL == str) + return NULL; + + // Remove leading characters + max_len = strlen(str); + for (len=0; max_len > len; ++len) + { + if (isspace(*str) || '\n' == *str) + ++str; + else if ((NULL != discard) && (NULL != strchr(discard, *str))) + ++str; + } + + // Remove trailing characters + for (len=strlen(str)-1; 0 <= len; --len) + { + if (isspace(str[len]) || '\n' == str[len]) + str[len] = '\0'; + else if ((NULL != discard) && (NULL != strchr(discard, str[len]))) + str[len] = '\0'; + } + + return str; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Utilities - Base Name +// =========================== +char* guest_utils_basename( char* str ) +{ + const char* basename = str; + + while ('\0' != *str) + { + if (*str++ == '/') + basename = str; + } + + return (char*) basename; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Utilities - Get JSON Value from Key +// return 0 if success, -1 if fail. +// ========================================= +int guest_utils_json_get_value( struct json_object* jobj, + const char* key, void * value ) +{ + struct json_object *jobj_value; + if (!json_object_object_get_ex(jobj, key, &jobj_value)) + { + DPRINTFE("failed to parse %s\n", key); + return -1; + } + enum json_type type = json_object_get_type(jobj_value); + switch(type) + { + case json_type_boolean: + *(unsigned int *)value = json_object_get_boolean(jobj_value); + break; + case json_type_int: + *(unsigned int *)value = json_object_get_int(jobj_value); + break; + case json_type_double: + *(double *)value = json_object_get_double(jobj_value); + break; + case json_type_string: + strcpy(value, json_object_get_string(jobj_value)); + break; + default: + DPRINTFE("failed to parse %s, type %d is not supported\n", key, type); + return -1; + break; + } + return 0; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/guest_utils.h b/guest-client/guest-client-3.0.1/guest_client/src/guest_utils.h new file mode 100755 index 00000000..25b75b67 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/guest_utils.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_UTILITIES_H__ +#define __GUEST_UTILITIES_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define STRINGIZE(str) #str +#define MAKE_STRING(str) STRINGIZE(str) + +// **************************************************************************** +// Guest Utilities - Trim +// ====================== +extern char* guest_utils_trim( char* str, char* discard ); +// **************************************************************************** + +// **************************************************************************** +// Guest Utilities - Base Name +// =========================== +extern char* guest_utils_basename( char* str ); +// **************************************************************************** + +// **************************************************************************** +// Guest Utilities - Get JSON Value from Key +// return 0 if success, -1 if fail. +// ========================================= +extern int guest_utils_json_get_value( struct json_object* jobj, + const char* key, void * value ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_UTILITIES_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/Makefile b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/Makefile new file mode 100755 index 00000000..0c74c670 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/Makefile @@ -0,0 +1,62 @@ +# +# Copyright(c) 2013-2016, Wind River Systems, Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Wind River Systems nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +SHELL = /bin/sh + +## Configuration Directory Variables +prefix := /usr/local +exec_prefix := $(prefix) +sysconfdir := $(prefix)/etc +includedir := $(prefix)/include +libdir := $(exec_prefix)/lib +bindir := $(exec_prefix)/bin + +MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) +CURRENT_DIR := $(patsubst %/,%,$(dir $(MAKEFILE_PATH))) + +ifndef BUILD_DIR +$(error BUILD_DIR undefined) +endif + +.SUFFIXES: +.SUFFIXES: .c .o + +.PHONY: build + +heartbeat_C_INCLUDES := -I$(CURRENT_DIR) -I$(CURRENT_DIR)/../ +heartbeat_C_INCLUDES += -I$(CURRENT_DIR)/../../../include +heartbeat_C_SRCS := $(wildcard *.c) +heartbeat_C_OBJS := ${heartbeat_C_SRCS:.c=.o} + +CFLAGS = -g -O2 -Wall -Werror -Wformat -DSYSCONFDIR=$(sysconfdir) + +%.o: %.c + $(CC) $(CFLAGS) $(heartbeat_C_INCLUDES) -c $< -o $(BUILD_DIR)/$@ -ljson-c + +build: $(heartbeat_C_OBJS) diff --git a/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat.c b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat.c new file mode 100755 index 00000000..48638dbc --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat.c @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_heartbeat.h" + +#include +#include + +#include "guest_types.h" +#include "guest_debug.h" +#include "guest_timer.h" + +#include "guest_heartbeat_config.h" +#include "guest_heartbeat_msg.h" +#include "guest_heartbeat_fsm.h" +#include "guest_heartbeat_health_script.h" +#include "guest_heartbeat_event_script.h" +#include "guest_heartbeat_mgmt_api.h" + +static GuestTimerIdT _release_timer_id = GUEST_TIMER_ID_INVALID; + +// **************************************************************************** +// Guest Heartbeat - Release +// ========================= +static bool guest_heartbeat_release(GuestTimerIdT timer_id) +{ + GuestErrorT error; + + error = guest_heartbeat_fsm_event_handler(GUEST_HEARTBEAT_FSM_RELEASE, + NULL); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to handle release event, error=%s.", + guest_error_str(error)); + return true; + } + + return false; // don't rearm +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat - Channel State Change +// ====================================== +static void guest_heartbeat_channel_state_change( bool state ) +{ + GuestErrorT error; + + if (state) + { + error = guest_heartbeat_fsm_event_handler( + GUEST_HEARTBEAT_FSM_CHANNEL_UP, NULL); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to handle channel up event, error=%s.", + guest_error_str(error)); + return; + } + } else { + error = guest_heartbeat_fsm_event_handler( + GUEST_HEARTBEAT_FSM_CHANNEL_DOWN, NULL); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to handle channel down event, error=%s.", + guest_error_str(error)); + return; + } + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat - Receive Init Ack Message +// ========================================== +static void guest_heartbeat_recv_init_ack_msg( int invocation_id ) +{ + void* event_data[] = {&invocation_id}; + GuestErrorT error; + + error = guest_heartbeat_fsm_event_handler(GUEST_HEARTBEAT_FSM_INIT_ACK, + event_data); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to handle heartbeat-init-ack event, error=%s.", + guest_error_str(error)); + return; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat - Receive Challenge Message +// =========================================== +static void guest_heartbeat_recv_challenge_msg( void ) +{ + GuestErrorT error; + + error = guest_heartbeat_fsm_event_handler(GUEST_HEARTBEAT_FSM_CHALLENGE, + NULL); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to handle heartbeat-challenge event, error=%s.", + guest_error_str(error)); + return; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat - Receive Action Notify Message +// =============================================== +static void guest_heartbeat_recv_action_notify_msg( + int invocation_id, GuestHeartbeatEventT event, + GuestHeartbeatNotifyT notify, int timeout_ms) +{ + void* event_data[] = {&invocation_id, &event, ¬ify, &timeout_ms}; + GuestHeartbeatFsmStateT state = guest_heartbeat_fsm_get_state(); + GuestErrorT error; + + if (GUEST_HEARTBEAT_FSM_ENABLED_STATE == state) + { + error = guest_heartbeat_fsm_event_handler(GUEST_HEARTBEAT_FSM_ACTION, + event_data); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to handle action notify for event %s, error=%s.", + guest_heartbeat_event_str(event), guest_error_str(error)); + return; + } + + } else { + error = guest_heartbeat_msg_send_action_response( + invocation_id, event, notify, + GUEST_HEARTBEAT_VOTE_RESULT_COMPLETE, ""); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to send action response for event %s, error=%s.", + guest_heartbeat_event_str(event), guest_error_str(error)); + return; + } + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat - Initialize +// ============================ +GuestErrorT guest_heartbeat_initialize( char* comm_device ) +{ + GuestHeartbeatMsgCallbacksT callbacks; + GuestErrorT error; + + _release_timer_id = GUEST_TIMER_ID_INVALID; + + error = guest_heartbeat_config_initialize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to initialize heartbeat configuration, error=%s.", + guest_error_str(error)); + return error; + } + + memset(&callbacks, 0, sizeof(callbacks)); + + callbacks.channel_state_change = guest_heartbeat_channel_state_change; + callbacks.recv_init_ack = guest_heartbeat_recv_init_ack_msg; + callbacks.recv_challenge = guest_heartbeat_recv_challenge_msg; + callbacks.recv_action_notify = guest_heartbeat_recv_action_notify_msg; + + error = guest_heartbeat_msg_initialize(comm_device, &callbacks); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to initialize heartbeat messaging, error=%s.", + guest_error_str(error)); + return error; + } + + error = guest_heartbeat_fsm_initialize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to initialize heartbeat fsm, error=%s.", + guest_error_str(error)); + return error; + } + + error = guest_heartbeat_mgmt_api_initialize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to initialize heartbeat management api, error=%s.", + guest_error_str(error)); + return error; + } + + error = guest_heartbeat_health_script_initialize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to initialize heartbeat health script handling, " + "error=%s.", guest_error_str(error)); + return error; + } + + error = guest_heartbeat_event_script_initialize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to initialize heartbeat event script handling, " + "error=%s.", guest_error_str(error)); + return error; + } + + error = guest_timer_register(1000, guest_heartbeat_release, + &_release_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to start release timer, error=%s.", + guest_error_str(error)); + return error; + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat - Finalize +// ========================== +GuestErrorT guest_heartbeat_finalize( void ) +{ + GuestErrorT error; + + error = guest_heartbeat_fsm_event_handler(GUEST_HEARTBEAT_FSM_SHUTDOWN, + NULL); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to handle shutdown event, error=%s.", + guest_error_str(error)); + } + + if (GUEST_TIMER_ID_INVALID != _release_timer_id) + { + error = guest_timer_deregister(_release_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to cancel release timer, error=%s.", + guest_error_str(error)); + } + _release_timer_id = GUEST_TIMER_ID_INVALID; + } + + error = guest_heartbeat_event_script_finalize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to finalize heartbeat event script handling, " + "error=%s.", guest_error_str(error)); + } + + error = guest_heartbeat_health_script_finalize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to finalize heartbeat health script handling, " + "error=%s.", guest_error_str(error)); + } + + error = guest_heartbeat_mgmt_api_finalize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to finalize heartbeat management api, error=%s.", + guest_error_str(error)); + } + + error = guest_heartbeat_fsm_finalize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to finalize heartbeat fsm, error=%s.", + guest_error_str(error)); + } + + error = guest_heartbeat_msg_finalize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to finalize heartbeat messaging, error=%s.", + guest_error_str(error)); + } + + error = guest_heartbeat_config_finalize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to finalize heartbeat configuration, error=%s.", + guest_error_str(error)); + } + + return GUEST_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat.h b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat.h new file mode 100755 index 00000000..d8148674 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __GUEST_HEARTBEAT_H__ +#define __GUEST_HEARTBEAT_H__ + +#include "guest_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// **************************************************************************** +// Guest Heartbeat - Initialize +// ============================ +extern GuestErrorT guest_heartbeat_initialize( char* comm_device ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat - Finalize +// ========================== +extern GuestErrorT guest_heartbeat_finalize( void ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_HEARTBEAT_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_config.c b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_config.c new file mode 100755 index 00000000..c1e695d6 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_config.c @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_heartbeat_config.h" + +#include +#include +#include + +#include "guest_types.h" +#include "guest_debug.h" +#include "guest_utils.h" + +#include "guest_heartbeat_types.h" + +#ifndef SYSCONFDIR +#define GUEST_HEARTBEAT_DEFAULT_CONFIG_FILE \ + "/etc/guest-client/heartbeat/guest_heartbeat.conf" +#else +#define GUEST_HEARTBEAT_DEFAULT_CONFIG_FILE \ + MAKE_STRING(SYSCONFDIR) "/guest-client/heartbeat/guest_heartbeat.conf" +#endif + +#define GUEST_HEARTBEAT_DEFAULT_HEARTBEAT_INIT_RETRY_MS 5000 +#define GUEST_HEARTBEAT_DEFAULT_HEARTBEAT_MIN_TIMEOUT_MS 5000 +#define GUEST_HEARTBEAT_DEFAULT_HEARTBEAT_INTERVAL_MS 1000 +#define GUEST_HEARTBEAT_DEFAULT_VOTE_MS 10000 +#define GUEST_HEARTBEAT_DEFAULT_SHUTDOWN_MS 10000 +#define GUEST_HEARTBEAT_DEFAULT_SUSPEND_MS 10000 +#define GUEST_HEARTBEAT_DEFAULT_RESUME_MS 10000 +#define GUEST_HEARTBEAT_DEFAULT_RESTART_MS 120000 + +static GuestHeartbeatConfigT _config; + +// **************************************************************************** +// Guest Heartbeat Configuration - Read +// ==================================== +static GuestErrorT guest_heartbeat_config_read( char filename[] ) +{ + char* s; + char* key; + char* value; + char delimiter[] = "="; + char discard[] = "\'\""; + char buf[1024]; + FILE* fp; + + fp = fopen(filename, "r"); + if (NULL == fp) + { + DPRINTFE("Failed to open file %s.", filename); + return GUEST_FAILED; + } + + while (NULL != (s = fgets(buf, sizeof(buf), fp))) + { + s = guest_utils_trim(s, NULL); + + // Skip empty string and comments + if (('\0' == *s) || ('#' == *s)) + continue; + + key = strtok(s, delimiter); + value = strtok(NULL, delimiter); + + key = guest_utils_trim(key, discard); + value = guest_utils_trim(value, discard); + + if ((NULL != key) && (NULL != value)) + { + if (0 == strcmp("HB_INIT_RETRY", key)) + { + _config.heartbeat_init_retry_ms = atoi(value); + + } else if (0 == strcmp("HB_MIN_TIMEOUT", key)) { + _config.heartbeat_min_timeout_ms = atoi(value); + + } else if (0 == strcmp("HB_INTERVAL", key)) { + _config.heartbeat_interval_ms = atoi(value); + + } else if (0 == strcmp("VOTE", key)) { + _config.vote_ms = atoi(value) * 1000; + + } else if (0 == strcmp("SHUTDOWN_NOTICE", key)) { + _config.shutdown_notice_ms = atoi(value) * 1000; + + } else if (0 == strcmp("SUSPEND_NOTICE", key)) { + _config.suspend_notice_ms = atoi(value) * 1000; + + } else if (0 == strcmp("RESUME_NOTICE", key)) { + _config.resume_notice_ms = atoi(value) * 1000; + + } else if (0 == strcmp("RESTART", key)) { + _config.restart_ms = atoi(value) * 1000; + + } else if (0 == strcmp("CORRECTIVE_ACTION", key)) { + if (0 == strcmp("reboot", value)) + { + _config.corrective_action = GUEST_HEARTBEAT_ACTION_REBOOT; + + } else if (0 == strcmp("stop", value)) { + _config.corrective_action = GUEST_HEARTBEAT_ACTION_STOP; + + } else if (0 == strcmp("log", value)) { + _config.corrective_action = GUEST_HEARTBEAT_ACTION_LOG; + } + + } else if (0 == strcmp("HEALTH_CHECK_INTERVAL", key)) { + _config.health_check_interval_ms = atoi(value) * 1000; + + } else if (0 == strcmp("HEALTH_CHECK_SCRIPT", key)) { + snprintf(_config.health_check_script, + sizeof(_config.health_check_script), "%s", value); + + } else if (0 == strcmp("EVENT_NOTIFICATION_SCRIPT", key)) { + snprintf(_config.event_handling_script, + sizeof(_config.event_handling_script), "%s", value); + + } else { + DPRINTFE("Unknown key %s in configuration file %s.", key, + filename); + } + } + } + + fclose(fp); + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Configuration - Dump +// ==================================== +static void guest_heartbeat_config_dump( void ) +{ + DPRINTFI("Guest-Client Heartbeat Configuration:"); + DPRINTFI(" heartbeat-init-retry: %i ms", _config.heartbeat_init_retry_ms); + DPRINTFI(" heartbeat-interval: %i ms", _config.heartbeat_interval_ms); + DPRINTFI(" heartbeat-min-timeout: %i ms", _config.heartbeat_min_timeout_ms); + DPRINTFI(" vote: %i ms", _config.vote_ms); + DPRINTFI(" shutdown-notice: %i ms", _config.shutdown_notice_ms); + DPRINTFI(" suspend-notice: %i ms", _config.suspend_notice_ms); + DPRINTFI(" resume-notice: %i ms", _config.resume_notice_ms); + DPRINTFI(" restart: %i ms", _config.restart_ms); + DPRINTFI(" health-check-interval: %i ms", _config.health_check_interval_ms); + DPRINTFI(" health-check-script: %s", _config.health_check_script); + DPRINTFI(" event-handling-script: %s", _config.event_handling_script); + DPRINTFI(" corrective-action: %s", + guest_heartbeat_action_str(_config.corrective_action)); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Configuration - Get +// =================================== +GuestHeartbeatConfigT* guest_heartbeat_config_get( void ) +{ + return &_config; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Configuration - Initialize +// ========================================== +GuestErrorT guest_heartbeat_config_initialize( void ) +{ + GuestErrorT error; + + memset(&_config, 0, sizeof(GuestHeartbeatConfigT)); + + _config.heartbeat_init_retry_ms = GUEST_HEARTBEAT_DEFAULT_HEARTBEAT_INIT_RETRY_MS; + _config.heartbeat_min_timeout_ms = GUEST_HEARTBEAT_DEFAULT_HEARTBEAT_MIN_TIMEOUT_MS; + _config.heartbeat_interval_ms = GUEST_HEARTBEAT_DEFAULT_HEARTBEAT_INTERVAL_MS; + _config.vote_ms = GUEST_HEARTBEAT_DEFAULT_VOTE_MS; + _config.shutdown_notice_ms = GUEST_HEARTBEAT_DEFAULT_SHUTDOWN_MS; + _config.suspend_notice_ms = GUEST_HEARTBEAT_DEFAULT_SUSPEND_MS; + _config.resume_notice_ms = GUEST_HEARTBEAT_DEFAULT_RESUME_MS; + _config.restart_ms = GUEST_HEARTBEAT_DEFAULT_RESTART_MS; + _config.corrective_action = GUEST_HEARTBEAT_ACTION_REBOOT; + + error = guest_heartbeat_config_read(GUEST_HEARTBEAT_DEFAULT_CONFIG_FILE); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to read guest heartbeat configuration, error=%s.", + guest_error_str(error)); + return error; + } + + guest_heartbeat_config_dump(); + + if (GUEST_HEARTBEAT_MIN_INTERVAL_MS > _config.heartbeat_interval_ms) + { + DPRINTFE("Guest heartbeat interval configuration is less than %i ms.", + GUEST_HEARTBEAT_MIN_INTERVAL_MS); + return GUEST_FAILED; + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Configuration - Finalize +// ======================================== +GuestErrorT guest_heartbeat_config_finalize( void ) +{ + memset(&_config, 0, sizeof(GuestHeartbeatConfigT)); + return GUEST_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_config.h b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_config.h new file mode 100755 index 00000000..6fd54a49 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_config.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_HERATBEAT_CONFIGURATION_H__ +#define __GUEST_HEARTBEAT_CONFIGURATION_H__ + +#include "guest_limits.h" +#include "guest_types.h" + +#include "guest_heartbeat_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int heartbeat_init_retry_ms; + int heartbeat_interval_ms; + int heartbeat_min_timeout_ms; + int vote_ms; + int shutdown_notice_ms; + int suspend_notice_ms; + int resume_notice_ms; + int restart_ms; + GuestHeartbeatActionT corrective_action; + int health_check_interval_ms; + char health_check_script[255]; + char event_handling_script[255]; +} GuestHeartbeatConfigT; + +// **************************************************************************** +// Guest Heartbeat Configuration - Get +// =================================== +extern GuestHeartbeatConfigT* guest_heartbeat_config_get( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Configuration - Initialize +// ========================================== +extern GuestErrorT guest_heartbeat_config_initialize( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Configuration - Finalize +// ======================================== +extern GuestErrorT guest_heartbeat_config_finalize( void ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_HEARTBEAT_CONFIGURATION_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_disabled_state.c b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_disabled_state.c new file mode 100755 index 00000000..97af2984 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_disabled_state.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_heartbeat_disabled_state.h" + +#include +#include +#include + +#include "guest_types.h" +#include "guest_debug.h" +#include "guest_timer.h" + +#include "guest_heartbeat_fsm.h" + +// **************************************************************************** +// Guest Heartbeat Disabled State - Enter +// ====================================== +GuestErrorT guest_heartbeat_disabled_state_enter( void ) +{ + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Disabled State - Exit +// ===================================== +GuestErrorT guest_heartbeat_disabled_state_exit( void ) +{ + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Disabled State - Transition +// =========================================== +GuestErrorT guest_heartbeat_disabled_state_transition( + GuestHeartbeatFsmStateT from_state ) +{ + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Disabled State - Event Handler +// ============================================== +GuestErrorT guest_heartbeat_disabled_state_event_handler( + GuestHeartbeatFsmEventT event, void* event_data[] ) +{ + switch (event) + { + case GUEST_HEARTBEAT_FSM_RELEASE: + case GUEST_HEARTBEAT_FSM_INIT_ACK: + case GUEST_HEARTBEAT_FSM_CHALLENGE: + case GUEST_HEARTBEAT_FSM_CHALLENGE_TIMEOUT: + case GUEST_HEARTBEAT_FSM_CHANNEL_DOWN: + case GUEST_HEARTBEAT_FSM_ACTION: + // Ignore + break; + + case GUEST_HEARTBEAT_FSM_CHANNEL_UP: + guest_heartbeat_fsm_set_state(GUEST_HEARTBEAT_FSM_ENABLING_STATE); + break; + + case GUEST_HEARTBEAT_FSM_SHUTDOWN: + guest_heartbeat_fsm_set_state(GUEST_HEARTBEAT_FSM_INITIAL_STATE); + break; + + default: + DPRINTFE("Ignoring event %s.", + guest_heartbeat_fsm_event_str(event)); + } + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Disabled State - Initialize +// =========================================== +GuestErrorT guest_heartbeat_disabled_state_initialize( void ) +{ + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Disabled State - Finalize +// ========================================= +GuestErrorT guest_heartbeat_disabled_state_finalize( void ) +{ + return GUEST_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_disabled_state.h b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_disabled_state.h new file mode 100755 index 00000000..38f88af9 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_disabled_state.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_HEARTBEAT_DISABLED_STATE_H__ +#define __GUEST_HEARTBEAT_DISABLED_STATE_H__ + +#include "guest_types.h" +#include "guest_heartbeat_fsm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// **************************************************************************** +// Guest Heartbeat Disabled State - Enter +// ====================================== +extern GuestErrorT guest_heartbeat_disabled_state_enter( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Disabled State - Exit +// ===================================== +extern GuestErrorT guest_heartbeat_disabled_state_exit( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Disabled State - Transition +// =========================================== +extern GuestErrorT guest_heartbeat_disabled_state_transition( + GuestHeartbeatFsmStateT from_state ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Disabled State - Event Handler +// ============================================== +extern GuestErrorT guest_heartbeat_disabled_state_event_handler( + GuestHeartbeatFsmEventT event, void* event_data[] ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Disabled State - Initialize +// =========================================== +extern GuestErrorT guest_heartbeat_disabled_state_initialize( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Disabled State - Finalize +// ========================================= +extern GuestErrorT guest_heartbeat_disabled_state_finalize( void ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_HEARTBEAT_DISABLED_STATE_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_enabled_state.c b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_enabled_state.c new file mode 100755 index 00000000..03cdd99c --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_enabled_state.c @@ -0,0 +1,698 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_heartbeat_enabled_state.h" + +#include +#include +#include +#include + +#include "guest_types.h" +#include "guest_debug.h" +#include "guest_time.h" +#include "guest_timer.h" + +#include "guest_heartbeat_config.h" +#include "guest_heartbeat_msg.h" +#include "guest_heartbeat_fsm.h" +#include "guest_heartbeat_health_script.h" +#include "guest_heartbeat_event_script.h" +#include "guest_heartbeat_mgmt_api.h" + +static bool _wait_application; +static bool _wait_script; +static int _action_invocation_id; +static GuestHeartbeatEventT _action_event; +static GuestHeartbeatNotifyT _action_notify; +GuestHeartbeatVoteResultT _vote_result; +static int _action_timeout_ms; +static GuestTimerIdT _health_check_timer_id = GUEST_TIMER_ID_INVALID; +static GuestTimerIdT _challenge_timeout_timer_id = GUEST_TIMER_ID_INVALID; +static GuestTimerIdT _action_timeout_timer_id = GUEST_TIMER_ID_INVALID; +static bool _health = true; +static char _health_log_msg[GUEST_HEARTBEAT_MAX_LOG_MSG_SIZE]; +static char _action_log_msg[GUEST_HEARTBEAT_MAX_LOG_MSG_SIZE]; +static GuestTimeT _last_time_reported; +static bool _last_health_reported = true; +static GuestHeartbeatActionT _last_corrective_action_reported; + +// **************************************************************************** +// Guest Heartbeat Enabled State - Health Callback +// =============================================== +static void guest_heartbeat_enabled_state_health_callback( + bool health, char* log_msg ) +{ + if (_health && !health) + { + DPRINTFI("Transition from healthy to unhealthy, msg=%s.", log_msg); + + } else if (!_health && health) { + DPRINTFI("Transition from unhealthy to healthy, msg=%s.", log_msg); + } + + _health = health; + snprintf(_health_log_msg, sizeof(_health_log_msg), "%s", log_msg); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabled State - Health Check +// ============================================ +static bool guest_heartbeat_enabled_state_health_check( + GuestTimerIdT timer_id ) +{ + GuestHeartbeatConfigT* config = guest_heartbeat_config_get(); + GuestErrorT error; + + guest_heartbeat_health_script_abort(); + + if ('\0' != config->health_check_script[0]) + { + error = guest_heartbeat_health_script_invoke( + config->health_check_script, + guest_heartbeat_enabled_state_health_callback); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to invoke health script %s.", + config->event_handling_script); + return true; // rearm + } + } + + return true; // rearm +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabled State - Challenge Timeout +// ================================================= +static bool guest_heartbeat_enabled_state_challenge_timeout( + GuestTimerIdT timer_id ) +{ + GuestHeartbeatConfigT* config = guest_heartbeat_config_get(); + GuestErrorT error; + int max_heartbeat_delay; + + max_heartbeat_delay = config->heartbeat_interval_ms*2; + if (max_heartbeat_delay < config->heartbeat_min_timeout_ms) + max_heartbeat_delay = config->heartbeat_min_timeout_ms; + + if (!guest_timer_scheduling_on_time_within(max_heartbeat_delay)) + { + DPRINTFE("Failed to receive a challenge in %i ms, but we are not " + "scheduling on time.", max_heartbeat_delay); + return true; // rearm + } + + DPRINTFE("Failed to receive a challenge in %i ms.", max_heartbeat_delay); + + error = guest_heartbeat_fsm_event_handler(GUEST_HEARTBEAT_FSM_CHALLENGE_TIMEOUT, + NULL); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to handle heartbeat-challenge-timeout event, " + "error=%s.", guest_error_str(error)); + return true; // rearm + } + + return true; // rearm +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabled State - Action Timeout +// ============================================== +static bool guest_heartbeat_enabled_state_action_timeout( + GuestTimerIdT timer_id ) +{ + char log_msg[GUEST_HEARTBEAT_MAX_LOG_MSG_SIZE]; + GuestHeartbeatVoteResultT vote_result; + GuestErrorT error; + + if (!guest_timer_scheduling_on_time_within(_action_timeout_ms)) + { + DPRINTFE("Failed to receive action script response in %i ms, but we " + "are not scheduling on time.", _action_timeout_ms); + return true; + } + + DPRINTFE("Failed to receive action responses in %i ms.", + _action_timeout_ms); + + guest_heartbeat_mgmt_api_action_abort(); + guest_heartbeat_event_script_abort(); + + if (((!_wait_application) || (!_wait_script)) && + (GUEST_HEARTBEAT_VOTE_RESULT_REJECT == _vote_result)) + { + vote_result = _vote_result; + snprintf(log_msg, sizeof(log_msg), "%s", _action_log_msg); + } else { + vote_result = GUEST_HEARTBEAT_VOTE_RESULT_TIMEOUT; + snprintf(log_msg, sizeof(log_msg), "Timeout on application and/or " + "script action responses."); + } + + error = guest_heartbeat_msg_send_action_response( + _action_invocation_id, _action_event, _action_notify, + vote_result, log_msg); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to send action script response for event %s, " + "notification=%s, error=%s.", + guest_heartbeat_event_str(_action_event), + guest_heartbeat_notify_str(_action_notify), + guest_error_str(error)); + } + + _action_timeout_timer_id = GUEST_TIMER_ID_INVALID; + return false; // don't rearm +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabled State - Action Application Callback +// =========================================================== +static void guest_heartbeat_enabled_state_action_app_callback( + GuestHeartbeatEventT event, GuestHeartbeatNotifyT notify, + GuestHeartbeatVoteResultT vote_result, char* log_msg ) +{ + bool update; + GuestErrorT error; + + DPRINTFI("Received action application response, event=%s, notify=%s, " + "vote-result=%s, msg=%s.", guest_heartbeat_event_str(event), + guest_heartbeat_notify_str(notify), + guest_heartbeat_vote_result_str(vote_result), log_msg); + + _wait_application = false; + update = false; + + switch (vote_result) + { + case GUEST_HEARTBEAT_VOTE_RESULT_REJECT: + update = true; + break; + + case GUEST_HEARTBEAT_VOTE_RESULT_ACCEPT: + case GUEST_HEARTBEAT_VOTE_RESULT_COMPLETE: + if (GUEST_HEARTBEAT_VOTE_RESULT_REJECT != _vote_result) + update = true; + break; + + default: + update = false; + break; + } + + if (update) + { + _action_event = event; + _action_notify = notify; + _vote_result = vote_result; + snprintf(_action_log_msg, sizeof(_action_log_msg), "%s", log_msg); + } + + if (!_wait_script) + { + error = guest_heartbeat_msg_send_action_response( + _action_invocation_id, _action_event, _action_notify, + _vote_result, _action_log_msg); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to send action response for event %s, " + "notification=%s, error=%s.", + guest_heartbeat_event_str(_action_event), + guest_heartbeat_notify_str(_action_notify), + guest_error_str(error)); + } + + if (GUEST_TIMER_ID_INVALID != _action_timeout_timer_id) + { + error = guest_timer_deregister(_action_timeout_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to cancel action script timeout timer, " + "error=%s.", guest_error_str(error)); + } + _action_timeout_timer_id = GUEST_TIMER_ID_INVALID; + } + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabled State - Action Script Callback +// ====================================================== +static void guest_heartbeat_enabled_state_action_script_callback( + GuestHeartbeatEventT event, GuestHeartbeatNotifyT notify, + GuestHeartbeatVoteResultT vote_result, char* log_msg ) +{ + bool update; + GuestErrorT error; + + DPRINTFI("Received event script response, event=%s, notify=%s, " + "vote-result=%s, msg=%s.", guest_heartbeat_event_str(event), + guest_heartbeat_notify_str(notify), + guest_heartbeat_vote_result_str(vote_result), log_msg); + + _wait_script = false; + update = false; + + switch (vote_result) + { + case GUEST_HEARTBEAT_VOTE_RESULT_REJECT: + update = true; + break; + + case GUEST_HEARTBEAT_VOTE_RESULT_ACCEPT: + case GUEST_HEARTBEAT_VOTE_RESULT_COMPLETE: + if (GUEST_HEARTBEAT_VOTE_RESULT_REJECT != _vote_result) + update = true; + break; + + default: + update = false; + break; + } + + if (update) + { + _action_event = event; + _action_notify = notify; + _vote_result = vote_result; + snprintf(_action_log_msg, sizeof(_action_log_msg), "%s", log_msg); + } + + if (!_wait_application) + { + error = guest_heartbeat_msg_send_action_response( + _action_invocation_id, _action_event, _action_notify, + _vote_result, _action_log_msg); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to send action response for event %s, " + "notification=%s, error=%s.", + guest_heartbeat_event_str(_action_event), + guest_heartbeat_notify_str(_action_notify), + guest_error_str(error)); + } + + if (GUEST_TIMER_ID_INVALID != _action_timeout_timer_id) + { + error = guest_timer_deregister(_action_timeout_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to cancel action script timeout timer, " + "error=%s.", guest_error_str(error)); + } + _action_timeout_timer_id = GUEST_TIMER_ID_INVALID; + } + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabled State - Enter +// ===================================== +GuestErrorT guest_heartbeat_enabled_state_enter( void ) +{ + int heartbeat_timeout; + GuestHeartbeatConfigT* config = guest_heartbeat_config_get(); + GuestErrorT error; + + _health = true; + _last_health_reported = true; + + heartbeat_timeout = config->heartbeat_interval_ms*2; + if (heartbeat_timeout < config->heartbeat_min_timeout_ms) + heartbeat_timeout = config->heartbeat_min_timeout_ms; + + error = guest_timer_register(heartbeat_timeout, + guest_heartbeat_enabled_state_challenge_timeout, + &_challenge_timeout_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to start challenge timeout timer, error=%s.", + guest_error_str(error)); + return error; + } + + if ((0 != config->health_check_interval_ms) && + ('\0' != config->health_check_script[0])) + { + error = guest_timer_register(config->health_check_interval_ms, + guest_heartbeat_enabled_state_health_check, + &_health_check_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to start health check timer, error=%s.", + guest_error_str(error)); + return error; + } + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabled State - Exit +// ==================================== +GuestErrorT guest_heartbeat_enabled_state_exit( void ) +{ + GuestErrorT error; + + if (GUEST_TIMER_ID_INVALID != _challenge_timeout_timer_id) + { + error = guest_timer_deregister(_challenge_timeout_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to cancel challenge timeout timer, error=%s.", + guest_error_str(error)); + } + _challenge_timeout_timer_id = GUEST_TIMER_ID_INVALID; + } + + if (GUEST_TIMER_ID_INVALID != _health_check_timer_id) + { + error = guest_timer_deregister(_health_check_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to cancel health check timer, error=%s.", + guest_error_str(error)); + } + _health_check_timer_id = GUEST_TIMER_ID_INVALID; + } + + if (GUEST_TIMER_ID_INVALID != _action_timeout_timer_id) + { + error = guest_timer_deregister(_action_timeout_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to cancel action timeout timer, error=%s.", + guest_error_str(error)); + } + _action_timeout_timer_id = GUEST_TIMER_ID_INVALID; + } + + guest_heartbeat_health_script_abort(); + guest_heartbeat_event_script_abort(); + guest_heartbeat_mgmt_api_action_abort(); + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabled State - Transition +// ========================================== +GuestErrorT guest_heartbeat_enabled_state_transition( + GuestHeartbeatFsmStateT from_state ) +{ + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabled State - Event Handler +// ============================================= +GuestErrorT guest_heartbeat_enabled_state_event_handler( + GuestHeartbeatFsmEventT event, void* event_data[] ) +{ + bool health = true; + GuestHeartbeatActionT corrective_action = GUEST_HEARTBEAT_ACTION_NONE; + char log_msg[GUEST_HEARTBEAT_MAX_LOG_MSG_SIZE]; + GuestHeartbeatConfigT* config = guest_heartbeat_config_get(); + GuestErrorT error; + + switch (event) { + case GUEST_HEARTBEAT_FSM_RELEASE: + case GUEST_HEARTBEAT_FSM_INIT_ACK: + case GUEST_HEARTBEAT_FSM_CHANNEL_UP: + // Ignore. + break; + + case GUEST_HEARTBEAT_FSM_CHANNEL_DOWN: + guest_heartbeat_fsm_set_state(GUEST_HEARTBEAT_FSM_DISABLED_STATE); + break; + + case GUEST_HEARTBEAT_FSM_CHALLENGE: + guest_timer_reset(_challenge_timeout_timer_id); + + error = guest_heartbeat_mgmt_api_get_health(&health, + &corrective_action, log_msg, + GUEST_HEARTBEAT_MAX_LOG_MSG_SIZE); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to get application health, error=%s.", + guest_error_str(error)); + } + + if (health) + { + // Applications are healthy, use the last health script status. + health = _health; + corrective_action = config->corrective_action; + snprintf(log_msg, GUEST_HEARTBEAT_MAX_LOG_MSG_SIZE, "%s", + _health_log_msg); + + } else if (!_health) { + // Applications are not healthy and the health script status + // is not healthy, need to merge the corrective action taken. + corrective_action = guest_heartbeat_merge_action( + corrective_action, + config->corrective_action); + + if (corrective_action == config->corrective_action) + { + snprintf(log_msg, GUEST_HEARTBEAT_MAX_LOG_MSG_SIZE, "%s", + _health_log_msg); + } + } + + if (!health) + { + if ((health == _last_health_reported) && + (corrective_action == _last_corrective_action_reported) && + (60000 > guest_time_get_elapsed_ms(&_last_time_reported))) + { + DPRINTFD("Unhealthy, already reported corrective action " + "%s, setting corrective action to none.", + guest_heartbeat_action_str(corrective_action)); + + // Don't keep asking for a corrective action to be taken + // over and over again at the heartbeat interval if it has + // already been reported. + corrective_action = GUEST_HEARTBEAT_ACTION_NONE; + + } else { + _last_health_reported = health; + _last_corrective_action_reported = corrective_action; + memset(&_last_time_reported, 0, sizeof(_last_time_reported)); + + DPRINTFI("Unhealthy, reporting corrective action %s.", + guest_heartbeat_action_str(corrective_action)); + } + } else { + _last_health_reported = true; + _last_corrective_action_reported = GUEST_HEARTBEAT_ACTION_NONE; + memset(&_last_time_reported, 0, sizeof(_last_time_reported)); + } + + error = guest_heartbeat_msg_send_challenge_response(health, + corrective_action, log_msg); + if (GUEST_OKAY == error) + { + if (GUEST_HEARTBEAT_ACTION_NONE != corrective_action) + { + guest_time_get(&_last_time_reported); + } + } else { + DPRINTFE("Failed to send challenge response, error=%s.", + guest_error_str(error)); + return GUEST_OKAY; + } + break; + + case GUEST_HEARTBEAT_FSM_CHALLENGE_TIMEOUT: + guest_heartbeat_fsm_set_state(GUEST_HEARTBEAT_FSM_ENABLING_STATE); + break; + + case GUEST_HEARTBEAT_FSM_ACTION: + guest_heartbeat_mgmt_api_action_abort(); + guest_heartbeat_event_script_abort(); + + _wait_application = false; + _wait_script = false; + _action_invocation_id = *(int*) event_data[0]; + _action_event = *(GuestHeartbeatEventT*) event_data[1]; + _action_notify = *(GuestHeartbeatNotifyT*) event_data[2]; + _action_timeout_ms = *(int*) event_data[3]; + _vote_result = GUEST_HEARTBEAT_VOTE_RESULT_UNKNOWN; + + error = guest_heartbeat_mgmt_api_action_notify( + _action_event, _action_notify, &_wait_application, + guest_heartbeat_enabled_state_action_app_callback); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to notify applications for event %s, " + "notification=%s.", + guest_heartbeat_event_str(_action_event), + guest_heartbeat_notify_str(_action_notify)); + } + + if ('\0' != config->event_handling_script[0]) + { + DPRINTFI("Invoke event script %s for event %s, " + "notification=%s.", config->event_handling_script, + guest_heartbeat_event_str(_action_event), + guest_heartbeat_notify_str(_action_notify)); + + error = guest_heartbeat_event_script_invoke( + config->event_handling_script, + _action_event, _action_notify, + guest_heartbeat_enabled_state_action_script_callback); + if (GUEST_OKAY == error) + { + _wait_script = true; + + } else { + DPRINTFE("Failed to invoke event script %s for event %s, " + "notification=%s.", config->event_handling_script, + guest_heartbeat_event_str(_action_event), + guest_heartbeat_notify_str(_action_notify)); + } + } + + if (_wait_application || _wait_script) + { + error = guest_timer_register(_action_timeout_ms, + guest_heartbeat_enabled_state_action_timeout, + &_action_timeout_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to start action timeout timer, error=%s.", + guest_error_str(error)); + guest_heartbeat_mgmt_api_action_abort(); + guest_heartbeat_event_script_abort(); + return GUEST_OKAY; + } + } else { + error = guest_heartbeat_msg_send_action_response( + _action_invocation_id, _action_event, + _action_notify, GUEST_HEARTBEAT_VOTE_RESULT_COMPLETE, + ""); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to send action response for event %s, " + "notification=%s, error=%s.", + guest_heartbeat_event_str(_action_event), + guest_heartbeat_notify_str(_action_notify), + guest_error_str(error)); + } + } + break; + + case GUEST_HEARTBEAT_FSM_SHUTDOWN: + error = guest_heartbeat_msg_send_exit("Exiting..."); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to send exit, error=%s.", + guest_error_str(error)); + } + + guest_heartbeat_fsm_set_state(GUEST_HEARTBEAT_FSM_INITIAL_STATE); + break; + + default: + DPRINTFE("Ignoring event %s.", + guest_heartbeat_fsm_event_str(event)); + } + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabled State - Initialize +// ========================================== +GuestErrorT guest_heartbeat_enabled_state_initialize( void ) +{ + _health = true; + _health_check_timer_id = GUEST_TIMER_ID_INVALID; + _challenge_timeout_timer_id = GUEST_TIMER_ID_INVALID; + _action_timeout_timer_id = GUEST_TIMER_ID_INVALID; + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabled State - Finalize +// ======================================== +GuestErrorT guest_heartbeat_enabled_state_finalize( void ) +{ + GuestErrorT error; + + if (GUEST_TIMER_ID_INVALID != _challenge_timeout_timer_id) + { + error = guest_timer_deregister(_challenge_timeout_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to cancel challenge timeout timer, error=%s.", + guest_error_str(error)); + } + _challenge_timeout_timer_id = GUEST_TIMER_ID_INVALID; + } + + if (GUEST_TIMER_ID_INVALID != _health_check_timer_id) + { + error = guest_timer_deregister(_health_check_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to cancel health check timer, error=%s.", + guest_error_str(error)); + } + _health_check_timer_id = GUEST_TIMER_ID_INVALID; + } + + if (GUEST_TIMER_ID_INVALID != _action_timeout_timer_id) + { + error = guest_timer_deregister(_action_timeout_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to cancel action timeout timer, error=%s.", + guest_error_str(error)); + } + _action_timeout_timer_id = GUEST_TIMER_ID_INVALID; + } + + _health = false; + + return GUEST_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_enabled_state.h b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_enabled_state.h new file mode 100755 index 00000000..7b71065b --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_enabled_state.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_HEARTBEAT_ENABLED_STATE_H__ +#define __GUEST_HEARTBEAT_ENABLED_STATE_H__ + +#include "guest_types.h" +#include "guest_heartbeat_fsm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// **************************************************************************** +// Guest Heartbeat Enabled State - Enter +// ===================================== +extern GuestErrorT guest_heartbeat_enabled_state_enter( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabled State - Exit +// ==================================== +extern GuestErrorT guest_heartbeat_enabled_state_exit( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabled State - Transition +// ========================================== +extern GuestErrorT guest_heartbeat_enabled_state_transition( + GuestHeartbeatFsmStateT from_state ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabled State - Event Handler +// ============================================= +extern GuestErrorT guest_heartbeat_enabled_state_event_handler( + GuestHeartbeatFsmEventT event, void* event_data[] ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabled State - Initialize +// ========================================== +extern GuestErrorT guest_heartbeat_enabled_state_initialize( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabled State - Finalize +// ======================================== +extern GuestErrorT guest_heartbeat_enabled_state_finalize( void ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_HEARTBEAT_ENABLED_STATE_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_enabling_state.c b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_enabling_state.c new file mode 100755 index 00000000..826f2a36 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_enabling_state.c @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_heartbeat_enabling_state.h" + +#include +#include +#include + +#include "guest_types.h" +#include "guest_debug.h" +#include "guest_timer.h" +#include "guest_config.h" + +#include "guest_heartbeat_config.h" +#include "guest_heartbeat_msg.h" +#include "guest_heartbeat_fsm.h" + +static int _prev_invocation_id; +static int _invocation_id; +static GuestTimerIdT _connect_timer_id = GUEST_TIMER_ID_INVALID; + +// **************************************************************************** +// Guest Heartbeat Enabling State - Attempt Connect +// ================================================ +static bool guest_heartbeat_enabling_state_connect( GuestTimerIdT timer_id ) +{ + GuestConfigT* cfg = guest_config_get(); + GuestHeartbeatMsgInitDataT data; + GuestHeartbeatConfigT* config = guest_heartbeat_config_get(); + GuestErrorT error; + + _prev_invocation_id = _invocation_id; + _invocation_id = rand(); + + memset(&data, 0, sizeof(data)); + snprintf(data.name, GUEST_NAME_MAX_CHAR, "%s", cfg->name); + data.heartbeat_interval_ms = config->heartbeat_interval_ms; + data.vote_ms = config->vote_ms; + data.shutdown_notice_ms = config->shutdown_notice_ms; + data.suspend_notice_ms = config->suspend_notice_ms; + data.resume_notice_ms = config->resume_notice_ms; + data.restart_ms = config->restart_ms; + data.corrective_action = config->corrective_action; + + error = guest_heartbeat_msg_send_init(_invocation_id, &data); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to send heartbeat init message, error=%s.", + guest_error_str(error)); + return true; + } + + return true; // rearm +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabling State - Enter +// ====================================== +GuestErrorT guest_heartbeat_enabling_state_enter( void ) +{ + GuestHeartbeatConfigT* config = guest_heartbeat_config_get(); + GuestErrorT error; + + error = guest_timer_register(config->heartbeat_init_retry_ms, + guest_heartbeat_enabling_state_connect, + &_connect_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to start connect timer, error=%s.", + guest_error_str(error)); + return error; + } + + guest_heartbeat_enabling_state_connect(_connect_timer_id); + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabling State - Exit +// ===================================== +GuestErrorT guest_heartbeat_enabling_state_exit( void ) +{ + GuestErrorT error; + + if (GUEST_TIMER_ID_INVALID != _connect_timer_id) + { + error = guest_timer_deregister(_connect_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to cancel connect timer, error=%s.", + guest_error_str(error)); + } + _connect_timer_id = GUEST_TIMER_ID_INVALID; + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabling State - Transition +// =========================================== +GuestErrorT guest_heartbeat_enabling_state_transition( + GuestHeartbeatFsmStateT from_state ) +{ + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabling State - Event Handler +// ============================================== +GuestErrorT guest_heartbeat_enabling_state_event_handler( + GuestHeartbeatFsmEventT event, void* event_data[] ) +{ + int invocation_id; + + switch (event) + { + case GUEST_HEARTBEAT_FSM_RELEASE: + case GUEST_HEARTBEAT_FSM_CHALLENGE: + case GUEST_HEARTBEAT_FSM_CHALLENGE_TIMEOUT: + case GUEST_HEARTBEAT_FSM_ACTION: + case GUEST_HEARTBEAT_FSM_CHANNEL_UP: + // Ignore. + break; + + case GUEST_HEARTBEAT_FSM_CHANNEL_DOWN: + guest_heartbeat_fsm_set_state(GUEST_HEARTBEAT_FSM_DISABLED_STATE); + break; + + case GUEST_HEARTBEAT_FSM_INIT_ACK: + invocation_id = *(int*) event_data[0]; + + if ((invocation_id == _invocation_id) || + (invocation_id == _prev_invocation_id)) + guest_heartbeat_fsm_set_state(GUEST_HEARTBEAT_FSM_ENABLED_STATE); + break; + + case GUEST_HEARTBEAT_FSM_SHUTDOWN: + guest_heartbeat_fsm_set_state(GUEST_HEARTBEAT_FSM_INITIAL_STATE); + break; + + default: + DPRINTFE("Ignoring event %s.", + guest_heartbeat_fsm_event_str(event)); + } + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabling State - Initialize +// =========================================== +GuestErrorT guest_heartbeat_enabling_state_initialize( void ) +{ + _connect_timer_id = GUEST_TIMER_ID_INVALID; + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabling State - Finalize +// ========================================= +GuestErrorT guest_heartbeat_enabling_state_finalize( void ) +{ + GuestErrorT error; + + if (GUEST_TIMER_ID_INVALID != _connect_timer_id) + { + error = guest_timer_deregister(_connect_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to cancel connect timer, error=%s.", + guest_error_str(error)); + } + _connect_timer_id = GUEST_TIMER_ID_INVALID; + } + + return GUEST_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_enabling_state.h b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_enabling_state.h new file mode 100755 index 00000000..df17931f --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_enabling_state.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_HEARTBEAT_ENABLING_STATE_H__ +#define __GUEST_HEARTBEAT_ENABLING_STATE_H__ + +#include "guest_types.h" +#include "guest_heartbeat_fsm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// **************************************************************************** +// Guest Heartbeat Enabling State - Enter +// ====================================== +extern GuestErrorT guest_heartbeat_enabling_state_enter( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabling State - Exit +// ===================================== +extern GuestErrorT guest_heartbeat_enabling_state_exit( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabling State - Transition +// =========================================== +extern GuestErrorT guest_heartbeat_enabling_state_transition( + GuestHeartbeatFsmStateT from_state ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabling State - Event Handler +// ============================================== +extern GuestErrorT guest_heartbeat_enabling_state_event_handler( + GuestHeartbeatFsmEventT event, void* event_data[] ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabling State - Initialize +// =========================================== +extern GuestErrorT guest_heartbeat_enabling_state_initialize( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Enabling State - Finalize +// ========================================= +extern GuestErrorT guest_heartbeat_enabling_state_finalize( void ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_HEARTBEAT_ENABLING_STATE_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_event_script.c b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_event_script.c new file mode 100755 index 00000000..c66d44c6 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_event_script.c @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_heartbeat_event_script.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "guest_types.h" +#include "guest_debug.h" +#include "guest_script.h" + +#include "guest_heartbeat_types.h" + +static GuestScriptIdT _script_id = GUEST_SCRIPT_ID_INVALID; +static GuestHeartbeatEventT _event; +static GuestHeartbeatNotifyT _notify; +static GuestHeartbeatEventScriptCallbackT _callback = NULL; + +// **************************************************************************** +// Guest Heartbeat Event Script - Event Argument +// ============================================= +const char* guest_heartbeat_event_script_event_arg( GuestHeartbeatEventT event ) +{ + switch (event) + { + case GUEST_HEARTBEAT_EVENT_STOP: return "stop"; + case GUEST_HEARTBEAT_EVENT_REBOOT: return "reboot"; + case GUEST_HEARTBEAT_EVENT_SUSPEND: return "suspend"; + case GUEST_HEARTBEAT_EVENT_PAUSE: return "pause"; + case GUEST_HEARTBEAT_EVENT_UNPAUSE: return "unpause"; + case GUEST_HEARTBEAT_EVENT_RESUME: return "resume"; + case GUEST_HEARTBEAT_EVENT_RESIZE_BEGIN: return "resize_begin"; + case GUEST_HEARTBEAT_EVENT_RESIZE_END: return "resize_end"; + case GUEST_HEARTBEAT_EVENT_LIVE_MIGRATE_BEGIN: return "live_migrate_begin"; + case GUEST_HEARTBEAT_EVENT_LIVE_MIGRATE_END: return "live_migrate_end"; + case GUEST_HEARTBEAT_EVENT_COLD_MIGRATE_BEGIN: return "cold_migrate_begin"; + case GUEST_HEARTBEAT_EVENT_COLD_MIGRATE_END: return "cold_migrate_end"; + default: + return NULL; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Event Script - Notify Argument +// ============================================== +const char* guest_heartbeat_event_script_notify_arg( GuestHeartbeatNotifyT notify ) +{ + switch (notify) + { + case GUEST_HEARTBEAT_NOTIFY_REVOCABLE: return "revocable"; + case GUEST_HEARTBEAT_NOTIFY_IRREVOCABLE: return "irrevocable"; + default: + return NULL; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Event Script - Abort +// ==================================== +void guest_heartbeat_event_script_abort( void ) +{ + if (GUEST_SCRIPT_ID_INVALID != _script_id) + { + DPRINTFI("Aborting event script for event %s, notification=%s, " + "script_id=%i.", guest_heartbeat_event_str(_event), + guest_heartbeat_notify_str(_notify), _script_id); + guest_script_abort(_script_id); + _script_id = GUEST_SCRIPT_ID_INVALID; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Event Script - Callback +// ======================================= +static void guest_heartbeat_event_script_callback( + GuestScriptIdT script_id, int exit_code, char* log_msg ) +{ + GuestHeartbeatVoteResultT vote_result; + + if (script_id == _script_id) + { + switch (exit_code) + { + case 0: + vote_result = GUEST_HEARTBEAT_VOTE_RESULT_ACCEPT; + break; + case 1: + vote_result = GUEST_HEARTBEAT_VOTE_RESULT_REJECT; + break; + default: + vote_result = GUEST_HEARTBEAT_VOTE_RESULT_ERROR; + break; + } + + if (NULL != _callback) + _callback(_event, _notify, vote_result, log_msg); + + _script_id = GUEST_SCRIPT_ID_INVALID; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Event Script - Invoke +// ===================================== +GuestErrorT guest_heartbeat_event_script_invoke( + char script[], GuestHeartbeatEventT event, GuestHeartbeatNotifyT notify, + GuestHeartbeatEventScriptCallbackT callback) +{ + const char* event_arg = guest_heartbeat_event_script_event_arg(event); + const char* notify_arg = guest_heartbeat_event_script_notify_arg(notify); + const char* script_argv[] = {script, notify_arg, event_arg, NULL}; + GuestErrorT error; + + _event = event; + _notify = notify; + _callback = callback; + + if (NULL == event_arg) + { + DPRINTFE("Event argument invalid, event=%s.", + guest_heartbeat_event_str(event)); + return GUEST_FAILED; + } + + if (NULL == notify_arg) + { + DPRINTFE("Notify argument invalid, event=%s.", + guest_heartbeat_notify_str(notify)); + return GUEST_FAILED; + } + + error = guest_script_invoke(script, (char**) script_argv, + guest_heartbeat_event_script_callback, + &_script_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to invoke script %s, error=%s.", script, + guest_error_str(error)); + return error; + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Event Script - Initialize +// ========================================= +GuestErrorT guest_heartbeat_event_script_initialize( void ) +{ + _script_id = GUEST_SCRIPT_ID_INVALID; + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Event Script - Finalize +// ======================================= +GuestErrorT guest_heartbeat_event_script_finalize( void ) +{ + guest_heartbeat_event_script_abort(); + _script_id = GUEST_SCRIPT_ID_INVALID; + return GUEST_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_event_script.h b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_event_script.h new file mode 100755 index 00000000..6b16931f --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_event_script.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_HEARTBEAT_EVENT_SCRIPT_H__ +#define __GUEST_HEARTBEAT_EVENT_SCRIPT_H__ + +#include "guest_types.h" +#include "guest_heartbeat_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*GuestHeartbeatEventScriptCallbackT) + (GuestHeartbeatEventT event, GuestHeartbeatNotifyT notify, + GuestHeartbeatVoteResultT vote_result, char* log_msg); + +// **************************************************************************** +// Guest Heartbeat Event Script - Abort +// ==================================== +extern void guest_heartbeat_event_script_abort( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Event Script - Invoke +// ===================================== +extern GuestErrorT guest_heartbeat_event_script_invoke( + char script[], GuestHeartbeatEventT event, GuestHeartbeatNotifyT notify, + GuestHeartbeatEventScriptCallbackT callback ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Event Script - Initialize +// ========================================= +extern GuestErrorT guest_heartbeat_event_script_initialize( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Event Script - Finalize +// ======================================= +extern GuestErrorT guest_heartbeat_event_script_finalize( void ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_HEARTBEAT_EVENT_SCRIPT_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_fsm.c b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_fsm.c new file mode 100755 index 00000000..21719ce7 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_fsm.c @@ -0,0 +1,369 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_heartbeat_fsm.h" + +#include +#include +#include + +#include "guest_types.h" +#include "guest_debug.h" +#include "guest_heartbeat_initial_state.h" +#include "guest_heartbeat_enabling_state.h" +#include "guest_heartbeat_enabled_state.h" +#include "guest_heartbeat_disabled_state.h" + +typedef GuestErrorT (*GuestHeartbeatFsmStateEnterT) (void); +typedef GuestErrorT (*GuestHeartbeatFsmStateExitT) (void); +typedef GuestErrorT (*GuestHeartbeatFsmStateTransitionT) + (GuestHeartbeatFsmStateT from_state); +typedef GuestErrorT (*GuestHeartbeatFsmStateEventHandlerT) + (GuestHeartbeatFsmEventT event, void* event_data[]); +typedef GuestErrorT (*GuestHeartbeatFsmStateInitializeT) (void); +typedef GuestErrorT (*GuestHeartbeatFsmStateFinalizeT) (void); + +typedef struct { + char name[40]; + GuestHeartbeatFsmStateEnterT enter; + GuestHeartbeatFsmStateExitT exit; + GuestHeartbeatFsmStateTransitionT transition; + GuestHeartbeatFsmStateEventHandlerT event_handler; + GuestHeartbeatFsmStateInitializeT initialize; + GuestHeartbeatFsmStateFinalizeT finalize; +} GuestHeartbeatFsmStateEntryT; + +static GuestHeartbeatFsmStateEntryT _states[GUEST_HEARTBEAT_FSM_MAX_STATES]; +static GuestHeartbeatFsmStateT _current_state = GUEST_HEARTBEAT_FSM_INITIAL_STATE; + +// **************************************************************************** +// Guest Heartbeat FSM - State String +// ================================== +const char* guest_heartbeat_fsm_state_str( GuestHeartbeatFsmStateT state ) +{ + if ((0 > state) || (GUEST_HEARTBEAT_FSM_MAX_STATES <= state)) + { + return "state-???"; + } + + return _states[state].name; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat FSM - Event String +// ================================== +const char* guest_heartbeat_fsm_event_str( GuestHeartbeatFsmEventT event ) +{ + switch (event) + { + case GUEST_HEARTBEAT_FSM_RELEASE: return "release"; + case GUEST_HEARTBEAT_FSM_INIT_ACK: return "init-ack"; + case GUEST_HEARTBEAT_FSM_CHALLENGE: return "challenge"; + case GUEST_HEARTBEAT_FSM_CHALLENGE_TIMEOUT: return "challenge-timeout"; + case GUEST_HEARTBEAT_FSM_ACTION: return "action"; + case GUEST_HEARTBEAT_FSM_CHANNEL_UP: return "channel-up"; + case GUEST_HEARTBEAT_FSM_CHANNEL_DOWN: return "channel-down"; + case GUEST_HEARTBEAT_FSM_SHUTDOWN: return "shutdown"; + default: + return "event-???"; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat FSM - Get State +// =============================== +GuestHeartbeatFsmStateT guest_heartbeat_fsm_get_state( void ) +{ + return _current_state; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat FSM - Enter State +// ================================= +static GuestErrorT guest_heartbeat_fsm_enter_state( GuestHeartbeatFsmStateT state ) +{ + GuestHeartbeatFsmStateEntryT* entry = &(_states[state]); + GuestErrorT error; + + if (NULL != entry->enter) + { + error = entry->enter(); + if (GUEST_OKAY != error) + { + DPRINTFE("Unable to enter state %s, error=%s.", entry->name, + guest_error_str(error)); + return error; + } + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat FSM - Exit State +// ================================ +static GuestErrorT guest_heartbeat_fsm_exit_state( GuestHeartbeatFsmStateT state ) +{ + GuestHeartbeatFsmStateEntryT* entry = &(_states[state]); + GuestErrorT error; + + if (NULL != entry->exit) + { + error = entry->exit(); + if (GUEST_OKAY != error) + { + DPRINTFE("Unable to exit state %s, error=%s.", entry->name, + guest_error_str(error)); + return error; + } + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat FSM - Transition State +// ====================================== +static GuestErrorT guest_heartbeat_fsm_transition_state( + GuestHeartbeatFsmStateT from_state ) +{ + GuestHeartbeatFsmStateEntryT* entry = &(_states[from_state]); + GuestErrorT error; + + if (NULL != entry->transition) + { + error = entry->transition(from_state); + if (GUEST_OKAY != error) + { + DPRINTFE("Unable to transition from state %s, error=%s.", + entry->name, guest_error_str(error)); + return error; + } + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat FSM - Set State +// =============================== +GuestErrorT guest_heartbeat_fsm_set_state( GuestHeartbeatFsmStateT state ) +{ + GuestHeartbeatFsmStateT prev_state = _current_state; + GuestHeartbeatFsmStateEntryT* prev_entry; + GuestHeartbeatFsmStateEntryT* entry; + GuestErrorT error, error2; + + if ((0 > state) || (GUEST_HEARTBEAT_FSM_MAX_STATES <= state)) + { + DPRINTFE("Invalid state %i given.", state); + return GUEST_FAILED; + } + + prev_entry = &(_states[prev_state]); + entry = &(_states[state]); + + error = guest_heartbeat_fsm_exit_state(prev_state); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to exit state %s, error=%s.", prev_entry->name, + guest_error_str(error)); + return( error ); + } + + _current_state = state; + + error = guest_heartbeat_fsm_transition_state(prev_state); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to transition to state %s, error=%s.", + prev_entry->name, guest_error_str(error)); + goto STATE_CHANGE_TRANSITION_ERROR; + } + + error = guest_heartbeat_fsm_enter_state(state); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to enter state %s, error=%s.", entry->name, + guest_error_str(error)); + goto STATE_CHANGE_ENTER_ERROR; + } + + return( GUEST_OKAY ); + +STATE_CHANGE_ENTER_ERROR: + error2 = guest_heartbeat_fsm_transition_state(state); + if (GUEST_OKAY != error2) + { + DPRINTFE("Failed to transition from state %s, error=%s.", + entry->name, guest_error_str(error2)); + abort(); + } + +STATE_CHANGE_TRANSITION_ERROR: + _current_state = prev_state; + + error2 = guest_heartbeat_fsm_enter_state(prev_state); + if (GUEST_OKAY != error2) + { + DPRINTFE("Failed to enter state (%s), error=%s.", prev_entry->name, + guest_error_str(error2)); + abort(); + } + + return error; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat FSM - Event Handler +// =================================== +GuestErrorT guest_heartbeat_fsm_event_handler( + GuestHeartbeatFsmEventT event, void* event_data[] ) +{ + GuestHeartbeatFsmStateT prev_state = _current_state; + GuestHeartbeatFsmStateEntryT* entry = &(_states[_current_state]); + GuestErrorT error; + + if (NULL != entry->event_handler) + { + error = entry->event_handler(event, event_data); + if (GUEST_OKAY != error) + { + DPRINTFE("Unable to handle event %s in state %s, error=%s.", + guest_heartbeat_fsm_event_str(event), entry->name, + guest_error_str(error)); + return error; + } + + if (prev_state != _current_state) + { + DPRINTFI("Guest-Client heartbeat state change from %s to %s, " + "event=%s.", guest_heartbeat_fsm_state_str(prev_state), + guest_heartbeat_fsm_state_str(_current_state), + guest_heartbeat_fsm_event_str(event)); + } else { + DPRINTFV("Guest-Client heartbeat no state change from %s, " + "event=%s.", guest_heartbeat_fsm_state_str(prev_state), + guest_heartbeat_fsm_event_str(event)); + } + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat FSM - Initialize +// ================================ +GuestErrorT guest_heartbeat_fsm_initialize( void ) +{ + GuestHeartbeatFsmStateEntryT* entry; + + memset(_states, 0, sizeof(_states)); + _current_state = GUEST_HEARTBEAT_FSM_INITIAL_STATE; + + // Initial State + entry = &(_states[GUEST_HEARTBEAT_FSM_INITIAL_STATE]); + snprintf(entry->name, sizeof(entry->name), "initial"); + entry->enter = guest_heartbeat_initial_state_enter; + entry->exit = guest_heartbeat_initial_state_exit; + entry->transition = guest_heartbeat_initial_state_transition; + entry->event_handler = guest_heartbeat_initial_state_event_handler; + entry->initialize = guest_heartbeat_initial_state_initialize; + entry->finalize = guest_heartbeat_initial_state_finalize; + + // Enabling State + entry = &(_states[GUEST_HEARTBEAT_FSM_ENABLING_STATE]); + snprintf(entry->name, sizeof(entry->name), "enabling"); + entry->enter = guest_heartbeat_enabling_state_enter; + entry->exit = guest_heartbeat_enabling_state_exit; + entry->transition = guest_heartbeat_enabling_state_transition; + entry->event_handler = guest_heartbeat_enabling_state_event_handler; + entry->initialize = guest_heartbeat_enabling_state_initialize; + entry->finalize = guest_heartbeat_enabling_state_finalize; + + // Enabled State + entry = &(_states[GUEST_HEARTBEAT_FSM_ENABLED_STATE]); + snprintf(entry->name, sizeof(entry->name), "enabled"); + entry->enter = guest_heartbeat_enabled_state_enter; + entry->exit = guest_heartbeat_enabled_state_exit; + entry->transition = guest_heartbeat_enabled_state_transition; + entry->event_handler = guest_heartbeat_enabled_state_event_handler; + entry->initialize = guest_heartbeat_enabled_state_initialize; + entry->finalize = guest_heartbeat_enabled_state_finalize; + + // Disabled State + entry = &(_states[GUEST_HEARTBEAT_FSM_DISABLED_STATE]); + snprintf(entry->name, sizeof(entry->name), "disabled"); + entry->enter = guest_heartbeat_disabled_state_enter; + entry->exit = guest_heartbeat_disabled_state_exit; + entry->transition = guest_heartbeat_disabled_state_transition; + entry->event_handler = guest_heartbeat_disabled_state_event_handler; + entry->initialize = guest_heartbeat_disabled_state_initialize; + entry->finalize = guest_heartbeat_disabled_state_finalize; + + unsigned int state_i; + for (state_i=0; GUEST_HEARTBEAT_FSM_MAX_STATES > state_i; ++state_i) + { + entry = &(_states[state_i]); + if (NULL != entry->initialize) + entry->initialize(); + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat FSM - Finalize +// ============================== +GuestErrorT guest_heartbeat_fsm_finalize( void ) +{ + GuestHeartbeatFsmStateEntryT* entry; + + unsigned int state_i; + for (state_i=0; GUEST_HEARTBEAT_FSM_MAX_STATES > state_i; ++state_i) + { + entry = &(_states[state_i]); + if (NULL != entry->finalize) + entry->finalize(); + } + + memset(_states, 0, sizeof(_states)); + _current_state = GUEST_HEARTBEAT_FSM_INITIAL_STATE; + + return GUEST_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_fsm.h b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_fsm.h new file mode 100755 index 00000000..6ff54456 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_fsm.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_HEARTBEAT_FSM_H__ +#define __GUEST_HEARTBEAT_FSM_H__ + +#include "guest_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + GUEST_HEARTBEAT_FSM_INITIAL_STATE, + GUEST_HEARTBEAT_FSM_ENABLING_STATE, + GUEST_HEARTBEAT_FSM_ENABLED_STATE, + GUEST_HEARTBEAT_FSM_DISABLED_STATE, + GUEST_HEARTBEAT_FSM_MAX_STATES +} GuestHeartbeatFsmStateT; + +typedef enum { + GUEST_HEARTBEAT_FSM_RELEASE, + GUEST_HEARTBEAT_FSM_INIT_ACK, + GUEST_HEARTBEAT_FSM_CHALLENGE, + GUEST_HEARTBEAT_FSM_CHALLENGE_TIMEOUT, + GUEST_HEARTBEAT_FSM_ACTION, + GUEST_HEARTBEAT_FSM_CHANNEL_UP, + GUEST_HEARTBEAT_FSM_CHANNEL_DOWN, + GUEST_HEARTBEAT_FSM_SHUTDOWN, + GUEST_HEARTBEAT_FSM_MAX_EVENTS +} GuestHeartbeatFsmEventT; + +// **************************************************************************** +// Guest Heartbeat FSM - State String +// ================================== +extern const char* guest_heartbeat_fsm_state_str( GuestHeartbeatFsmStateT state ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat FSM - Event String +// ================================== +extern const char* guest_heartbeat_fsm_event_str( GuestHeartbeatFsmEventT event ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat FSM - Get State +// =============================== +extern GuestHeartbeatFsmStateT guest_heartbeat_fsm_get_state( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat FSM - Set State +// =============================== +extern GuestErrorT guest_heartbeat_fsm_set_state( GuestHeartbeatFsmStateT state ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat FSM - Event Handler +// =================================== +extern GuestErrorT guest_heartbeat_fsm_event_handler( + GuestHeartbeatFsmEventT event, void* event_data[] ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat FSM - Initialize +// ================================ +extern GuestErrorT guest_heartbeat_fsm_initialize( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat FSM - Finalize +// ============================== +extern GuestErrorT guest_heartbeat_fsm_finalize( void ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_HEARTBEAT_FSM_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_health_script.c b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_health_script.c new file mode 100755 index 00000000..73c4a38c --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_health_script.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_heartbeat_health_script.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "guest_types.h" +#include "guest_debug.h" +#include "guest_script.h" + +static GuestScriptIdT _script_id = GUEST_SCRIPT_ID_INVALID; +static GuestHeartbeatHealthScriptCallbackT _callback = NULL; + +// **************************************************************************** +// Guest Heartbeat Health Script - Abort +// ===================================== +void guest_heartbeat_health_script_abort( void ) +{ + if (GUEST_SCRIPT_ID_INVALID != _script_id) + { + DPRINTFI("Aborting health script, script_id=%i.", _script_id); + guest_script_abort(_script_id); + _script_id = GUEST_SCRIPT_ID_INVALID; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Health Script - Callback +// ======================================== +static void guest_heartbeat_health_script_callback( + GuestScriptIdT script_id, int exit_code, char* log_msg ) +{ + if (script_id == _script_id) + { + if (NULL != _callback) + _callback((1 != exit_code), log_msg); + + _script_id = GUEST_SCRIPT_ID_INVALID; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Health Script - Invoke +// ====================================== +GuestErrorT guest_heartbeat_health_script_invoke( + char script[], GuestHeartbeatHealthScriptCallbackT callback) +{ + const char* script_argv[] = {script, NULL}; + GuestErrorT error; + + _callback = callback; + + error = guest_script_invoke(script, (char**) script_argv, + guest_heartbeat_health_script_callback, + &_script_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to invoke script %s, error=%s.", script, + guest_error_str(error)); + return error; + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Health Script - Initialize +// ========================================== +GuestErrorT guest_heartbeat_health_script_initialize( void ) +{ + _script_id = GUEST_SCRIPT_ID_INVALID; + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Health Script - Finalize +// ======================================== +GuestErrorT guest_heartbeat_health_script_finalize( void ) +{ + guest_heartbeat_health_script_abort(); + _script_id = GUEST_SCRIPT_ID_INVALID; + return GUEST_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_health_script.h b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_health_script.h new file mode 100755 index 00000000..6e52119e --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_health_script.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_HEARTBEAT_HEALTH_SCRIPT_H__ +#define __GUEST_HEARTBEAT_HEALTH_SCRIPT_H__ + +#include + +#include "guest_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*GuestHeartbeatHealthScriptCallbackT) (bool health, char* log_msg); + +// **************************************************************************** +// Guest Heartbeat Health Script - Abort +// ===================================== +extern void guest_heartbeat_health_script_abort( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Health Script - Invoke +// ====================================== +extern GuestErrorT guest_heartbeat_health_script_invoke( + char script[], GuestHeartbeatHealthScriptCallbackT callback ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Health Script - Initialize +// ========================================== +extern GuestErrorT guest_heartbeat_health_script_initialize( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Health Script - Finalize +// ======================================== +extern GuestErrorT guest_heartbeat_health_script_finalize( void ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_HEARTBEAT_HEALTH_SCRIPT_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_initial_state.c b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_initial_state.c new file mode 100755 index 00000000..9df06f6d --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_initial_state.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_heartbeat_initial_state.h" + +#include "guest_types.h" +#include "guest_debug.h" +#include "guest_heartbeat_fsm.h" + +// **************************************************************************** +// Guest Heartbeat Initial State - Enter +// ===================================== +GuestErrorT guest_heartbeat_initial_state_enter( void ) +{ + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Initial State - Exit +// ==================================== +GuestErrorT guest_heartbeat_initial_state_exit( void ) +{ + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Initial State - Transition +// ========================================== +GuestErrorT guest_heartbeat_initial_state_transition( + GuestHeartbeatFsmStateT from_state ) +{ + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Initial State - Event Handler +// ============================================= +GuestErrorT guest_heartbeat_initial_state_event_handler( + GuestHeartbeatFsmEventT event, void* event_data[] ) +{ + switch (event) + { + case GUEST_HEARTBEAT_FSM_INIT_ACK: + case GUEST_HEARTBEAT_FSM_CHALLENGE: + case GUEST_HEARTBEAT_FSM_CHALLENGE_TIMEOUT: + case GUEST_HEARTBEAT_FSM_ACTION: + case GUEST_HEARTBEAT_FSM_SHUTDOWN: + // Ignore + break; + + case GUEST_HEARTBEAT_FSM_RELEASE: + case GUEST_HEARTBEAT_FSM_CHANNEL_UP: + guest_heartbeat_fsm_set_state(GUEST_HEARTBEAT_FSM_ENABLING_STATE); + break; + + case GUEST_HEARTBEAT_FSM_CHANNEL_DOWN: + guest_heartbeat_fsm_set_state(GUEST_HEARTBEAT_FSM_DISABLED_STATE); + break; + + default: + DPRINTFV("Ignoring event %s.", + guest_heartbeat_fsm_event_str(event)); + } + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Initial State - Initialize +// ========================================== +GuestErrorT guest_heartbeat_initial_state_initialize( void ) +{ + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Initial State - Finalize +// ======================================== +GuestErrorT guest_heartbeat_initial_state_finalize( void ) +{ + return GUEST_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_initial_state.h b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_initial_state.h new file mode 100755 index 00000000..db05cf83 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_initial_state.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_HEARTBEAT_INITIAL_STATE_H__ +#define __GUEST_HEARTBEAT_INITIAL_STATE_H__ + +#include "guest_types.h" +#include "guest_heartbeat_fsm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// **************************************************************************** +// Guest Heartbeat Initial State - Enter +// ===================================== +extern GuestErrorT guest_heartbeat_initial_state_enter( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Initial State - Exit +// ==================================== +extern GuestErrorT guest_heartbeat_initial_state_exit( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Initial State - Transition +// ========================================== +extern GuestErrorT guest_heartbeat_initial_state_transition( + GuestHeartbeatFsmStateT from_state ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Initial State - Event Handler +// ============================================= +extern GuestErrorT guest_heartbeat_initial_state_event_handler( + GuestHeartbeatFsmEventT event, void* event_data[] ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Initial State - Initialize +// ========================================== +extern GuestErrorT guest_heartbeat_initial_state_initialize( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Initial State - Finalize +// ======================================== +extern GuestErrorT guest_heartbeat_initial_state_finalize( void ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_HEARTBEAT_INITIAL_STATE_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_mgmt_api.c b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_mgmt_api.c new file mode 100755 index 00000000..00ee9dc3 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_mgmt_api.c @@ -0,0 +1,1400 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_heartbeat_mgmt_api.h" + +#include +#include +#include +#include +#include +#include + +#include "guest_limits.h" +#include "guest_types.h" +#include "guest_debug.h" +#include "guest_selobj.h" +#include "guest_unix.h" +#include "guest_stream.h" +#include "guest_timer.h" + +#include "guest_heartbeat_types.h" +#include "guest_heartbeat_api_msg_defs.h" + +#define GUEST_HEARTBEAT_MGMT_API_CHALLENGE_DEPTH 4 + +typedef struct { + char name[GUEST_HEARTBEAT_API_MSG_MAX_APPLICATION_NAME_SIZE]; + int heartbeat_interval_ms; + int vote_ms; + int shutdown_notice_ms; + int suspend_notice_ms; + int resume_notice_ms; + GuestHeartbeatActionT corrective_action; +} GuestHeartbeatMgmtApiAppConfigT; + +typedef struct { + bool healthy; + GuestHeartbeatActionT corrective_action; + char log_msg[GUEST_HEARTBEAT_API_MSG_MAX_LOG_SIZE]; +} GuestHeartbeatMgmtApiAppHealthT; + +typedef struct { + bool running; + int invocation_id; + GuestHeartbeatEventT event; + GuestHeartbeatNotifyT notify; + GuestHeartbeatVoteResultT vote_result; + char log_msg[GUEST_HEARTBEAT_API_MSG_MAX_LOG_SIZE]; +} GuestHeartbeatMgmtApiAppActionT; + +typedef struct { + bool inuse; + bool registered; + bool final; + int sock; + GuestStreamT stream; + int challenge_depth; + int last_challenge[GUEST_HEARTBEAT_MGMT_API_CHALLENGE_DEPTH]; + bool send_challenge_response; + GuestTimerIdT heartbeat_timer; + GuestTimerIdT heartbeat_timeout_timer; + GuestTimerIdT action_timer; + GuestHeartbeatMgmtApiAppConfigT application_config; + GuestHeartbeatMgmtApiAppHealthT application_health; + GuestHeartbeatMgmtApiAppActionT application_action; +} GuestHeartbeatMgmtApiConnectionT; + +static int _sock = -1; +static uint32_t _msg_sequence; +static GuestHeartbeatMgmtApiActionResponseT _callback; +static GuestHeartbeatMgmtApiConnectionT _connections[GUEST_APPLICATIONS_MAX]; + +// **************************************************************************** +// Guest Heartbeat Management API - Handle Action Completed +// ======================================================== +static void guest_heartbeat_mgmt_api_handle_action_completed( void ) +{ + bool update; + bool invoke_callback; + GuestHeartbeatMgmtApiAppConfigT* app_config; + GuestHeartbeatMgmtApiAppActionT* app_action; + GuestHeartbeatMgmtApiConnectionT* connection; + GuestHeartbeatEventT event; + GuestHeartbeatNotifyT notify; + GuestHeartbeatVoteResultT vote_result; + char* log_msg; + + unsigned int connection_i; + for (connection_i=0; GUEST_APPLICATIONS_MAX > connection_i; ++connection_i) + { + connection = &(_connections[connection_i]); + if (connection->inuse && connection->registered) + { + if (connection->application_action.running) + { + DPRINTFD("Still waiting for application %s to respond.", + connection->application_config.name); + return; + } + } + } + + if (NULL == _callback) + return; + + vote_result = GUEST_HEARTBEAT_VOTE_RESULT_UNKNOWN; + invoke_callback = false; + + // All action responses received or timed out. + for (connection_i=0; GUEST_APPLICATIONS_MAX > connection_i; ++connection_i) + { + connection = &(_connections[connection_i]); + if (connection->inuse && connection->registered) + { + app_config = &(connection->application_config); + app_action = &(connection->application_action); + + DPRINTFI("Application %s vote %s for event %s, notification=%s.", + app_config->name, + guest_heartbeat_vote_result_str(app_action->vote_result), + guest_heartbeat_event_str(app_action->event), + guest_heartbeat_notify_str(app_action->notify)); + + update = false; + + switch (app_action->vote_result) + { + case GUEST_HEARTBEAT_VOTE_RESULT_REJECT: + update = true; + break; + + case GUEST_HEARTBEAT_VOTE_RESULT_ACCEPT: + case GUEST_HEARTBEAT_VOTE_RESULT_COMPLETE: + if (GUEST_HEARTBEAT_VOTE_RESULT_REJECT != vote_result) + update = true; + break; + + default: + update = false; + break; + } + + if (update) + { + event = app_action->event; + notify = app_action->notify; + vote_result = app_action->vote_result; + log_msg = &(app_action->log_msg[0]); + invoke_callback = true; + } + } + } + + if (invoke_callback) + _callback(event, notify, vote_result, log_msg); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Close Connection +// ================================================= +static void guest_heartbeat_mgmt_api_close_connection( + GuestHeartbeatMgmtApiConnectionT* connection ) +{ + GuestErrorT error; + + if (0 <= connection->sock) + { + error = guest_selobj_deregister(connection->sock); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to deregister select on unix socket, error=%s.", + guest_error_str(error)); + } + + close(connection->sock); + } + + if (GUEST_TIMER_ID_INVALID != connection->heartbeat_timer) + { + error = guest_timer_deregister(connection->heartbeat_timer); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to cancel heartbeat timer, error=%s.", + guest_error_str(error)); + } + } + + if (GUEST_TIMER_ID_INVALID != connection->heartbeat_timeout_timer) + { + error = guest_timer_deregister(connection->heartbeat_timeout_timer); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to cancel heartbeat timer, error=%s.", + guest_error_str(error)); + } + } + + if (GUEST_TIMER_ID_INVALID != connection->action_timer) + { + error = guest_timer_deregister(connection->action_timer); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to cancel action timer, error=%s.", + guest_error_str(error)); + } + } + + memset(connection, 0, sizeof(GuestHeartbeatMgmtApiConnectionT)); + connection->sock = -1; + connection->heartbeat_timer = GUEST_TIMER_ID_INVALID; + connection->heartbeat_timeout_timer = GUEST_TIMER_ID_INVALID; + connection->action_timer = GUEST_TIMER_ID_INVALID; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Action (Network to Host) +// ========================================================= +static GuestHeartbeatActionT guest_heartbeat_mgmt_api_action_ntoh( + GuestHeartbeatApiMsgActionT action ) +{ + switch (action) + { + case GUEST_HEARTBEAT_API_MSG_ACTION_NONE: + return GUEST_HEARTBEAT_ACTION_NONE; + case GUEST_HEARTBEAT_API_MSG_ACTION_REBOOT: + return GUEST_HEARTBEAT_ACTION_REBOOT; + case GUEST_HEARTBEAT_API_MSG_ACTION_STOP: + return GUEST_HEARTBEAT_ACTION_STOP; + case GUEST_HEARTBEAT_API_MSG_ACTION_LOG: + return GUEST_HEARTBEAT_ACTION_LOG; + default: + DPRINTFE("Unknown action %i.", action); + return GUEST_HEARTBEAT_ACTION_UNKNOWN; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Event (Host to Network) +// ======================================================== +static GuestHeartbeatApiMsgEventT guest_heartbeat_mgmt_api_event_hton( + GuestHeartbeatEventT event ) +{ + switch (event) + { + case GUEST_HEARTBEAT_EVENT_STOP: + return GUEST_HEARTBEAT_API_MSG_EVENT_STOP; + case GUEST_HEARTBEAT_EVENT_REBOOT: + return GUEST_HEARTBEAT_API_MSG_EVENT_REBOOT; + case GUEST_HEARTBEAT_EVENT_SUSPEND: + return GUEST_HEARTBEAT_API_MSG_EVENT_SUSPEND; + case GUEST_HEARTBEAT_EVENT_PAUSE: + return GUEST_HEARTBEAT_API_MSG_EVENT_PAUSE; + case GUEST_HEARTBEAT_EVENT_UNPAUSE: + return GUEST_HEARTBEAT_API_MSG_EVENT_UNPAUSE; + case GUEST_HEARTBEAT_EVENT_RESUME: + return GUEST_HEARTBEAT_API_MSG_EVENT_RESUME; + case GUEST_HEARTBEAT_EVENT_RESIZE_BEGIN: + return GUEST_HEARTBEAT_API_MSG_EVENT_RESIZE_BEGIN; + case GUEST_HEARTBEAT_EVENT_RESIZE_END: + return GUEST_HEARTBEAT_API_MSG_EVENT_RESIZE_END; + case GUEST_HEARTBEAT_EVENT_LIVE_MIGRATE_BEGIN: + return GUEST_HEARTBEAT_API_MSG_EVENT_LIVE_MIGRATE_BEGIN; + case GUEST_HEARTBEAT_EVENT_LIVE_MIGRATE_END: + return GUEST_HEARTBEAT_API_MSG_EVENT_LIVE_MIGRATE_END; + case GUEST_HEARTBEAT_EVENT_COLD_MIGRATE_BEGIN: + return GUEST_HEARTBEAT_API_MSG_EVENT_COLD_MIGRATE_BEGIN; + case GUEST_HEARTBEAT_EVENT_COLD_MIGRATE_END: + return GUEST_HEARTBEAT_API_MSG_EVENT_COLD_MIGRATE_END; + default: + DPRINTFE("Unknown event %i.", event); + return GUEST_HEARTBEAT_API_MSG_EVENT_UNKNOWN; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Event (Network to Host) +// ======================================================== +static GuestHeartbeatEventT guest_heartbeat_mgmt_api_event_ntoh( + GuestHeartbeatApiMsgEventT event ) +{ + switch (event) + { + case GUEST_HEARTBEAT_API_MSG_EVENT_STOP: + return GUEST_HEARTBEAT_EVENT_STOP; + case GUEST_HEARTBEAT_API_MSG_EVENT_REBOOT: + return GUEST_HEARTBEAT_EVENT_REBOOT; + case GUEST_HEARTBEAT_API_MSG_EVENT_SUSPEND: + return GUEST_HEARTBEAT_EVENT_SUSPEND; + case GUEST_HEARTBEAT_API_MSG_EVENT_PAUSE: + return GUEST_HEARTBEAT_EVENT_PAUSE; + case GUEST_HEARTBEAT_API_MSG_EVENT_UNPAUSE: + return GUEST_HEARTBEAT_EVENT_UNPAUSE; + case GUEST_HEARTBEAT_API_MSG_EVENT_RESUME: + return GUEST_HEARTBEAT_EVENT_RESUME; + case GUEST_HEARTBEAT_API_MSG_EVENT_RESIZE_BEGIN: + return GUEST_HEARTBEAT_EVENT_RESIZE_BEGIN; + case GUEST_HEARTBEAT_API_MSG_EVENT_RESIZE_END: + return GUEST_HEARTBEAT_EVENT_RESIZE_END; + case GUEST_HEARTBEAT_API_MSG_EVENT_LIVE_MIGRATE_BEGIN: + return GUEST_HEARTBEAT_EVENT_LIVE_MIGRATE_BEGIN; + case GUEST_HEARTBEAT_API_MSG_EVENT_LIVE_MIGRATE_END: + return GUEST_HEARTBEAT_EVENT_LIVE_MIGRATE_END; + case GUEST_HEARTBEAT_API_MSG_EVENT_COLD_MIGRATE_BEGIN: + return GUEST_HEARTBEAT_EVENT_COLD_MIGRATE_BEGIN; + case GUEST_HEARTBEAT_API_MSG_EVENT_COLD_MIGRATE_END: + return GUEST_HEARTBEAT_EVENT_COLD_MIGRATE_END; + default: + DPRINTFE("Unknown event %i.", event); + return GUEST_HEARTBEAT_EVENT_UNKNOWN; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Notify (Host to Network) +// ========================================================= +static GuestHeartbeatApiMsgNotifyT guest_heartbeat_mgmt_api_notify_hton( + GuestHeartbeatNotifyT notify ) +{ + switch (notify) + { + case GUEST_HEARTBEAT_NOTIFY_REVOCABLE: + return GUEST_HEARTBEAT_API_MSG_NOTIFY_REVOCABLE; + case GUEST_HEARTBEAT_NOTIFY_IRREVOCABLE: + return GUEST_HEARTBEAT_API_MSG_NOTIFY_IRREVOCABLE; + default: + DPRINTFE("Unknown notify type %i.", notify); + return GUEST_HEARTBEAT_API_MSG_NOTIFY_UNKNOWN; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Notify (Network to Host) +// ========================================================= +static GuestHeartbeatNotifyT guest_heartbeat_mgmt_api_notify_ntoh( + GuestHeartbeatApiMsgNotifyT notify ) +{ + switch (notify) + { + case GUEST_HEARTBEAT_API_MSG_NOTIFY_REVOCABLE: + return GUEST_HEARTBEAT_NOTIFY_REVOCABLE; + case GUEST_HEARTBEAT_API_MSG_NOTIFY_IRREVOCABLE: + return GUEST_HEARTBEAT_NOTIFY_IRREVOCABLE; + default: + DPRINTFE("Unknown notify type %i.", notify); + return GUEST_HEARTBEAT_NOTIFY_UNKNOWN; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Vote Result (Network to Host) +// ============================================================== +static GuestHeartbeatVoteResultT guest_heartbeat_mgmt_api_vote_result_ntoh( + GuestHeartbeatApiMsgVoteResultT vote_result ) +{ + switch (vote_result) + { + case GUEST_HEARTBEAT_API_MSG_VOTE_RESULT_ACCEPT: + return GUEST_HEARTBEAT_VOTE_RESULT_ACCEPT; + case GUEST_HEARTBEAT_API_MSG_VOTE_RESULT_REJECT: + return GUEST_HEARTBEAT_VOTE_RESULT_REJECT; + case GUEST_HEARTBEAT_API_MSG_VOTE_RESULT_COMPLETE: + return GUEST_HEARTBEAT_VOTE_RESULT_COMPLETE; + default: + DPRINTFE("Unknown vote result %i.", vote_result); + return GUEST_HEARTBEAT_VOTE_RESULT_UNKNOWN; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Send Init Ack +// ============================================== +static GuestErrorT guest_heartbeat_mgmt_api_send_init_ack( int s, bool accept ) +{ + GuestHeartbeatApiMsgT msg; + GuestHeartbeatApiMsgHeaderT *hdr = &(msg.header); + GuestHeartbeatApiMsgInitAckT *bdy = &(msg.body.init_ack); + GuestErrorT error; + + memset(&msg, 0, sizeof(msg)); + + memcpy(&(hdr->magic), GUEST_HEARTBEAT_API_MSG_MAGIC_VALUE, + GUEST_HEARTBEAT_API_MSG_MAGIC_SIZE); + hdr->version = GUEST_HEARTBEAT_API_MSG_VERSION_CURRENT; + hdr->revision = GUEST_HEARTBEAT_API_MSG_REVISION_CURRENT; + hdr->msg_type = GUEST_HEARTBEAT_API_MSG_INIT_ACK; + hdr->sequence = ++_msg_sequence; + hdr->size = sizeof(msg); + + bdy->accepted = accept; + + error = guest_unix_send(s, &msg, sizeof(msg)); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to send guest heartbeat register ack message, " + "error=%s.", guest_error_str(error)); + return error; + } + + DPRINTFD("Sent register ack."); + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Send Heartbeat +// =============================================== +static GuestErrorT guest_heartbeat_mgmt_api_send_heartbeat( int s, int challenge ) +{ + GuestHeartbeatApiMsgT msg; + GuestHeartbeatApiMsgHeaderT *hdr = &(msg.header); + GuestHeartbeatApiMsgChallengeT *bdy = &(msg.body.challenge); + GuestErrorT error; + + memset(&msg, 0, sizeof(msg)); + + memcpy(&(hdr->magic), GUEST_HEARTBEAT_API_MSG_MAGIC_VALUE, + GUEST_HEARTBEAT_API_MSG_MAGIC_SIZE); + hdr->version = GUEST_HEARTBEAT_API_MSG_VERSION_CURRENT; + hdr->revision = GUEST_HEARTBEAT_API_MSG_REVISION_CURRENT; + hdr->msg_type = GUEST_HEARTBEAT_API_MSG_CHALLENGE; + hdr->sequence = ++_msg_sequence; + hdr->size = sizeof(msg); + + bdy->heartbeat_challenge = challenge; + + error = guest_unix_send(s, &msg, sizeof(msg)); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to send guest heartbeat message, error=%s.", + guest_error_str(error)); + return error; + } + + DPRINTFD("Sent heartbeat message."); + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Send Action Notify +// =================================================== +static GuestErrorT guest_heartbeat_mgmt_api_send_action_notify( + int s, int invocation_id, GuestHeartbeatEventT event, + GuestHeartbeatNotifyT notify ) +{ + GuestHeartbeatApiMsgT msg; + GuestHeartbeatApiMsgHeaderT *hdr = &(msg.header); + GuestHeartbeatApiMsgActionNotifyT *bdy = &(msg.body.action_notify); + GuestErrorT error; + + memset(&msg, 0, sizeof(msg)); + + memcpy(&(hdr->magic), GUEST_HEARTBEAT_API_MSG_MAGIC_VALUE, + GUEST_HEARTBEAT_API_MSG_MAGIC_SIZE); + hdr->version = GUEST_HEARTBEAT_API_MSG_VERSION_CURRENT; + hdr->revision = GUEST_HEARTBEAT_API_MSG_REVISION_CURRENT; + hdr->msg_type = GUEST_HEARTBEAT_API_MSG_ACTION_NOTIFY; + hdr->sequence = ++_msg_sequence; + hdr->size = sizeof(msg); + + bdy->invocation_id = invocation_id; + bdy->event_type = guest_heartbeat_mgmt_api_event_hton(event); + bdy->notification_type = guest_heartbeat_mgmt_api_notify_hton(notify); + + error = guest_unix_send(s, &msg, sizeof(msg)); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to send guest heartbeat action notify message, " + "error=%s.", guest_error_str(error)); + return error; + } + + DPRINTFD("Sent action notify."); + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Heartbeat Interval +// =================================================== +static bool guest_heartbeat_mgmt_api_heartbeat_interval( GuestTimerIdT timer_id ) +{ + int challenge; + GuestHeartbeatMgmtApiConnectionT* connection; + GuestErrorT error; + + unsigned int connection_i; + for (connection_i=0; GUEST_APPLICATIONS_MAX > connection_i; ++connection_i) + { + connection = &(_connections[connection_i]); + if (connection->inuse) + if (timer_id == connection->heartbeat_timer) + break; + } + + if (GUEST_APPLICATIONS_MAX <= connection_i) + { + DPRINTFE("Uknown timer %i.", timer_id); + return false; // don't rearm + } + + if (!(connection->send_challenge_response)) + { + DPRINTFD("Waiting for challenge response for previous iteration."); + return true; // rearm + } + + challenge = rand(); + ++connection->challenge_depth; + if (GUEST_HEARTBEAT_MGMT_API_CHALLENGE_DEPTH <= connection->challenge_depth) + connection->challenge_depth = 0; + + connection->last_challenge[connection->challenge_depth] = challenge; + + error = guest_heartbeat_mgmt_api_send_heartbeat( connection->sock, + challenge ); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to send heartbeat, error=%s.", guest_error_str(error)); + return true; // rearm + } + + connection->send_challenge_response = false; + return true; // rearm +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Heartbeat Timeout +// ================================================== +static bool guest_heartbeat_mgmt_api_heartbeat_timeout( GuestTimerIdT timer_id ) +{ + bool prev_health; + GuestHeartbeatMgmtApiAppConfigT* app_config; + GuestHeartbeatMgmtApiAppHealthT* app_health; + GuestHeartbeatMgmtApiConnectionT* connection; + int max_heartbeat_delay; + + unsigned int connection_i; + for (connection_i=0; GUEST_APPLICATIONS_MAX > connection_i; ++connection_i) + { + connection = &(_connections[connection_i]); + if (connection->inuse) + if (timer_id == connection->heartbeat_timeout_timer) + break; + } + + if (GUEST_APPLICATIONS_MAX <= connection_i) + { + DPRINTFE("Uknown timer %i.", timer_id); + return false; // don't rearm + } + + app_config = &(connection->application_config); + app_health = &(connection->application_health); + + max_heartbeat_delay = app_config->heartbeat_interval_ms*2; + + if (!guest_timer_scheduling_on_time_within(max_heartbeat_delay)) + { + DPRINTFE("Failed to receive a heartbeat in %i ms, but we are not " + "scheduling on time.", max_heartbeat_delay); + return true; // rearm + } + + prev_health = app_health->healthy; + app_health->healthy = false; + app_health->corrective_action = app_config->corrective_action; + snprintf(app_health->log_msg, GUEST_HEARTBEAT_API_MSG_MAX_LOG_SIZE, + "Application %s heartbeat timeout %i ms.", app_config->name, + app_config->heartbeat_interval_ms*2); + + if (prev_health) + { + DPRINTFI("Application %s heartbeat timeout %i ms, " + "corrective_action=%s, log_msg=%s.", app_config->name, + max_heartbeat_delay, + guest_heartbeat_action_str(app_health->corrective_action), + app_health->log_msg); + } + return true; // rearm +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Receive Init +// ============================================= +static void guest_heartbeat_mgmt_api_recv_init( + GuestHeartbeatMgmtApiConnectionT* connection ) +{ + bool accepted = true; + GuestHeartbeatMgmtApiAppConfigT* app_config; + GuestHeartbeatMgmtApiAppHealthT* app_health; + GuestHeartbeatMgmtApiAppActionT* app_action; + char* ptr = connection->stream.bytes + sizeof(GuestHeartbeatApiMsgHeaderT); + GuestErrorT error; + + DPRINTFD("Heartbeat Init received..."); + + app_config = &(connection->application_config); + app_health = &(connection->application_health); + app_action = &(connection->application_action); + + snprintf(app_config->name, + GUEST_HEARTBEAT_API_MSG_MAX_APPLICATION_NAME_SIZE, "%s", ptr); + ptr += GUEST_HEARTBEAT_API_MSG_MAX_APPLICATION_NAME_SIZE; + app_config->heartbeat_interval_ms = *(uint32_t*) ptr; + ptr += sizeof(uint32_t); + app_config->vote_ms = (*(uint32_t*) ptr) * 1000; + ptr += sizeof(uint32_t); + app_config->shutdown_notice_ms = (*(uint32_t*) ptr) * 1000; + ptr += sizeof(uint32_t); + app_config->suspend_notice_ms = (*(uint32_t*) ptr) * 1000; + ptr += sizeof(uint32_t); + app_config->resume_notice_ms = (*(uint32_t*) ptr) * 1000; + ptr += sizeof(uint32_t); + app_config->corrective_action + = guest_heartbeat_mgmt_api_action_ntoh(*(uint32_t*) ptr); + ptr += sizeof(uint32_t); + + if (GUEST_HEARTBEAT_MIN_INTERVAL_MS > app_config->heartbeat_interval_ms) + { + DPRINTFE("Not accepting application %s registration, unsupported " + "heartbeat interval, less than %s ms.", app_config->name, + GUEST_HEARTBEAT_MIN_INTERVAL_MS); + accepted = false; + } + + error = guest_heartbeat_mgmt_api_send_init_ack(connection->sock, accepted); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to send register ack message, error=%s.", + guest_error_str(error)); + return; + } + + if (!accepted) + return; + + error = guest_timer_register(app_config->heartbeat_interval_ms, + guest_heartbeat_mgmt_api_heartbeat_interval, + &(connection->heartbeat_timer)); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to register heartbeat timer, error=%s.", + guest_error_str(error)); + return; + } + + error = guest_timer_register(app_config->heartbeat_interval_ms*2, + guest_heartbeat_mgmt_api_heartbeat_timeout, + &(connection->heartbeat_timeout_timer)); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to register heartbeat timeout timer, error=%s.", + guest_error_str(error)); + return; + } + + app_health->healthy = true; + app_health->corrective_action = GUEST_HEARTBEAT_ACTION_NONE; + app_health->log_msg[0] = '\0'; + + app_action->event = GUEST_HEARTBEAT_EVENT_UNKNOWN; + app_action->notify = GUEST_HEARTBEAT_NOTIFY_UNKNOWN; + app_action->vote_result = GUEST_HEARTBEAT_VOTE_RESULT_UNKNOWN; + app_action->log_msg[0] = '\0'; + + connection->send_challenge_response = true; + connection->registered = true; + connection->final = false; + + DPRINTFI("Connection accepted from %s.", app_config->name); + DPRINTFI(" socket: %i", connection->sock); + DPRINTFI(" heartbeat_interval_ms: %i", app_config->heartbeat_interval_ms); + DPRINTFI(" vote_ms: %i", app_config->vote_ms); + DPRINTFI(" shutdown_notice_ms: %i", app_config->shutdown_notice_ms); + DPRINTFI(" resume_notice_ms: %i", app_config->resume_notice_ms); + DPRINTFI(" corrective_action: %s", + guest_heartbeat_action_str(app_config->corrective_action)); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Receive Final +// ============================================== +static void guest_heartbeat_mgmt_api_recv_final( + GuestHeartbeatMgmtApiConnectionT* connection ) +{ + char* log_msg; + char* ptr = connection->stream.bytes + sizeof(GuestHeartbeatApiMsgHeaderT); + + log_msg = ptr; + ptr += GUEST_HEARTBEAT_API_MSG_MAX_LOG_SIZE; + + DPRINTFD("Heartbeat Final Response received..."); + + DPRINTFI("Application %s has deregistered, msg=%s.", + connection->application_config.name, log_msg); + + connection->registered = false; + connection->final = true; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Receive Challenge Response +// =========================================================== +static void guest_heartbeat_mgmt_api_recv_challenge_response( + GuestHeartbeatMgmtApiConnectionT* connection ) +{ + uint32_t heartbeat_response; + uint32_t health; + GuestHeartbeatActionT corrective_action; + char* log_msg; + GuestHeartbeatMgmtApiAppConfigT* app_config; + GuestHeartbeatMgmtApiAppHealthT* app_health; + char* ptr = connection->stream.bytes + sizeof(GuestHeartbeatApiMsgHeaderT); + + heartbeat_response = *(uint32_t*) ptr; + ptr += sizeof(uint32_t); + health = *(uint32_t*) ptr; + ptr += sizeof(uint32_t); + corrective_action + = guest_heartbeat_mgmt_api_action_ntoh(*(uint32_t*) ptr); + ptr += sizeof(uint32_t); + log_msg = ptr; + ptr += GUEST_HEARTBEAT_API_MSG_MAX_LOG_SIZE; + + unsigned int challenge_i; + for (challenge_i=0; GUEST_HEARTBEAT_MGMT_API_CHALLENGE_DEPTH > challenge_i; + ++challenge_i) + { + if (connection->last_challenge[challenge_i] == heartbeat_response) + break; + } + + if (GUEST_HEARTBEAT_MGMT_API_CHALLENGE_DEPTH == challenge_i) + { + DPRINTFE("Mismatch between last transmitted challenges and received " + "challenge."); + return; + } + + DPRINTFD("Heartbeat Challenge Response received..."); + connection->send_challenge_response = true; + + app_config = &(connection->application_config); + app_health = &(connection->application_health); + + if (health != app_health->healthy) + { + DPRINTFI("Application %s health state change, prev_health=%i, " + "health=%i, corrective_action=%s, log_msg=%s.", + app_config->name, app_health->healthy, health, + guest_heartbeat_action_str(corrective_action), + log_msg); + } + + app_health->healthy = health; + app_health->corrective_action = corrective_action; + snprintf(app_health->log_msg, GUEST_HEARTBEAT_API_MSG_MAX_LOG_SIZE, "%s", + log_msg); + + guest_timer_reset(connection->heartbeat_timeout_timer); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Receive Action Response +// ======================================================== +static void guest_heartbeat_mgmt_api_recv_action_response( + GuestHeartbeatMgmtApiConnectionT* connection ) +{ + int invocation_id; + GuestHeartbeatEventT event; + GuestHeartbeatNotifyT notify; + GuestHeartbeatVoteResultT vote_result; + GuestHeartbeatMgmtApiAppActionT* app_action; + char* log_msg; + char* ptr = connection->stream.bytes + sizeof(GuestHeartbeatApiMsgHeaderT); + GuestErrorT error; + + app_action = &(connection->application_action); + + invocation_id = *(uint32_t*) ptr; + ptr += sizeof(uint32_t); + event = guest_heartbeat_mgmt_api_event_ntoh(*(uint32_t*) ptr); + ptr += sizeof(uint32_t); + notify = guest_heartbeat_mgmt_api_notify_ntoh(*(uint32_t*) ptr); + ptr += sizeof(uint32_t); + vote_result = guest_heartbeat_mgmt_api_vote_result_ntoh(*(uint32_t*) ptr); + ptr += sizeof(uint32_t); + log_msg = ptr; + ptr += GUEST_HEARTBEAT_API_MSG_MAX_LOG_SIZE; + + if (!(app_action->running)) + { + DPRINTFD("No action inprogress."); + return; + } + + if (invocation_id != app_action->invocation_id) + { + DPRINTFE("Unexpected action invocation %i received for %s.", + invocation_id, guest_heartbeat_event_str(event)); + return; + } + + DPRINTFD("Heartbeat Action Response received..."); + + app_action->running = false; + app_action->event = event; + app_action->notify = notify; + app_action->vote_result = vote_result; + snprintf(app_action->log_msg, GUEST_HEARTBEAT_API_MSG_MAX_LOG_SIZE, "%s", + log_msg); + + if (GUEST_TIMER_ID_INVALID != connection->action_timer) + { + error = guest_timer_deregister(connection->action_timer); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to cancel action timer, error=%s.", + guest_error_str(error)); + } + + connection->action_timer = GUEST_TIMER_ID_INVALID; + } + + guest_heartbeat_mgmt_api_handle_action_completed(); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Dispatch +// ========================================= +static void guest_heartbeat_mgmt_api_dispatch( int selobj ) +{ + static bool have_start = false; + static bool have_header = false; + static GuestHeartbeatApiMsgHeaderT hdr; + + bool more; + int bytes_received; + GuestHeartbeatMgmtApiConnectionT* connection; + GuestErrorT error; + + unsigned int connection_i; + for (connection_i=0; GUEST_APPLICATIONS_MAX > connection_i; ++connection_i) + { + connection = &(_connections[connection_i]); + if (connection->inuse) + if (selobj == connection->sock) + break; + } + + if (GUEST_APPLICATIONS_MAX <= connection_i) + { + DPRINTFE("Uknown selection object %i.", selobj); + close(selobj); + return; + } + + error = guest_unix_receive(connection->sock, connection->stream.end_ptr, + connection->stream.avail, &bytes_received); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to receive message, error=%s.", + guest_error_str(error)); + return; + } + + if (0 == bytes_received) + { + DPRINTFI("Connection closed on %i.", connection->sock); + guest_heartbeat_mgmt_api_close_connection(connection); + return; + } + + DPRINTFD("Bytes received is %i.", bytes_received); + + connection->stream.end_ptr += bytes_received; + connection->stream.avail -= bytes_received; + connection->stream.size += bytes_received; + + do + { + more = false; + + if (!have_start) + { + memset(&hdr, 0 ,sizeof(GuestHeartbeatApiMsgHeaderT)); + have_start = guest_stream_get_next(&(connection->stream)); + } + + if (have_start && !have_header) + { + if (sizeof(GuestHeartbeatApiMsgHeaderT) <= connection->stream.size) + { + char* ptr = connection->stream.bytes + + GUEST_HEARTBEAT_API_MSG_MAGIC_SIZE; + + hdr.version = *(uint8_t*) ptr; + ptr += sizeof(uint8_t); + hdr.revision = *(uint8_t*) ptr; + ptr += sizeof(uint8_t); + hdr.msg_type = *(uint16_t*) ptr; + ptr += sizeof(uint16_t); + hdr.sequence = *(uint32_t*) ptr; + ptr += sizeof(uint32_t); + hdr.size = *(uint32_t*) ptr; + ptr += sizeof(uint32_t); + + DPRINTFD("Message header: version=%i, revision=%i, " + "msg_type=%i, sequence=%u, size=%u", hdr.version, + hdr.revision, hdr.msg_type, hdr.sequence, hdr.size); + + if (GUEST_HEARTBEAT_API_MSG_VERSION_CURRENT == hdr.version) + { + have_header = true; + } else { + have_start = false; + have_header = false; + guest_stream_advance(GUEST_HEARTBEAT_API_MSG_MAGIC_SIZE, + &connection->stream); + more = true; + } + } + } + + if (have_start && have_header) + { + if (sizeof(GuestHeartbeatApiMsgT) <= connection->stream.size) + { + switch(hdr.msg_type) + { + case GUEST_HEARTBEAT_API_MSG_INIT: + guest_heartbeat_mgmt_api_recv_init(connection); + break; + + case GUEST_HEARTBEAT_API_MSG_FINAL: + guest_heartbeat_mgmt_api_recv_final(connection); + break; + + case GUEST_HEARTBEAT_API_MSG_CHALLENGE_RESPONSE: + guest_heartbeat_mgmt_api_recv_challenge_response(connection); + break; + + case GUEST_HEARTBEAT_API_MSG_ACTION_RESPONSE: + guest_heartbeat_mgmt_api_recv_action_response(connection); + break; + + default: + DPRINTFV("Unknown message type %i.", + (int) hdr.msg_type); + break; + } + + have_start = false; + have_header = false; + guest_stream_advance(sizeof(GuestHeartbeatApiMsgT), + &(connection->stream)); + more = true; + } + } + } while (more); + + if (0 >= connection->stream.avail) + guest_stream_reset(&(connection->stream)); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Connect Handler +// ================================================ +static void guest_heartbeat_mgmt_api_connect_handler( int selobj, char* address ) +{ + int stream_size; + GuestSelObjCallbacksT callbacks; + GuestHeartbeatMgmtApiConnectionT* connection; + GuestErrorT error; + + DPRINTFD("Connect on socket %i.", selobj); + + // Find unused connection. + unsigned int connection_i; + for (connection_i=0; GUEST_APPLICATIONS_MAX > connection_i; ++connection_i) + { + connection = &(_connections[connection_i]); + if (!connection->inuse) + { + memset(connection, 0, sizeof(GuestHeartbeatMgmtApiConnectionT)); + connection->inuse = true; + connection->registered = false; + connection->sock = selobj; + connection->heartbeat_timer = GUEST_TIMER_ID_INVALID; + connection->heartbeat_timeout_timer = GUEST_TIMER_ID_INVALID; + connection->action_timer = GUEST_TIMER_ID_INVALID; + break; + } + } + + if (GUEST_APPLICATIONS_MAX <= connection_i) + { + // Find unregistered connection and replace. + unsigned int connection_i; + for (connection_i=0; GUEST_APPLICATIONS_MAX > connection_i; ++connection_i) + { + connection = &(_connections[connection_i]); + if ((connection->inuse) && (!connection->registered)) + { + guest_heartbeat_mgmt_api_close_connection(connection); + connection->inuse = true; + connection->registered = false; + connection->sock = selobj; + break; + } + } + } + + if (GUEST_APPLICATIONS_MAX <= connection_i) + { + DPRINTFE("Failed to allocate connection."); + close(selobj); + return; + } + + stream_size = sizeof(GuestHeartbeatApiMsgT)*4; + if (8192 > stream_size) + stream_size = 8192; + + error = guest_stream_setup(GUEST_HEARTBEAT_API_MSG_MAGIC_VALUE, + GUEST_HEARTBEAT_API_MSG_MAGIC_SIZE, + stream_size, &(connection->stream)); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to setup stream, error=%s.", guest_error_str(error)); + close(connection->sock); + memset(connection, 0, sizeof(GuestHeartbeatMgmtApiConnectionT)); + connection->sock = -1; + connection->heartbeat_timer = GUEST_TIMER_ID_INVALID; + connection->heartbeat_timeout_timer = GUEST_TIMER_ID_INVALID; + connection->action_timer = GUEST_TIMER_ID_INVALID; + + return; + } + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.read_callback = guest_heartbeat_mgmt_api_dispatch; + + error = guest_selobj_register(connection->sock, &callbacks); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to register select on unix socket, error=%s.", + guest_error_str(error)); + close(connection->sock); + guest_stream_release(&(connection->stream)); + memset(connection, 0, sizeof(GuestHeartbeatMgmtApiConnectionT)); + connection->sock = -1; + connection->heartbeat_timer = GUEST_TIMER_ID_INVALID; + connection->heartbeat_timeout_timer = GUEST_TIMER_ID_INVALID; + connection->action_timer = GUEST_TIMER_ID_INVALID; + return; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Get Health +// =========================================== +GuestErrorT guest_heartbeat_mgmt_api_get_health( + bool* health, GuestHeartbeatActionT* corrective_action, char log_msg[], + int log_msg_size ) +{ + GuestHeartbeatMgmtApiAppHealthT* app_health; + GuestHeartbeatMgmtApiConnectionT* connection; + GuestHeartbeatActionT update; + + *health = true; + *corrective_action = GUEST_HEARTBEAT_ACTION_NONE; + log_msg[0] = '\0'; + + unsigned int connection_i; + for (connection_i=0; GUEST_APPLICATIONS_MAX > connection_i; ++connection_i) + { + connection = &(_connections[connection_i]); + if ((connection->inuse) && (connection->registered)) + { + app_health = &(connection->application_health); + if (!(app_health->healthy)) + { + update = guest_heartbeat_merge_action( + *corrective_action, app_health->corrective_action); + + if (update == app_health->corrective_action) + { + *health = false; + *corrective_action = update; + snprintf(log_msg, log_msg_size, "%s", app_health->log_msg); + } + } + } + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Action Timeout +// =============================================== +static bool guest_heartbeat_mgmt_api_action_timeout( GuestTimerIdT timer_id ) +{ + GuestHeartbeatMgmtApiAppActionT* app_action; + GuestHeartbeatMgmtApiConnectionT* connection; + + unsigned int connection_i; + for (connection_i=0; GUEST_APPLICATIONS_MAX > connection_i; ++connection_i) + { + connection = &(_connections[connection_i]); + if (connection->inuse) + if (timer_id == connection->action_timer) + break; + } + + if (GUEST_APPLICATIONS_MAX <= connection_i) + { + DPRINTFE("Uknown timer %i.", timer_id); + return false; // don't rearm + } + + app_action = &(connection->application_action); + + if (app_action->running) + { + app_action->running = false; + app_action->vote_result = GUEST_HEARTBEAT_VOTE_RESULT_TIMEOUT; + } + + guest_heartbeat_mgmt_api_handle_action_completed(); + connection->action_timer = GUEST_TIMER_ID_INVALID; + return false; // don't rearm +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Action Abort +// ============================================= +void guest_heartbeat_mgmt_api_action_abort( void ) +{ + GuestHeartbeatMgmtApiAppConfigT* app_config; + GuestHeartbeatMgmtApiAppActionT* app_action; + GuestHeartbeatMgmtApiConnectionT* connection; + GuestErrorT error; + + unsigned int connection_i; + for (connection_i=0; GUEST_APPLICATIONS_MAX > connection_i; ++connection_i) + { + connection = &(_connections[connection_i]); + if (connection->inuse && connection->registered) + { + app_config = &(connection->application_config); + app_action = &(connection->application_action); + + if (app_action->running) + { + DPRINTFI("Aborting action %s for application %s, " + "notification=%s.", app_config->name, + guest_heartbeat_event_str(app_action->event), + guest_heartbeat_notify_str(app_action->notify)); + } + + app_action->running = false; + + if (GUEST_TIMER_ID_INVALID != connection->action_timer) + { + error = guest_timer_deregister(connection->action_timer); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to cancel action timer, error=%s.", + guest_error_str(error)); + } + connection->action_timer = GUEST_TIMER_ID_INVALID; + } + } + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Action Notify +// ============================================== +GuestErrorT guest_heartbeat_mgmt_api_action_notify( + GuestHeartbeatEventT event, GuestHeartbeatNotifyT notify, bool* wait, + GuestHeartbeatMgmtApiActionResponseT callback ) +{ + int action_timeout_ms; + GuestHeartbeatMgmtApiAppConfigT* app_config; + GuestHeartbeatMgmtApiAppActionT* app_action; + GuestHeartbeatMgmtApiConnectionT* connection; + GuestErrorT error; + + *wait = false; + _callback = NULL; + + unsigned int connection_i; + for (connection_i=0; GUEST_APPLICATIONS_MAX > connection_i; ++connection_i) + { + connection = &(_connections[connection_i]); + if (connection->inuse && connection->registered) + { + app_config = &(connection->application_config); + app_action = &(connection->application_action); + + app_action->running = true; + app_action->invocation_id = rand(); + app_action->event = event; + app_action->notify = notify; + app_action->vote_result = GUEST_HEARTBEAT_VOTE_RESULT_UNKNOWN; + + error = guest_heartbeat_mgmt_api_send_action_notify( + connection->sock, app_action->invocation_id, event, + notify ); + if (GUEST_OKAY == error) + { + DPRINTFI("Sent action to appplication %s for event %s, " + "notification=%s.", app_config->name, + guest_heartbeat_event_str(event), + guest_heartbeat_notify_str(notify)); + } else { + DPRINTFE("Failed to send action to application %s for " + "event %s, notification=%s.", app_config->name, + guest_heartbeat_event_str(event), + guest_heartbeat_notify_str(notify)); + } + + if (GUEST_HEARTBEAT_NOTIFY_IRREVOCABLE == notify) + { + switch (event) + { + case GUEST_HEARTBEAT_EVENT_STOP: + case GUEST_HEARTBEAT_EVENT_REBOOT: + action_timeout_ms = app_config->shutdown_notice_ms; + break; + + case GUEST_HEARTBEAT_EVENT_PAUSE: + case GUEST_HEARTBEAT_EVENT_SUSPEND: + case GUEST_HEARTBEAT_EVENT_RESIZE_BEGIN: + case GUEST_HEARTBEAT_EVENT_LIVE_MIGRATE_BEGIN: + case GUEST_HEARTBEAT_EVENT_COLD_MIGRATE_BEGIN: + action_timeout_ms = app_config->suspend_notice_ms; + break; + + case GUEST_HEARTBEAT_EVENT_UNPAUSE: + case GUEST_HEARTBEAT_EVENT_RESUME: + case GUEST_HEARTBEAT_EVENT_RESIZE_END: + case GUEST_HEARTBEAT_EVENT_LIVE_MIGRATE_END: + case GUEST_HEARTBEAT_EVENT_COLD_MIGRATE_END: + action_timeout_ms = app_config->resume_notice_ms; + break; + + default: + action_timeout_ms = app_config->shutdown_notice_ms; + break; + } + } else { + action_timeout_ms = app_config->vote_ms; + } + + error = guest_timer_register(action_timeout_ms, + guest_heartbeat_mgmt_api_action_timeout, + &(connection->action_timer)); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to register action timeout timer, error=%s.", + guest_error_str(error)); + abort(); + } + + *wait = true; + _callback = callback; + } + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Initialize +// =========================================== +GuestErrorT guest_heartbeat_mgmt_api_initialize( void ) +{ + GuestHeartbeatMgmtApiConnectionT* connection; + GuestErrorT error; + + memset(&_connections, 0, sizeof(_connections)); + + unsigned int connection_i; + for (connection_i=0; GUEST_APPLICATIONS_MAX > connection_i; ++connection_i) + { + connection = &(_connections[connection_i]); + connection->sock = -1; + connection->heartbeat_timer = GUEST_TIMER_ID_INVALID; + connection->heartbeat_timeout_timer = GUEST_TIMER_ID_INVALID; + connection->action_timer = GUEST_TIMER_ID_INVALID; + } + + error = guest_unix_open(&_sock); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to open unix socket, error=%s.", + guest_error_str(error)); + return error; + } + + error = guest_unix_listen(_sock, GUEST_HEARTBEAT_API_MSG_ADDRESS, + guest_heartbeat_mgmt_api_connect_handler); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to listen on unix socket, error=%s.", + guest_error_str(error)); + return error; + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Finalize +// ========================================= +GuestErrorT guest_heartbeat_mgmt_api_finalize( void ) +{ + GuestHeartbeatMgmtApiConnectionT* connection; + GuestErrorT error; + + if (0 <= _sock) + { + error = guest_selobj_deregister(_sock); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to deregister select on unix socket, error=%s.", + guest_error_str(error)); + } + + error = guest_unix_close(_sock); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to close unix socket, error=%s.", + guest_error_str(error)); + } + _sock = -1; + } + + unsigned int connection_i; + for (connection_i=0; GUEST_APPLICATIONS_MAX > connection_i; ++connection_i) + { + connection = &(_connections[connection_i]); + guest_heartbeat_mgmt_api_close_connection(connection); + } + + memset(&_connections, 0, sizeof(_connections)); + return GUEST_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_mgmt_api.h b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_mgmt_api.h new file mode 100755 index 00000000..d5663322 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_mgmt_api.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_HERATBEAT_MGMT_API_H__ +#define __GUEST_HEARTBEAT_MGMT_API_H__ + +#include + +#include "guest_types.h" +#include "guest_heartbeat_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*GuestHeartbeatMgmtApiActionResponseT) + (GuestHeartbeatEventT event, GuestHeartbeatNotifyT notify, + GuestHeartbeatVoteResultT vote_result, char log_msg[]); + +// **************************************************************************** +// Guest Heartbeat Management API - Get Health +// =========================================== +extern GuestErrorT guest_heartbeat_mgmt_api_get_health( + bool* health, GuestHeartbeatActionT* corrective_action, char log_msg[], + int log_msg_size ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Action Abort +// ============================================= +extern void guest_heartbeat_mgmt_api_action_abort( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Action Notify +// ============================================== +extern GuestErrorT guest_heartbeat_mgmt_api_action_notify( + GuestHeartbeatEventT event, GuestHeartbeatNotifyT notify, bool* wait, + GuestHeartbeatMgmtApiActionResponseT callback ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Initialize +// =========================================== +extern GuestErrorT guest_heartbeat_mgmt_api_initialize( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Management API - Finalize +// ========================================= +extern GuestErrorT guest_heartbeat_mgmt_api_finalize( void ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_HEARTBEAT_MGMT_API_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_msg.c b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_msg.c new file mode 100755 index 00000000..11df2feb --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_msg.c @@ -0,0 +1,1209 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_heartbeat_msg_defs.h" +#include "guest_heartbeat_msg.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "guest_limits.h" +#include "guest_types.h" +#include "guest_debug.h" +#include "guest_selobj.h" +#include "guest_channel.h" +#include "guest_signal.h" +#include "guest_utils.h" + +#include "guest_heartbeat_types.h" + +#define GUEST_HEARTBEAT_PROPAGATION_DELAY_IN_SECS 1 +#define GUEST_HEARTBEAT_CHALLENGE_DEPTH 6 + +static int _signal_fd = -1; +static int _challenge_depth; +static uint32_t _last_tx_challenge[GUEST_HEARTBEAT_CHALLENGE_DEPTH]; +static uint32_t _last_rx_challenge; +static uint32_t _msg_sequence; +static GuestChannelIdT _channel_id = GUEST_CHANNEL_ID_INVALID; +static GuestHeartbeatMsgCallbacksT _callbacks; +// Tokener serves as reassembly buffer for host connection. +static struct json_tokener* tok; + +// **************************************************************************** +// Guest Heartbeat Message - Action (Host to Network) +// ================================================== +static const char *guest_heartbeat_msg_action_hton( + GuestHeartbeatActionT action ) +{ + switch (action) + { + case GUEST_HEARTBEAT_ACTION_NONE: + return GUEST_HEARTBEAT_MSG_ACTION_NONE; + case GUEST_HEARTBEAT_ACTION_REBOOT: + return GUEST_HEARTBEAT_MSG_ACTION_REBOOT; + case GUEST_HEARTBEAT_ACTION_STOP: + return GUEST_HEARTBEAT_MSG_ACTION_STOP; + case GUEST_HEARTBEAT_ACTION_LOG: + return GUEST_HEARTBEAT_MSG_ACTION_LOG; + default: + DPRINTFE("Unknown action %i.", action); + return GUEST_HEARTBEAT_MSG_ACTION_UNKNOWN; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Action (Network to Host) +// ================================================== +static GuestHeartbeatActionT guest_heartbeat_msg_action_ntoh( + const char *action ) +{ + if (!strcmp(action, GUEST_HEARTBEAT_MSG_ACTION_REBOOT)) + { + return GUEST_HEARTBEAT_ACTION_REBOOT; + } + else if (!strcmp(action, GUEST_HEARTBEAT_MSG_ACTION_STOP)) { + return GUEST_HEARTBEAT_ACTION_STOP; + } + else if (!strcmp(action, GUEST_HEARTBEAT_MSG_ACTION_LOG)) { + return GUEST_HEARTBEAT_ACTION_LOG; + } + else { + DPRINTFE("Unknown action %i.", action); + return GUEST_HEARTBEAT_ACTION_UNKNOWN; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Event (Host to Network) +// ================================================== +static const char *guest_heartbeat_msg_event_hton( + GuestHeartbeatEventT event ) +{ + switch (event) + { + case GUEST_HEARTBEAT_EVENT_STOP: + return GUEST_HEARTBEAT_MSG_EVENT_STOP; + case GUEST_HEARTBEAT_EVENT_REBOOT: + return GUEST_HEARTBEAT_MSG_EVENT_REBOOT; + case GUEST_HEARTBEAT_EVENT_SUSPEND: + return GUEST_HEARTBEAT_MSG_EVENT_SUSPEND; + case GUEST_HEARTBEAT_EVENT_PAUSE: + return GUEST_HEARTBEAT_MSG_EVENT_PAUSE; + case GUEST_HEARTBEAT_EVENT_UNPAUSE: + return GUEST_HEARTBEAT_MSG_EVENT_UNPAUSE; + case GUEST_HEARTBEAT_EVENT_RESUME: + return GUEST_HEARTBEAT_MSG_EVENT_RESUME; + case GUEST_HEARTBEAT_EVENT_RESIZE_BEGIN: + return GUEST_HEARTBEAT_MSG_EVENT_RESIZE_BEGIN; + case GUEST_HEARTBEAT_EVENT_RESIZE_END: + return GUEST_HEARTBEAT_MSG_EVENT_RESIZE_END; + case GUEST_HEARTBEAT_EVENT_LIVE_MIGRATE_BEGIN: + return GUEST_HEARTBEAT_MSG_EVENT_LIVE_MIGRATE_BEGIN; + case GUEST_HEARTBEAT_EVENT_LIVE_MIGRATE_END: + return GUEST_HEARTBEAT_MSG_EVENT_LIVE_MIGRATE_END; + case GUEST_HEARTBEAT_EVENT_COLD_MIGRATE_BEGIN: + return GUEST_HEARTBEAT_MSG_EVENT_COLD_MIGRATE_BEGIN; + case GUEST_HEARTBEAT_EVENT_COLD_MIGRATE_END: + return GUEST_HEARTBEAT_MSG_EVENT_COLD_MIGRATE_END; + default: + DPRINTFE("Unknown event %i.", event); + return GUEST_HEARTBEAT_MSG_EVENT_UNKNOWN; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Event (Network to Host) +// ================================================= +static GuestHeartbeatEventT guest_heartbeat_msg_event_ntoh( + const char *event ) +{ + if (!strcmp(event, GUEST_HEARTBEAT_MSG_EVENT_STOP)) + { + return GUEST_HEARTBEAT_EVENT_STOP; + } + else if (!strcmp(event, GUEST_HEARTBEAT_MSG_EVENT_REBOOT)) { + return GUEST_HEARTBEAT_EVENT_REBOOT; + } + else if (!strcmp(event, GUEST_HEARTBEAT_MSG_EVENT_SUSPEND)) { + return GUEST_HEARTBEAT_EVENT_SUSPEND; + } + else if (!strcmp(event, GUEST_HEARTBEAT_MSG_EVENT_PAUSE)) { + return GUEST_HEARTBEAT_EVENT_PAUSE; + } + else if (!strcmp(event, GUEST_HEARTBEAT_MSG_EVENT_UNPAUSE)) { + return GUEST_HEARTBEAT_EVENT_UNPAUSE; + } + else if (!strcmp(event, GUEST_HEARTBEAT_MSG_EVENT_RESUME)) { + return GUEST_HEARTBEAT_EVENT_RESUME; + } + else if (!strcmp(event, GUEST_HEARTBEAT_MSG_EVENT_RESIZE_BEGIN)) { + return GUEST_HEARTBEAT_EVENT_RESIZE_BEGIN; + } + else if (!strcmp(event, GUEST_HEARTBEAT_MSG_EVENT_RESIZE_END)) { + return GUEST_HEARTBEAT_EVENT_RESIZE_END; + } + else if (!strcmp(event, GUEST_HEARTBEAT_MSG_EVENT_LIVE_MIGRATE_BEGIN)) { + return GUEST_HEARTBEAT_EVENT_LIVE_MIGRATE_BEGIN; + } + else if (!strcmp(event, GUEST_HEARTBEAT_MSG_EVENT_LIVE_MIGRATE_END)) { + return GUEST_HEARTBEAT_EVENT_LIVE_MIGRATE_END; + } + else if (!strcmp(event, GUEST_HEARTBEAT_MSG_EVENT_COLD_MIGRATE_BEGIN)) { + return GUEST_HEARTBEAT_EVENT_COLD_MIGRATE_BEGIN; + } + else if (!strcmp(event, GUEST_HEARTBEAT_MSG_EVENT_COLD_MIGRATE_END)) { + return GUEST_HEARTBEAT_EVENT_COLD_MIGRATE_END; + } + else { + DPRINTFE("Unknown event %i.", event); + return GUEST_HEARTBEAT_EVENT_UNKNOWN; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Notify (Host to Network) +// ================================================== +static const char *guest_heartbeat_msg_notify_hton( + GuestHeartbeatNotifyT notify ) +{ + switch (notify) + { + case GUEST_HEARTBEAT_NOTIFY_REVOCABLE: + return GUEST_HEARTBEAT_MSG_NOTIFY_REVOCABLE; + case GUEST_HEARTBEAT_NOTIFY_IRREVOCABLE: + return GUEST_HEARTBEAT_MSG_NOTIFY_IRREVOCABLE; + default: + DPRINTFE("Unknown notify %i.", notify); + return GUEST_HEARTBEAT_MSG_NOTIFY_UNKNOWN; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Notify (Network to Host) +// ================================================== +static GuestHeartbeatNotifyT guest_heartbeat_msg_notify_ntoh( + const char *notify ) +{ + if (!strcmp(notify, GUEST_HEARTBEAT_MSG_NOTIFY_REVOCABLE)) + { + return GUEST_HEARTBEAT_NOTIFY_REVOCABLE; + } + else if (!strcmp(notify, GUEST_HEARTBEAT_MSG_NOTIFY_IRREVOCABLE)) { + return GUEST_HEARTBEAT_NOTIFY_IRREVOCABLE; + } + else { + DPRINTFE("Unknown notify %i.", notify); + return GUEST_HEARTBEAT_NOTIFY_UNKNOWN; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Vote Result (Host to Network) +// ======================================================= +static const char * guest_heartbeat_msg_vote_result_hton( + GuestHeartbeatVoteResultT vote_result ) +{ + switch (vote_result) + { + case GUEST_HEARTBEAT_VOTE_RESULT_ACCEPT: + return GUEST_HEARTBEAT_MSG_VOTE_RESULT_ACCEPT; + case GUEST_HEARTBEAT_VOTE_RESULT_REJECT: + return GUEST_HEARTBEAT_MSG_VOTE_RESULT_REJECT; + case GUEST_HEARTBEAT_VOTE_RESULT_COMPLETE: + return GUEST_HEARTBEAT_MSG_VOTE_RESULT_COMPLETE; + case GUEST_HEARTBEAT_VOTE_RESULT_TIMEOUT: + return GUEST_HEARTBEAT_MSG_VOTE_RESULT_TIMEOUT; + case GUEST_HEARTBEAT_VOTE_RESULT_ERROR: + return GUEST_HEARTBEAT_MSG_VOTE_RESULT_ERROR; + default: + DPRINTFE("Unknown vote result %i.", vote_result); + return GUEST_HEARTBEAT_MSG_VOTE_RESULT_UNKNOWN; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Vote Result (Network to Host) +// ======================================================= +static GuestHeartbeatVoteResultT guest_heartbeat_msg_vote_result_ntoh( + const char *vote_result ) +{ + if (!strcmp(vote_result, GUEST_HEARTBEAT_MSG_VOTE_RESULT_ACCEPT)) + { + return GUEST_HEARTBEAT_VOTE_RESULT_ACCEPT; + } + else if (!strcmp(vote_result, GUEST_HEARTBEAT_MSG_VOTE_RESULT_REJECT)) { + return GUEST_HEARTBEAT_VOTE_RESULT_REJECT; + } + else if (!strcmp(vote_result, GUEST_HEARTBEAT_MSG_VOTE_RESULT_COMPLETE)) { + return GUEST_HEARTBEAT_VOTE_RESULT_COMPLETE; + } + else if (!strcmp(vote_result, GUEST_HEARTBEAT_MSG_VOTE_RESULT_TIMEOUT)) { + return GUEST_HEARTBEAT_VOTE_RESULT_TIMEOUT; + } + else if (!strcmp(vote_result, GUEST_HEARTBEAT_MSG_VOTE_RESULT_ERROR)) { + return GUEST_HEARTBEAT_VOTE_RESULT_ERROR; + } + else { + DPRINTFE("Unknown vote result %i.", vote_result); + return GUEST_HEARTBEAT_VOTE_RESULT_UNKNOWN; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Send Init +// =================================== +GuestErrorT guest_heartbeat_msg_send_init( + int invocation_id, GuestHeartbeatMsgInitDataT* data ) +{ + GuestErrorT error; + + char msg[GUEST_HEARTBEAT_MSG_MAX_MSG_SIZE]; + snprintf(msg, sizeof(msg), "\n{\"%s\":%d,\"%s\":%d,\"%s\":\"%s\",\"%s\":%d," + "\"%s\":%d,\"%s\":\"%s\"," + "\"%s\":%d,\"%s\":%d,\"%s\":%d,\"%s\":%d,\"%s\":%d,\"%s\":%d," + "\"%s\":\"%s\"}\n", + GUEST_HEARTBEAT_MSG_VERSION, GUEST_HEARTBEAT_MSG_VERSION_CURRENT, + GUEST_HEARTBEAT_MSG_REVISION, GUEST_HEARTBEAT_MSG_REVISION_CURRENT, + GUEST_HEARTBEAT_MSG_MSG_TYPE, GUEST_HEARTBEAT_MSG_INIT, + GUEST_HEARTBEAT_MSG_SEQUENCE, ++_msg_sequence, + + GUEST_HEARTBEAT_MSG_INVOCATION_ID, invocation_id, + GUEST_HEARTBEAT_MSG_NAME, data->name, + + GUEST_HEARTBEAT_MSG_HEARTBEAT_INTERVAL_MS, + data->heartbeat_interval_ms, + GUEST_HEARTBEAT_MSG_VOTE_SECS, + data->vote_ms/1000 + GUEST_HEARTBEAT_PROPAGATION_DELAY_IN_SECS, + GUEST_HEARTBEAT_MSG_SHUTDOWN_NOTICE_SECS, + data->shutdown_notice_ms/1000 + GUEST_HEARTBEAT_PROPAGATION_DELAY_IN_SECS, + GUEST_HEARTBEAT_MSG_SUSPEND_NOTICE_SECS, + data->suspend_notice_ms/1000 + GUEST_HEARTBEAT_PROPAGATION_DELAY_IN_SECS, + GUEST_HEARTBEAT_MSG_RESUME_NOTICE_SECS, + data->resume_notice_ms/1000 + GUEST_HEARTBEAT_PROPAGATION_DELAY_IN_SECS, + GUEST_HEARTBEAT_MSG_RESTART_SECS, + data->restart_ms/1000 + GUEST_HEARTBEAT_PROPAGATION_DELAY_IN_SECS, + + GUEST_HEARTBEAT_MSG_CORRECTIVE_ACTION, + guest_heartbeat_msg_action_hton(data->corrective_action)); + + error = guest_channel_send(_channel_id, msg, strlen(msg)); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to send guest heartbeat init message, error=%s.", + guest_error_str(error)); + return error; + } + + DPRINTFI("Sent heartbeat init message, invocation_id=%i.", invocation_id); + DPRINTFD("Sent heartbeat init message: %s", msg); + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Send Init Ack +// ======================================= +GuestErrorT guest_heartbeat_msg_send_init_ack( int invocation_id ) +{ + GuestErrorT error; + + char msg[GUEST_HEARTBEAT_MSG_MAX_MSG_SIZE]; + snprintf(msg, sizeof(msg), "\n{\"%s\":%d,\"%s\":%d,\"%s\":\"%s\",\"%s\":%d," + "\"%s\":%d}\n", + GUEST_HEARTBEAT_MSG_VERSION, GUEST_HEARTBEAT_MSG_VERSION_CURRENT, + GUEST_HEARTBEAT_MSG_REVISION, GUEST_HEARTBEAT_MSG_REVISION_CURRENT, + GUEST_HEARTBEAT_MSG_MSG_TYPE, GUEST_HEARTBEAT_MSG_INIT_ACK, + GUEST_HEARTBEAT_MSG_SEQUENCE, ++_msg_sequence, + + GUEST_HEARTBEAT_MSG_INVOCATION_ID, invocation_id); + + error = guest_channel_send(_channel_id, msg, strlen(msg)); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to send guest heartbeat init ack message, error=%s.", + guest_error_str(error)); + return error; + } + + DPRINTFI("Sent heartbeat init ack message: %s", msg); + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Send Exit +// =================================== +GuestErrorT guest_heartbeat_msg_send_exit( char log_msg[] ) +{ + GuestErrorT error; + + char log_msg_buf[GUEST_HEARTBEAT_MSG_MAX_LOG_SIZE]; + snprintf(log_msg_buf, GUEST_HEARTBEAT_MSG_MAX_LOG_SIZE, "%s", log_msg); + + char msg[GUEST_HEARTBEAT_MSG_MAX_MSG_SIZE]; + snprintf(msg, sizeof(msg), "\n{\"%s\":%d,\"%s\":%d,\"%s\":\"%s\",\"%s\":%d," + "\"%s\":\"%s\"}\n", + GUEST_HEARTBEAT_MSG_VERSION, GUEST_HEARTBEAT_MSG_VERSION_CURRENT, + GUEST_HEARTBEAT_MSG_REVISION, GUEST_HEARTBEAT_MSG_REVISION_CURRENT, + GUEST_HEARTBEAT_MSG_MSG_TYPE, GUEST_HEARTBEAT_MSG_EXIT, + GUEST_HEARTBEAT_MSG_SEQUENCE, ++_msg_sequence, + + GUEST_HEARTBEAT_MSG_LOG_MSG, log_msg_buf); + + error = guest_channel_send(_channel_id, msg, strlen(msg)); + + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to send guest heartbeat exit message, error=%s.", + guest_error_str(error)); + return error; + } + + DPRINTFI("Sent heartbeat exit message: %s", msg); + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Send Challenge +// ======================================== +GuestErrorT guest_heartbeat_msg_send_challenge( void ) +{ + GuestErrorT error; + + ++_challenge_depth; + if (GUEST_HEARTBEAT_CHALLENGE_DEPTH <= _challenge_depth) + _challenge_depth = 0; + + _last_tx_challenge[_challenge_depth] = rand(); + + char msg[GUEST_HEARTBEAT_MSG_MAX_MSG_SIZE]; + snprintf(msg, sizeof(msg), "\n{\"%s\":%d,\"%s\":%d,\"%s\":\"%s\",\"%s\":%d," + "\"%s\":%d}\n", + GUEST_HEARTBEAT_MSG_VERSION, GUEST_HEARTBEAT_MSG_VERSION_CURRENT, + GUEST_HEARTBEAT_MSG_REVISION, GUEST_HEARTBEAT_MSG_REVISION_CURRENT, + GUEST_HEARTBEAT_MSG_MSG_TYPE, GUEST_HEARTBEAT_MSG_CHALLENGE, + GUEST_HEARTBEAT_MSG_SEQUENCE, ++_msg_sequence, + + GUEST_HEARTBEAT_MSG_HEARTBEAT_CHALLENGE, _last_tx_challenge[_challenge_depth]); + + error = guest_channel_send(_channel_id, msg, strlen(msg)); + + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to send guest heartbeat challenge message, " + "error=%s.", guest_error_str(error)); + return error; + } + + DPRINTFD("Sent heartbeat challenge message, challenge=%i.", + _last_tx_challenge[_challenge_depth]); + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Send Challenge Response +// ================================================= +GuestErrorT guest_heartbeat_msg_send_challenge_response( + bool health, GuestHeartbeatActionT corrective_action, char log_msg[] ) +{ + GuestErrorT error; + + char log_msg_buf[GUEST_HEARTBEAT_MSG_MAX_LOG_SIZE]; + snprintf(log_msg_buf, GUEST_HEARTBEAT_MSG_MAX_LOG_SIZE, "%s", log_msg); + + char msg[GUEST_HEARTBEAT_MSG_MAX_MSG_SIZE]; + snprintf(msg, sizeof(msg), "\n{\"%s\":%d,\"%s\":%d,\"%s\":\"%s\",\"%s\":%d," + "\"%s\":%d,\"%s\":\"%s\",\"%s\":\"%s\",\"%s\":\"%s\"}\n", + GUEST_HEARTBEAT_MSG_VERSION, GUEST_HEARTBEAT_MSG_VERSION_CURRENT, + GUEST_HEARTBEAT_MSG_REVISION, GUEST_HEARTBEAT_MSG_REVISION_CURRENT, + GUEST_HEARTBEAT_MSG_MSG_TYPE, GUEST_HEARTBEAT_MSG_CHALLENGE_RESPONSE, + GUEST_HEARTBEAT_MSG_SEQUENCE, ++_msg_sequence, + + GUEST_HEARTBEAT_MSG_HEARTBEAT_RESPONSE, _last_rx_challenge, + GUEST_HEARTBEAT_MSG_HEARTBEAT_HEALTH, + health ? GUEST_HEARTBEAT_MSG_HEALTHY : GUEST_HEARTBEAT_MSG_UNHEALTHY, + GUEST_HEARTBEAT_MSG_CORRECTIVE_ACTION, + guest_heartbeat_msg_action_hton(corrective_action), + GUEST_HEARTBEAT_MSG_LOG_MSG, log_msg_buf); + + error = guest_channel_send(_channel_id, msg, strlen(msg)); + + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to send guest heartbeat challenge response message, " + "error=%s.", guest_error_str(error)); + return error; + } + // print info logs with message content only if not healthy + if (!health) + { + DPRINTFI("Unhealthy, sent heartbeat challenge response message: %s", msg); + } + else { + DPRINTFD("Sent heartbeat challenge response message, challenge=%i.", + _last_rx_challenge); + } + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Send Action Notify +// ============================================ +GuestErrorT guest_heartbeat_msg_send_action_notify( + int invocation_id, GuestHeartbeatEventT event, + GuestHeartbeatNotifyT notify, int timeout_ms ) +{ + GuestErrorT error; + + char msg[GUEST_HEARTBEAT_MSG_MAX_MSG_SIZE]; + snprintf(msg, sizeof(msg), "\n{\"%s\":%d,\"%s\":%d,\"%s\":\"%s\",\"%s\":%d," + "\"%s\":%d,\"%s\":\"%s\",\"%s\":\"%s\",\"%s\":%d}\n", + GUEST_HEARTBEAT_MSG_VERSION, GUEST_HEARTBEAT_MSG_VERSION_CURRENT, + GUEST_HEARTBEAT_MSG_REVISION, GUEST_HEARTBEAT_MSG_REVISION_CURRENT, + GUEST_HEARTBEAT_MSG_MSG_TYPE, GUEST_HEARTBEAT_MSG_ACTION_NOTIFY, + GUEST_HEARTBEAT_MSG_SEQUENCE, ++_msg_sequence, + + GUEST_HEARTBEAT_MSG_INVOCATION_ID, invocation_id, + GUEST_HEARTBEAT_MSG_EVENT_TYPE, guest_heartbeat_msg_event_hton(event), + GUEST_HEARTBEAT_MSG_NOTIFICATION_TYPE, guest_heartbeat_msg_notify_hton(notify), + GUEST_HEARTBEAT_MSG_TIMEOUT_MS, timeout_ms); + + error = guest_channel_send(_channel_id, msg, strlen(msg)); + + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to send guest heartbeat action notify message, " + "error=%s.", guest_error_str(error)); + return error; + } + + DPRINTFI("Sent heartbeat action notify message, invocation_id=%i.", + invocation_id); + DPRINTFD("Sent heartbeat action notify message: %s", msg); + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Send Action Response +// ============================================== +GuestErrorT guest_heartbeat_msg_send_action_response( + int invocation_id, GuestHeartbeatEventT event, + GuestHeartbeatNotifyT notify, GuestHeartbeatVoteResultT vote_result, + char log_msg[] ) +{ + GuestErrorT error; + + char log_msg_buf[GUEST_HEARTBEAT_MSG_MAX_LOG_SIZE]; + snprintf(log_msg_buf, GUEST_HEARTBEAT_MSG_MAX_LOG_SIZE, "%s", log_msg); + + + char msg[GUEST_HEARTBEAT_MSG_MAX_MSG_SIZE]; + snprintf(msg, sizeof(msg), "\n{\"%s\":%d,\"%s\":%d,\"%s\":\"%s\",\"%s\":%d," + "\"%s\":%d,\"%s\":\"%s\",\"%s\":\"%s\",\"%s\":\"%s\",\"%s\":\"%s\"}\n", + GUEST_HEARTBEAT_MSG_VERSION, GUEST_HEARTBEAT_MSG_VERSION_CURRENT, + GUEST_HEARTBEAT_MSG_REVISION, GUEST_HEARTBEAT_MSG_REVISION_CURRENT, + GUEST_HEARTBEAT_MSG_MSG_TYPE, GUEST_HEARTBEAT_MSG_ACTION_RESPONSE, + GUEST_HEARTBEAT_MSG_SEQUENCE, ++_msg_sequence, + + GUEST_HEARTBEAT_MSG_INVOCATION_ID, invocation_id, + GUEST_HEARTBEAT_MSG_EVENT_TYPE, guest_heartbeat_msg_event_hton(event), + GUEST_HEARTBEAT_MSG_NOTIFICATION_TYPE, guest_heartbeat_msg_notify_hton(notify), + GUEST_HEARTBEAT_MSG_VOTE_RESULT, guest_heartbeat_msg_vote_result_hton(vote_result), + GUEST_HEARTBEAT_MSG_LOG_MSG, log_msg_buf); + + error = guest_channel_send(_channel_id, msg, strlen(msg)); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to send guest heartbeat action response message, " + "error=%s.", guest_error_str(error)); + return error; + } + + DPRINTFI("Sent heartbeat action response message: %s", msg); + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Receive Init +// ====================================== +static void guest_heartbeat_msg_recv_init( struct json_object *jobj_msg ) +{ + char name[GUEST_HEARTBEAT_MSG_MAX_NAME_SIZE]; + uint32_t invocation_id; + char corrective_action[GUEST_HEARTBEAT_MSG_MAX_VALUE_SIZE]; + GuestHeartbeatMsgInitDataT data; + uint32_t vote_secs, shutdown_notice_secs, suspend_notice_secs; + uint32_t resume_notice_secs, restart_secs; + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_INVOCATION_ID, &invocation_id)) + return; + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_NAME, &name)) + return; + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_HEARTBEAT_INTERVAL_MS, &data.heartbeat_interval_ms)) + return; + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_VOTE_SECS, &vote_secs)) + return; + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_SHUTDOWN_NOTICE_SECS, &shutdown_notice_secs)) + return; + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_SUSPEND_NOTICE_SECS, &suspend_notice_secs)) + return; + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_RESUME_NOTICE_SECS, &resume_notice_secs)) + return; + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_RESTART_SECS, &restart_secs)) + return; + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_CORRECTIVE_ACTION, &corrective_action)) + return; + + data.vote_ms = vote_secs*1000; + data.shutdown_notice_ms = shutdown_notice_secs*1000; + data.suspend_notice_ms = suspend_notice_secs*1000; + data.resume_notice_ms= resume_notice_secs*1000; + data.restart_ms = restart_secs*1000; + + snprintf(data.name, GUEST_NAME_MAX_CHAR, "%s", name); + data.corrective_action = guest_heartbeat_msg_action_ntoh(corrective_action); + + DPRINTFI("Heartbeat Init received, invocation_id=%i", invocation_id); + + const char *msg = json_object_to_json_string_ext(jobj_msg, JSON_C_TO_STRING_PLAIN); + DPRINTFD("Heartbeat Init message received: %s", msg); + + if (NULL != _callbacks.recv_init) + _callbacks.recv_init(invocation_id, &data); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Receive Init Ack +// ========================================== +static void guest_heartbeat_msg_recv_init_ack( struct json_object *jobj_msg ) +{ + uint32_t invocation_id; + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_INVOCATION_ID, &invocation_id)) + return; + + DPRINTFI("Heartbeat Init Ack received, invocation_id=%i.", + invocation_id); + + const char *msg = json_object_to_json_string_ext(jobj_msg, JSON_C_TO_STRING_PLAIN); + DPRINTFD("Heartbeat Init Ack message received: %s", msg); + + if (NULL != _callbacks.recv_init_ack) + _callbacks.recv_init_ack(invocation_id); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Receive Exit +// ====================================== +static void guest_heartbeat_msg_recv_exit( struct json_object *jobj_msg ) +{ + char log_msg[GUEST_HEARTBEAT_MSG_MAX_LOG_SIZE]; + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_LOG_MSG, &log_msg)) + return; + + DPRINTFI("Heartbeat Exit received, msg=%s.", log_msg); + + const char *msg = json_object_to_json_string_ext(jobj_msg, JSON_C_TO_STRING_PLAIN); + DPRINTFD("Heartbeat Exit message received: %s", msg); + + if (NULL != _callbacks.recv_exit) + _callbacks.recv_exit(log_msg); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Receive Challenge +// =========================================== +static void guest_heartbeat_msg_recv_challenge( struct json_object *jobj_msg ) +{ + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_HEARTBEAT_CHALLENGE, &_last_rx_challenge)) + return; + + DPRINTFD("Heartbeat Challenge received, challenge=%i.", _last_rx_challenge); + + if (NULL != _callbacks.recv_challenge) + _callbacks.recv_challenge(); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Receive Challenge Ack +// =============================================== +static void guest_heartbeat_msg_recv_challenge_ack( struct json_object *jobj_msg ) +{ + char health[GUEST_HEARTBEAT_MSG_MAX_VALUE_SIZE]; + char corrective_action_str[GUEST_HEARTBEAT_MSG_MAX_VALUE_SIZE]; + GuestHeartbeatActionT corrective_action; + char log_msg[GUEST_HEARTBEAT_MSG_MAX_LOG_SIZE]; + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_HEARTBEAT_RESPONSE, &_last_rx_challenge)) + return; + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_HEARTBEAT_HEALTH, &health)) + return; + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_CORRECTIVE_ACTION, &corrective_action_str)) + return; + + corrective_action = guest_heartbeat_msg_action_ntoh(corrective_action_str); + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_LOG_MSG, &log_msg)) + return; + + DPRINTFD("Heartbeat Challenge Response received, challenge=%i.", + _last_rx_challenge); + + unsigned int challenge_i; + for (challenge_i=0; GUEST_HEARTBEAT_CHALLENGE_DEPTH > challenge_i; + ++challenge_i) + { + if (_last_tx_challenge[challenge_i] == _last_rx_challenge) + break; + } + + if (GUEST_HEARTBEAT_CHALLENGE_DEPTH == challenge_i) + { + DPRINTFE("Mismatch between last transmitted challenges and last " + "received challenge."); + return; + } + if (NULL != _callbacks.recv_challenge_ack) + _callbacks.recv_challenge_ack(!strcmp(health, GUEST_HEARTBEAT_MSG_HEALTHY), + corrective_action, log_msg); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Receive Action Notify +// =============================================== +static void guest_heartbeat_msg_recv_action_notify( struct json_object *jobj_msg ) +{ + uint32_t invocation_id; + char event_type[GUEST_HEARTBEAT_MSG_MAX_VALUE_SIZE]; + char notification_type[GUEST_HEARTBEAT_MSG_MAX_VALUE_SIZE]; + uint32_t timeout_ms; + GuestHeartbeatEventT event; + GuestHeartbeatNotifyT notify; + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_INVOCATION_ID, &invocation_id)) + return; + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_EVENT_TYPE, &event_type)) + return; + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_NOTIFICATION_TYPE, ¬ification_type)) + return; + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_TIMEOUT_MS, &timeout_ms)) + return; + + if (timeout_ms > (GUEST_HEARTBEAT_PROPAGATION_DELAY_IN_SECS*1000)) + timeout_ms -= (GUEST_HEARTBEAT_PROPAGATION_DELAY_IN_SECS * 1000); + + event = guest_heartbeat_msg_event_ntoh(event_type); + notify = guest_heartbeat_msg_notify_ntoh(notification_type); + + DPRINTFI("Heartbeat Action Notify received, invocation_id=%i.", + invocation_id); + const char *msg = json_object_to_json_string_ext(jobj_msg, JSON_C_TO_STRING_PLAIN); + DPRINTFD("Heartbeat Action Notify message received: %s", msg); + + if (NULL != _callbacks.recv_action_notify) + _callbacks.recv_action_notify(invocation_id, event, notify, timeout_ms); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Receive Action Response +// ================================================= +static void guest_heartbeat_msg_recv_action_response( struct json_object *jobj_msg ) +{ + uint32_t invocation_id; + char event_type[GUEST_HEARTBEAT_MSG_MAX_VALUE_SIZE]; + char notification_type[GUEST_HEARTBEAT_MSG_MAX_VALUE_SIZE]; + char vote_result[GUEST_HEARTBEAT_MSG_MAX_VALUE_SIZE]; + GuestHeartbeatEventT event; + GuestHeartbeatNotifyT notify; + GuestHeartbeatVoteResultT result; + char log_msg[GUEST_HEARTBEAT_MSG_MAX_LOG_SIZE]; + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_INVOCATION_ID, &invocation_id)) + return; + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_EVENT_TYPE, &event_type)) + return; + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_NOTIFICATION_TYPE, ¬ification_type)) + return; + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_VOTE_RESULT, &vote_result)) + return; + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_LOG_MSG, &log_msg)) + return; + + event = guest_heartbeat_msg_event_ntoh(event_type); + notify = guest_heartbeat_msg_notify_ntoh(notification_type); + result = guest_heartbeat_msg_vote_result_ntoh(vote_result); + + DPRINTFI("Heartbeat Action Response received, invocation_id=%i.", + invocation_id); + const char *msg = json_object_to_json_string_ext(jobj_msg, JSON_C_TO_STRING_PLAIN); + DPRINTFD("Heartbeat Action Response message received: %s", msg); + + if (NULL != _callbacks.recv_action_response) + _callbacks.recv_action_response(invocation_id, event, notify, + result, log_msg); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Receive Nack +// ================================================= +static void guest_heartbeat_msg_recv_nack( struct json_object *jobj_msg ) +{ + uint32_t invocation_id; + char log_msg[GUEST_HEARTBEAT_MSG_MAX_LOG_SIZE]; + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_INVOCATION_ID, &invocation_id)) + return; + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_LOG_MSG, &log_msg)) + return; + + DPRINTFE("Heartbeat Nack message received, invocation_id=%i, error msg: %s", + invocation_id, log_msg); + + const char *msg = json_object_to_json_string_ext(jobj_msg, JSON_C_TO_STRING_PLAIN); + DPRINTFD("Heartbeat Nack message received: %s", msg); + +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Dispatch +// ================================== +void guest_heartbeat_msg_dispatch(json_object *jobj_msg) +{ + int version; + char msg_type[GUEST_HEARTBEAT_MSG_MAX_VALUE_SIZE]; + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_VERSION, &version)) + return; + + if (GUEST_HEARTBEAT_MSG_VERSION_CURRENT != version) + { + DPRINTFI("message received version %d, expected %d, dropping\n", + version, GUEST_HEARTBEAT_MSG_VERSION_CURRENT); + return; + } + + if (guest_utils_json_get_value(jobj_msg, GUEST_HEARTBEAT_MSG_MSG_TYPE, &msg_type)) + return; + + if (!strcmp(msg_type, GUEST_HEARTBEAT_MSG_INIT)) { + guest_heartbeat_msg_recv_init(jobj_msg); + } else if (!strcmp(msg_type, GUEST_HEARTBEAT_MSG_INIT_ACK)) { + guest_heartbeat_msg_recv_init_ack(jobj_msg); + } else if (!strcmp(msg_type, GUEST_HEARTBEAT_MSG_EXIT)) { + guest_heartbeat_msg_recv_exit(jobj_msg); + } else if (!strcmp(msg_type, GUEST_HEARTBEAT_MSG_CHALLENGE)) { + guest_heartbeat_msg_recv_challenge(jobj_msg); + } else if (!strcmp(msg_type, GUEST_HEARTBEAT_MSG_CHALLENGE_RESPONSE)) { + guest_heartbeat_msg_recv_challenge_ack(jobj_msg); + } else if (!strcmp(msg_type, GUEST_HEARTBEAT_MSG_ACTION_NOTIFY)) { + guest_heartbeat_msg_recv_action_notify(jobj_msg); + } else if (!strcmp(msg_type, GUEST_HEARTBEAT_MSG_ACTION_RESPONSE)) { + guest_heartbeat_msg_recv_action_response(jobj_msg); + } else if (!strcmp(msg_type, GUEST_HEARTBEAT_MSG_NACK)) { + guest_heartbeat_msg_recv_nack(jobj_msg); + } else { + DPRINTFV("Unknown message type %s.", msg_type); + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Parser +// ================================== +/** + Multiple messages from the host can be bundled together into a single "read" + so we need to check message boundaries and handle breaking the message apart. + Assume a valid message does not contain newline '\n', and newline is added to + the beginning and end of each message by the sender to delimit the boundaries. +*/ +void guest_heartbeat_msg_parser(void *buf, ssize_t len, json_tokener* tok, int newline_found) +{ + json_object *jobj = json_tokener_parse_ex(tok, buf, len); + enum json_tokener_error jerr = json_tokener_get_error(tok); + + if (jerr == json_tokener_success) { + guest_heartbeat_msg_dispatch(jobj); + json_object_put(jobj); + return; + } + + else if (jerr == json_tokener_continue) { + // partial JSON is parsed , continue to read from socket. + if (newline_found) { + // if newline was found in the middle of the buffer, the message + // should be completed at this point. Throw out incomplete message + // by resetting tokener. + json_tokener_reset(tok); + } + } + else + { + // parsing error + json_tokener_reset(tok); + } +} +// **************************************************************************** + + +// **************************************************************************** +// Guest Heartbeat Message - Handler +// ================================== +void guest_heartbeat_msg_handler(void *buf, ssize_t len,json_tokener* tok) +{ + void *newline; + ssize_t len_head; + +next: + if (len == 0) + return; + + // search for newline as delimiter + newline = memchr(buf, '\n', len); + + if (newline) { + // split buffer to head and tail at the location of newline. + // feed the head to the parser and recursively process the tail. + len_head = newline-buf; + + // parse head + if (len_head > 0) + guest_heartbeat_msg_parser(buf, len_head, tok, 1); + + // start of the tail: skip newline + buf += len_head+1; + // length of the tail: deduct 1 for the newline character + len -= len_head+1; + + // continue to process the tail. + goto next; + } + else { + guest_heartbeat_msg_parser(buf, len, tok, 0); + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Receive +// ================================== +static void guest_heartbeat_msg_receiver( int selobj ) +{ + int bytes_received; + GuestErrorT error; + char buf[4096]; + + error = guest_channel_receive(_channel_id, buf, sizeof(buf), + &bytes_received); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to receive message, error=%s.", + guest_error_str(error)); + return; + } + + DPRINTFV("Bytes received is %i.", bytes_received); + guest_heartbeat_msg_handler(buf, bytes_received, tok); + +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Signal Handler +// ======================================== +static void guest_heartbeat_msg_signal_handler( int signum ) +{ + int64_t sigval = signum; + int result; + + if ((SIGIO == signum) && (0 <= _signal_fd)) + { + result = write(_signal_fd, &sigval, sizeof(sigval)); + if (0 > result) + { + DPRINTFE("Failed to write signal, error=%s", strerror(errno)); + return; + } + + guest_signal_ignore(signum); + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Hangup +// ================================ +static void guest_heartbeat_msg_hangup( int selobj ) +{ + DPRINTFI("Heartbeat messaging hangup."); + + if (GUEST_CHANNEL_ID_INVALID != _channel_id) + { + int selobj; + + selobj = guest_channel_get_selobj(_channel_id); + if (0 <= selobj) + { + GuestErrorT error; + + error = guest_selobj_deregister(selobj); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to deregister selection object %i, " + "error=%s.", selobj, guest_error_str(error)); + } + + guest_signal_register_handler(SIGIO, + guest_heartbeat_msg_signal_handler); + + if (NULL != _callbacks.channel_state_change) + _callbacks.channel_state_change(false); + } + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Signal Dispatch +// ========================================= +static void guest_heartbeat_msg_signal_dispatch( int selobj ) +{ + int signum; + int64_t sigval = 0; + int result; + GuestSelObjCallbacksT callbacks; + GuestErrorT error; + + result = read(_signal_fd, &sigval, sizeof(sigval)); + if (0 > result) + { + if (EINTR == errno) + { + DPRINTFD("Interrupted on signal read, error=%s.", strerror(errno)); + } else { + DPRINTFE("Failed to dispatch signal, error=%s.", strerror(errno)); + } + return; + } + + signum = sigval; + + if (SIGIO == signum) + { + DPRINTFI("Heartbeat messaging available."); + + if (GUEST_CHANNEL_ID_INVALID != _channel_id) + { + selobj = guest_channel_get_selobj(_channel_id); + if (0 <= selobj) + { + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.read_callback = guest_heartbeat_msg_receiver; + callbacks.hangup_callback = guest_heartbeat_msg_hangup; + + error = guest_selobj_register(selobj, &callbacks); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to register selection object %i, " + "error=%s.", selobj, guest_error_str(error)); + abort(); + } + + if (NULL != _callbacks.channel_state_change) + _callbacks.channel_state_change(true); + } + } + } else { + DPRINTFI("Ignoring signal %i.", signum); + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Initialize +// ==================================== +GuestErrorT guest_heartbeat_msg_initialize( + char* comm_device, GuestHeartbeatMsgCallbacksT* callbacks ) +{ + int selobj; + GuestSelObjCallbacksT selobj_callbacks; + GuestErrorT error; + + _channel_id = GUEST_CHANNEL_ID_INVALID; + + error = guest_channel_open(comm_device, &_channel_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to open communication channel over device %s, " + "error=%s.", comm_device, guest_error_str(error)); + return error; + } + + selobj = guest_channel_get_selobj(_channel_id); + if (0 <= selobj) + { + memset(&selobj_callbacks, 0, sizeof(selobj_callbacks)); + selobj_callbacks.read_callback = guest_heartbeat_msg_receiver; + selobj_callbacks.hangup_callback = guest_heartbeat_msg_hangup; + + error = guest_selobj_register(selobj, &selobj_callbacks); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to register selection object %i, error=%s.", + selobj, guest_error_str(error)); + return error; + } + } + + _signal_fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + if (0 > _signal_fd) + { + DPRINTFE("Failed to open signal file descriptor,error=%s.", + strerror(errno)); + return GUEST_FAILED; + } + + memset(&selobj_callbacks, 0, sizeof(selobj_callbacks)); + selobj_callbacks.read_callback = guest_heartbeat_msg_signal_dispatch; + + error = guest_selobj_register(_signal_fd, &selobj_callbacks); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to register selection object, error=%s.", + guest_error_str(error)); + close(_signal_fd); + _signal_fd = -1; + return error; + } + + memcpy(&_callbacks, callbacks, sizeof(GuestHeartbeatMsgCallbacksT)); + + tok = json_tokener_new(); + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Finalize +// ================================== +GuestErrorT guest_heartbeat_msg_finalize( void ) +{ + int selobj; + GuestErrorT error; + + memset(&_callbacks, 0, sizeof(GuestHeartbeatMsgCallbacksT)); + free(tok); + + if (0 <= _signal_fd) + { + error = guest_selobj_deregister(_signal_fd); + if (GUEST_OKAY != error) + DPRINTFE("Failed to deregister selection object, error=%s.", + guest_error_str(error)); + + close(_signal_fd); + _signal_fd = -1; + } + + if (GUEST_CHANNEL_ID_INVALID != _channel_id) + { + selobj = guest_channel_get_selobj(_channel_id); + if (0 <= selobj) + { + error = guest_selobj_deregister(selobj); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to deregister selection object %i, error=%s.", + selobj, guest_error_str(error)); + } + } + + error = guest_channel_close(_channel_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed close communication channel, error=%s.", + guest_error_str(error)); + } + _channel_id = GUEST_CHANNEL_ID_INVALID; + } + + return GUEST_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_msg.h b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_msg.h new file mode 100755 index 00000000..4bb296c3 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_msg.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_HERATBEAT_MESSAGE_H__ +#define __GUEST_HEARTBEAT_MESSAGE_H__ + +#include +#include + +#include "guest_limits.h" +#include "guest_types.h" + +#include "guest_heartbeat_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define GUEST_HEARTBEAT_MAX_LOG_MSG_SIZE 192 + +typedef struct { + char name[GUEST_NAME_MAX_CHAR]; + unsigned int heartbeat_interval_ms; + unsigned int vote_ms; + unsigned int shutdown_notice_ms; + unsigned int suspend_notice_ms; + unsigned int resume_notice_ms; + unsigned int restart_ms; + GuestHeartbeatActionT corrective_action; +} GuestHeartbeatMsgInitDataT; + +typedef void (*GuestHeartbeatMsgChannelStateChangeT) (bool state); +typedef void (*GuestHeartbeatMsgRecvInitT) + (int invocation_id, GuestHeartbeatMsgInitDataT* data); +typedef void (*GuestHeartbeatMsgRecvInitAckT) (int invocation_id); +typedef void (*GuestHeartbeatMsgRecvExitT) (char log_msg[]); +typedef void (*GuestHeartbeatMsgRecvChallengeT) (void); +typedef void (*GuestHeartbeatMsgRecvChallengeAckT) + (bool health, GuestHeartbeatActionT corrective_action, char log_msg[]); +typedef void (*GuestHeartbeatMsgRecvActionNotifyT) + (int invocation_id, GuestHeartbeatEventT event, + GuestHeartbeatNotifyT notify, int timeout_ms); +typedef void (*GuestHeartbeatMsgRecvActionResponseT) + (int invocation_id, GuestHeartbeatEventT event, + GuestHeartbeatNotifyT notify, GuestHeartbeatVoteResultT vote_result, + char log_msg[]); + +typedef struct { + GuestHeartbeatMsgChannelStateChangeT channel_state_change; + GuestHeartbeatMsgRecvInitT recv_init; + GuestHeartbeatMsgRecvInitAckT recv_init_ack; + GuestHeartbeatMsgRecvExitT recv_exit; + GuestHeartbeatMsgRecvChallengeT recv_challenge; + GuestHeartbeatMsgRecvChallengeAckT recv_challenge_ack; + GuestHeartbeatMsgRecvActionNotifyT recv_action_notify; + GuestHeartbeatMsgRecvActionResponseT recv_action_response; +} GuestHeartbeatMsgCallbacksT; + +// **************************************************************************** +// Guest Heartbeat Message - Send Init +// =================================== +extern GuestErrorT guest_heartbeat_msg_send_init( int invocation_id, + GuestHeartbeatMsgInitDataT* data ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Send Init Ack +// ======================================= +extern GuestErrorT guest_heartbeat_msg_send_init_ack( int invocation_id ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Send Exit +// =================================== +extern GuestErrorT guest_heartbeat_msg_send_exit( char log_msg[] ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Send Challenge +// ======================================== +extern GuestErrorT guest_heartbeat_msg_send_challenge( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Send Challenge Response +// ================================================= +extern GuestErrorT guest_heartbeat_msg_send_challenge_response( + bool health, GuestHeartbeatActionT corrective_action, char log_msg[] ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Send Action Notify +// ============================================ +extern GuestErrorT guest_heartbeat_msg_send_action_notify( + int invocation_id, GuestHeartbeatEventT event, + GuestHeartbeatNotifyT notify, int timeout_ms ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Send Action Response +// ============================================== +GuestErrorT guest_heartbeat_msg_send_action_response( + int invocation_id, GuestHeartbeatEventT event, + GuestHeartbeatNotifyT notify, GuestHeartbeatVoteResultT vote_result, + char log_msg[] ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Initialize +// ==================================== +extern GuestErrorT guest_heartbeat_msg_initialize( + char* comm_device, GuestHeartbeatMsgCallbacksT* callbacks ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Message - Finalize +// ================================== +extern GuestErrorT guest_heartbeat_msg_finalize( void ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_HEARTBEAT_MESSAGE_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_msg_defs.h b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_msg_defs.h new file mode 100755 index 00000000..2a12c86f --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_msg_defs.h @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_HEARTBEAT_MESSAGE_DEFINITIONS_H__ +#define __GUEST_HEARTBEAT_MESSAGE_DEFINITIONS_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define GUEST_HEARTBEAT_MSG_VERSION_CURRENT 3 +#define GUEST_HEARTBEAT_MSG_REVISION_CURRENT 1 + +#define GUEST_HEARTBEAT_MSG_HEALTHY "healthy" +#define GUEST_HEARTBEAT_MSG_UNHEALTHY "unhealthy" + +// maximum size of a string value except instance name and log msg +#define GUEST_HEARTBEAT_MSG_MAX_VALUE_SIZE 32 +// maximum size of instance name +#define GUEST_HEARTBEAT_MSG_MAX_NAME_SIZE 64 +#define GUEST_HEARTBEAT_MSG_MAX_LOG_SIZE 224 +#define GUEST_HEARTBEAT_MSG_MAX_MSG_SIZE 1056 +#define GUEST_HEARTBEAT_MSG_MIN_MSG_SIZE 32 + +// *IMPORTANT NOTE* The keys defined here should match those defined in +// NFV-VIM in order to properly encode/decode REST-API messages. + +// Keys for Repair Action +#define GUEST_HEARTBEAT_MSG_ACTION_UNKNOWN "unknown" +#define GUEST_HEARTBEAT_MSG_ACTION_NONE "none" +#define GUEST_HEARTBEAT_MSG_ACTION_REBOOT "reboot" +#define GUEST_HEARTBEAT_MSG_ACTION_STOP "stop" +#define GUEST_HEARTBEAT_MSG_ACTION_LOG "log" + +// Keys for Event Type +#define GUEST_HEARTBEAT_MSG_EVENT_UNKNOWN "unknown" +#define GUEST_HEARTBEAT_MSG_EVENT_STOP "stop" +#define GUEST_HEARTBEAT_MSG_EVENT_REBOOT "reboot" +#define GUEST_HEARTBEAT_MSG_EVENT_SUSPEND "suspend" +#define GUEST_HEARTBEAT_MSG_EVENT_PAUSE "pause" +#define GUEST_HEARTBEAT_MSG_EVENT_UNPAUSE "unpause" +#define GUEST_HEARTBEAT_MSG_EVENT_RESUME "resume" +#define GUEST_HEARTBEAT_MSG_EVENT_RESIZE_BEGIN "resize_begin" +#define GUEST_HEARTBEAT_MSG_EVENT_RESIZE_END "resize_end" +#define GUEST_HEARTBEAT_MSG_EVENT_LIVE_MIGRATE_BEGIN "live_migrate_begin" +#define GUEST_HEARTBEAT_MSG_EVENT_LIVE_MIGRATE_END "live_migrate_end" +#define GUEST_HEARTBEAT_MSG_EVENT_COLD_MIGRATE_BEGIN "cold_migrate_begin" +#define GUEST_HEARTBEAT_MSG_EVENT_COLD_MIGRATE_END "cold_migrate_end" + +// Keys for Notification Type +#define GUEST_HEARTBEAT_MSG_NOTIFY_UNKNOWN "unknown" +#define GUEST_HEARTBEAT_MSG_NOTIFY_REVOCABLE "revocable" +#define GUEST_HEARTBEAT_MSG_NOTIFY_IRREVOCABLE "irrevocable" + +// Keys for Vote Result +#define GUEST_HEARTBEAT_MSG_VOTE_RESULT_UNKNOWN "unknown" +#define GUEST_HEARTBEAT_MSG_VOTE_RESULT_ACCEPT "accept" +#define GUEST_HEARTBEAT_MSG_VOTE_RESULT_REJECT "reject" +#define GUEST_HEARTBEAT_MSG_VOTE_RESULT_COMPLETE "complete" +#define GUEST_HEARTBEAT_MSG_VOTE_RESULT_TIMEOUT "timeout" +#define GUEST_HEARTBEAT_MSG_VOTE_RESULT_ERROR "error" + +// client registers for heartbeat service +#define GUEST_HEARTBEAT_MSG_INIT "init" +// server accepts new client +#define GUEST_HEARTBEAT_MSG_INIT_ACK "init_ack" +// client intends to exit +#define GUEST_HEARTBEAT_MSG_EXIT "exit" +// server challenges client, are you healthy +#define GUEST_HEARTBEAT_MSG_CHALLENGE "challenge" +// client response to challenge +#define GUEST_HEARTBEAT_MSG_CHALLENGE_RESPONSE "challenge_response" +// server proposes/demands action +#define GUEST_HEARTBEAT_MSG_ACTION_NOTIFY "action_notify" +// client votes on action, or indicates action complete +#define GUEST_HEARTBEAT_MSG_ACTION_RESPONSE "action_response" +// server notify client of failure in processing client message +#define GUEST_HEARTBEAT_MSG_NACK "nack" + +// Keys for messages between Host and Guest +#define GUEST_HEARTBEAT_MSG_VERSION "version" +#define GUEST_HEARTBEAT_MSG_REVISION "revision" +#define GUEST_HEARTBEAT_MSG_MSG_TYPE "msg_type" +#define GUEST_HEARTBEAT_MSG_SEQUENCE "sequence" +#define GUEST_HEARTBEAT_MSG_INVOCATION_ID "invocation_id" +#define GUEST_HEARTBEAT_MSG_NAME "name" +#define GUEST_HEARTBEAT_MSG_HEARTBEAT_INTERVAL_MS "heartbeat_interval_ms" +#define GUEST_HEARTBEAT_MSG_VOTE_SECS "vote_secs" +#define GUEST_HEARTBEAT_MSG_SHUTDOWN_NOTICE_SECS "shutdown_notice_secs" +#define GUEST_HEARTBEAT_MSG_SUSPEND_NOTICE_SECS "suspend_notice_secs" +#define GUEST_HEARTBEAT_MSG_RESUME_NOTICE_SECS "resume_notice_secs" +#define GUEST_HEARTBEAT_MSG_RESTART_SECS "restart_secs" +#define GUEST_HEARTBEAT_MSG_CORRECTIVE_ACTION "corrective_action" +#define GUEST_HEARTBEAT_MSG_LOG_MSG "log_msg" +#define GUEST_HEARTBEAT_MSG_HEARTBEAT_CHALLENGE "heartbeat_challenge" +#define GUEST_HEARTBEAT_MSG_HEARTBEAT_RESPONSE "heartbeat_response" +#define GUEST_HEARTBEAT_MSG_HEARTBEAT_HEALTH "heartbeat_health" +#define GUEST_HEARTBEAT_MSG_EVENT_TYPE "event_type" +#define GUEST_HEARTBEAT_MSG_NOTIFICATION_TYPE "notification_type" +#define GUEST_HEARTBEAT_MSG_TIMEOUT_MS "timeout_ms" +#define GUEST_HEARTBEAT_MSG_VOTE_RESULT "vote_result" + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_HEARTBEAT_MESSAGE_DEFINITIONS_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_types.c b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_types.c new file mode 100755 index 00000000..6edcc9c8 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_types.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_heartbeat_types.h" + +// **************************************************************************** +// Guest Heartbeat Types - Action String +// ===================================== +const char* guest_heartbeat_action_str( GuestHeartbeatActionT action ) +{ + switch (action) + { + case GUEST_HEARTBEAT_ACTION_NONE: return "none"; + case GUEST_HEARTBEAT_ACTION_REBOOT: return "reboot"; + case GUEST_HEARTBEAT_ACTION_STOP: return "stop"; + case GUEST_HEARTBEAT_ACTION_LOG: return "log"; + default: + return "action-???"; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Types - Event String +// ==================================== +const char* guest_heartbeat_event_str( GuestHeartbeatEventT event ) +{ + switch (event) + { + case GUEST_HEARTBEAT_EVENT_STOP: return "stop"; + case GUEST_HEARTBEAT_EVENT_REBOOT: return "reboot"; + case GUEST_HEARTBEAT_EVENT_SUSPEND: return "suspend"; + case GUEST_HEARTBEAT_EVENT_PAUSE: return "pause"; + case GUEST_HEARTBEAT_EVENT_UNPAUSE: return "unpause"; + case GUEST_HEARTBEAT_EVENT_RESUME: return "resume"; + case GUEST_HEARTBEAT_EVENT_RESIZE_BEGIN: return "resize-begin"; + case GUEST_HEARTBEAT_EVENT_RESIZE_END: return "resize-end"; + case GUEST_HEARTBEAT_EVENT_LIVE_MIGRATE_BEGIN: return "live-migrate-begin"; + case GUEST_HEARTBEAT_EVENT_LIVE_MIGRATE_END: return "live-migrate-end"; + case GUEST_HEARTBEAT_EVENT_COLD_MIGRATE_BEGIN: return "cold-migrate-begin"; + case GUEST_HEARTBEAT_EVENT_COLD_MIGRATE_END: return "cold-migrate-end"; + default: + return "event-???"; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Types - Notify String +// ===================================== +const char* guest_heartbeat_notify_str( GuestHeartbeatNotifyT notify ) +{ + switch (notify) + { + case GUEST_HEARTBEAT_NOTIFY_REVOCABLE: return "revocable"; + case GUEST_HEARTBEAT_NOTIFY_IRREVOCABLE: return "irrevocable"; + default: + return "notify-???"; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Types - Vote Result String +// ========================================== +const char* guest_heartbeat_vote_result_str( GuestHeartbeatVoteResultT result ) +{ + switch (result) + { + case GUEST_HEARTBEAT_VOTE_RESULT_ACCEPT: return "accept"; + case GUEST_HEARTBEAT_VOTE_RESULT_REJECT: return "reject"; + case GUEST_HEARTBEAT_VOTE_RESULT_COMPLETE: return "complete"; + case GUEST_HEARTBEAT_VOTE_RESULT_TIMEOUT: return "timeout"; + case GUEST_HEARTBEAT_VOTE_RESULT_ERROR: return "error"; + default: + return "vote-???"; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Types - Merge Action +// ==================================== +GuestHeartbeatActionT guest_heartbeat_merge_action( + GuestHeartbeatActionT current_action, GuestHeartbeatActionT new_action ) +{ + switch (new_action) + { + case GUEST_HEARTBEAT_ACTION_STOP: + return new_action; + + case GUEST_HEARTBEAT_ACTION_REBOOT: + if (GUEST_HEARTBEAT_ACTION_STOP != current_action) + return new_action; + return current_action; + + case GUEST_HEARTBEAT_ACTION_LOG: + if ((GUEST_HEARTBEAT_ACTION_STOP != current_action) && + (GUEST_HEARTBEAT_ACTION_REBOOT != current_action)) + return new_action; + return current_action; + + default: + return current_action; + } +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_types.h b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_types.h new file mode 100755 index 00000000..bf0a5463 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/heartbeat/guest_heartbeat_types.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_HEARTBEAT_TYPES_H__ +#define __GUEST_HEARTBEAT_TYPES_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + GUEST_HEARTBEAT_ACTION_UNKNOWN, + GUEST_HEARTBEAT_ACTION_NONE, + GUEST_HEARTBEAT_ACTION_REBOOT, + GUEST_HEARTBEAT_ACTION_STOP, + GUEST_HEARTBEAT_ACTION_LOG, + GUEST_HEARTBEAT_ACTION_MAX, +} GuestHeartbeatActionT; + +typedef enum { + GUEST_HEARTBEAT_EVENT_UNKNOWN, + GUEST_HEARTBEAT_EVENT_STOP, + GUEST_HEARTBEAT_EVENT_REBOOT, + GUEST_HEARTBEAT_EVENT_SUSPEND, + GUEST_HEARTBEAT_EVENT_PAUSE, + GUEST_HEARTBEAT_EVENT_UNPAUSE, + GUEST_HEARTBEAT_EVENT_RESUME, + GUEST_HEARTBEAT_EVENT_RESIZE_BEGIN, + GUEST_HEARTBEAT_EVENT_RESIZE_END, + GUEST_HEARTBEAT_EVENT_LIVE_MIGRATE_BEGIN, + GUEST_HEARTBEAT_EVENT_LIVE_MIGRATE_END, + GUEST_HEARTBEAT_EVENT_COLD_MIGRATE_BEGIN, + GUEST_HEARTBEAT_EVENT_COLD_MIGRATE_END, + GUEST_HEARTBEAT_EVENT_MAX, +} GuestHeartbeatEventT; + +typedef enum { + GUEST_HEARTBEAT_NOTIFY_UNKNOWN, + GUEST_HEARTBEAT_NOTIFY_REVOCABLE, + GUEST_HEARTBEAT_NOTIFY_IRREVOCABLE, + GUEST_HEARTBEAT_NOTIFY_MAX, +} GuestHeartbeatNotifyT; + +typedef enum { + GUEST_HEARTBEAT_VOTE_RESULT_UNKNOWN, + GUEST_HEARTBEAT_VOTE_RESULT_ACCEPT, + GUEST_HEARTBEAT_VOTE_RESULT_REJECT, + GUEST_HEARTBEAT_VOTE_RESULT_COMPLETE, + GUEST_HEARTBEAT_VOTE_RESULT_TIMEOUT, + GUEST_HEARTBEAT_VOTE_RESULT_ERROR, + GUEST_HEARTBEAT_VOTE_RESULT_MAX, +} GuestHeartbeatVoteResultT; + +// **************************************************************************** +// Guest Heartbeat Types - Action String +// ===================================== +extern const char* guest_heartbeat_action_str( GuestHeartbeatActionT action ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Types - Event String +// ==================================== +extern const char* guest_heartbeat_event_str( GuestHeartbeatEventT event ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Types - Notify String +// ===================================== +extern const char* guest_heartbeat_notify_str( GuestHeartbeatNotifyT notify ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Types - Vote Result String +// ========================================== +extern const char* guest_heartbeat_vote_result_str( GuestHeartbeatVoteResultT result ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Types - Merge Action +// ==================================== +extern GuestHeartbeatActionT guest_heartbeat_merge_action( + GuestHeartbeatActionT current_action, GuestHeartbeatActionT new_action ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_HEARTBEAT_TYPES_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/test/Makefile b/guest-client/guest-client-3.0.1/guest_client/src/test/Makefile new file mode 100755 index 00000000..b58cb799 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/test/Makefile @@ -0,0 +1,77 @@ +# +# Copyright(c) 2013-2016, Wind River Systems, Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Wind River Systems nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +SHELL = /bin/sh + +## Configuration Directory Variables +prefix := /usr/local +exec_prefix := $(prefix) +sysconfdir := $(prefix)/etc +includedir := $(prefix)/include +libdir := $(exec_prefix)/lib +bindir := $(exec_prefix)/bin + +MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) +CURRENT_DIR := $(patsubst %/,%,$(dir $(MAKEFILE_PATH))) +BUILD_DIR := $(CURRENT_DIR)/build + +.SUFFIXES: +.SUFFIXES: .c .o + +.PHONY: all build create_build_dir clean distclean + +program_NAME := guest-client-test +program_C_INCLUDES := -I$(CURRENT_DIR) -I$(CURRENT_DIR)/../ +program_C_INCLUDES += -I$(CURRENT_DIR)/../heartbeat +program_C_INCLUDES += -I$(CURRENT_DIR)/../../include +program_C_SRCS := $(wildcard *.c) +program_C_OBJS := ${program_C_SRCS:.c=.o} +program_LDLIBS := -lrt + +CFLAGS = -g -O2 -Wall -Werror -Wformat -DSYSCONFDIR=$(sysconfdir) + +all: build + +%.o: %.c + $(CC) $(CFLAGS) $(program_C_INCLUDES) -c $^ -o $(BUILD_DIR)/$@ -ljson-c + +$(program_NAME): $(program_C_OBJS) + @(cd ../; make --no-print-directory build sysconfdir=$(sysconfdir) BUILD_DIR=$(BUILD_DIR)) + @-($(RM) $(BUILD_DIR)/guest_client.o $(BUILD_DIR)/guest-client) + $(CC) $(CFLAGS) $(BUILD_DIR)/*.o $(program_LDLIBS) -o $(BUILD_DIR)/$(program_NAME) -ljson-c + +create_build_dir: + mkdir -p --mode 755 $(BUILD_DIR) + +build: create_build_dir $(program_NAME) + +clean: + @-($(RM) $(BUILD_DIR)/*) + +distclean: clean diff --git a/guest-client/guest-client-3.0.1/guest_client/src/test/guest_test.c b/guest-client/guest-client-3.0.1/guest_client/src/test/guest_test.c new file mode 100755 index 00000000..a321f372 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/test/guest_test.c @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_test.h" + +#include +#include + +#include "guest_types.h" +#include "guest_debug.h" +#include "guest_timer.h" + +#include "guest_heartbeat_types.h" +#include "guest_heartbeat_msg.h" + +static bool _challenge_response_recvd = false; +static GuestTimerIdT _heartbeat_timer_id = GUEST_TIMER_ID_INVALID; +static GuestTimerIdT _heartbeat_timeout_timer_id = GUEST_TIMER_ID_INVALID; + +// **************************************************************************** +// Guest Heartbeat - Timeout +// ========================= +static bool guest_heartbeat_timeout( GuestTimerIdT timer_id ) +{ + GuestErrorT error; + + DPRINTFE("--------> HEARTBEAT TIMEOUT <--------"); + + if (GUEST_TIMER_ID_INVALID != _heartbeat_timer_id) + { + error = guest_timer_deregister(_heartbeat_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to cancel heartbeat timer, error=%s.", + guest_error_str(error)); + } + _heartbeat_timer_id = GUEST_TIMER_ID_INVALID; + } + + return false; // don't rearm +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat - Periodic +// ========================== +static bool guest_heartbeat_periodic( GuestTimerIdT timer_id ) +{ + GuestErrorT error; + + if (_challenge_response_recvd) + { + error = guest_heartbeat_msg_send_challenge(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to send challenge, error=%s.", + guest_error_str(error)); + } + _challenge_response_recvd = false; + } + return true; // rearm +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat - Receive Init Message +// ====================================== +static void guest_heartbeat_recv_init_msg( + int invocation_id, GuestHeartbeatMsgInitDataT* data ) +{ + GuestErrorT error; + + if (GUEST_TIMER_ID_INVALID != _heartbeat_timer_id) + { + error = guest_timer_deregister(_heartbeat_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to cancel heartbeat timer, error=%s.", + guest_error_str(error)); + } + _heartbeat_timer_id = GUEST_TIMER_ID_INVALID; + } + + error = guest_timer_register(data->heartbeat_interval_ms, + guest_heartbeat_periodic, + &_heartbeat_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to start heartbeat timer, error=%s.", + guest_error_str(error)); + return; + } + + error = guest_heartbeat_msg_send_init_ack(invocation_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to send init ack, error=%s.", guest_error_str(error)); + } + + error = guest_heartbeat_msg_send_challenge(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to send challenge, error=%s.", guest_error_str(error)); + } + + _challenge_response_recvd = false; + + if (GUEST_TIMER_ID_INVALID != _heartbeat_timeout_timer_id) + { + error = guest_timer_deregister(_heartbeat_timeout_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to cancel heartbeat timeout timer, error=%s.", + guest_error_str(error)); + } + _heartbeat_timeout_timer_id = GUEST_TIMER_ID_INVALID; + } + + error = guest_timer_register(data->heartbeat_interval_ms*2, + guest_heartbeat_timeout, + &_heartbeat_timeout_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to start heartbeat timeout timer, error=%s.", + guest_error_str(error)); + return; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat - Receive Exit Message +// ====================================== +static void guest_heartbeat_recv_exit_msg( char log_msg[] ) +{ + GuestErrorT error; + + DPRINTFI("--------> HEARTBEAT EXIT <--------"); + DPRINTFI("reason=%s", log_msg); + + if (GUEST_TIMER_ID_INVALID != _heartbeat_timer_id) + { + error = guest_timer_deregister(_heartbeat_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to cancel heartbeat timer, error=%s.", + guest_error_str(error)); + } + _heartbeat_timer_id = GUEST_TIMER_ID_INVALID; + } + + if (GUEST_TIMER_ID_INVALID != _heartbeat_timeout_timer_id) + { + error = guest_timer_deregister(_heartbeat_timeout_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to cancel heartbeat timeout timer, error=%s.", + guest_error_str(error)); + } + _heartbeat_timeout_timer_id = GUEST_TIMER_ID_INVALID; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat - Receive Challenge Ack Message +// =============================================== +static void guest_heartbeat_recv_challenge_ack_msg( + bool health, GuestHeartbeatActionT corrective_action, char log_msg[] ) +{ + GuestErrorT error; + + _challenge_response_recvd = true; + + if (!health) + { + DPRINTFE("--------> HEARTBEAT UNHEALTHY <--------"); + DPRINTFE("corrective_action=%s, reason=%s", + guest_heartbeat_action_str(corrective_action), log_msg); + + if (GUEST_TIMER_ID_INVALID != _heartbeat_timer_id) + { + error = guest_timer_deregister(_heartbeat_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to cancel heartbeat timer, error=%s.", + guest_error_str(error)); + } + _heartbeat_timer_id = GUEST_TIMER_ID_INVALID; + } + + if (GUEST_TIMER_ID_INVALID != _heartbeat_timeout_timer_id) + { + error = guest_timer_deregister(_heartbeat_timeout_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to cancel heartbeat timeout timer, error=%s.", + guest_error_str(error)); + } + _heartbeat_timeout_timer_id = GUEST_TIMER_ID_INVALID; + } + + return; + } + + guest_timer_reset(_heartbeat_timeout_timer_id); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat - Receive Action Response Message +// ================================================= +static void guest_heartbeat_recv_action_response_msg( + int invocation_id, GuestHeartbeatEventT event, + GuestHeartbeatNotifyT notify, GuestHeartbeatVoteResultT vote_result, + char log_msg[] ) +{ + DPRINTFI("--------> ACTION RESPONSE <--------"); + DPRINTFI("invocation_id=%i event=%s, notify=%s, vote-result=%s, reason=%s", + invocation_id, guest_heartbeat_event_str(event), + guest_heartbeat_notify_str(notify), + guest_heartbeat_vote_result_str(vote_result), log_msg); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Test - Initialize +// ======================= +GuestErrorT guest_test_initialize( char* comm_device ) +{ + GuestHeartbeatMsgCallbacksT callbacks; + GuestErrorT error; + + memset(&callbacks, 0, sizeof(callbacks)); + + callbacks.recv_init = guest_heartbeat_recv_init_msg; + callbacks.recv_exit = guest_heartbeat_recv_exit_msg; + callbacks.recv_challenge_ack = guest_heartbeat_recv_challenge_ack_msg; + callbacks.recv_action_response = guest_heartbeat_recv_action_response_msg; + + error = guest_heartbeat_msg_initialize(comm_device, &callbacks); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to initialize heartbeat messaging, error=%s.", + guest_error_str(error)); + return error; + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Test - Finalize +// ===================== +GuestErrorT guest_test_finalize( void ) +{ + GuestErrorT error; + + if (GUEST_TIMER_ID_INVALID != _heartbeat_timer_id) + { + error = guest_timer_deregister(_heartbeat_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to cancel heartbeat timer, error=%s.", + guest_error_str(error)); + } + _heartbeat_timer_id = GUEST_TIMER_ID_INVALID; + } + + if (GUEST_TIMER_ID_INVALID != _heartbeat_timeout_timer_id) + { + error = guest_timer_deregister(_heartbeat_timeout_timer_id); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to cancel heartbeat timeout timer, error=%s.", + guest_error_str(error)); + } + _heartbeat_timeout_timer_id = GUEST_TIMER_ID_INVALID; + } + + error = guest_heartbeat_msg_finalize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to finalize heartbeat messaging, error=%s.", + guest_error_str(error)); + } + + return GUEST_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/test/guest_test.h b/guest-client/guest-client-3.0.1/guest_client/src/test/guest_test.h new file mode 100755 index 00000000..409cac3c --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/test/guest_test.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_TEST_H__ +#define __GUEST_TEST_H__ + +#include "guest_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// **************************************************************************** +// Guest Test - Initialize +// ======================= +extern GuestErrorT guest_test_initialize( char* comm_device ); +// **************************************************************************** + +// **************************************************************************** +// Guest Test - Finalize +// ===================== +extern GuestErrorT guest_test_finalize( void ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_TEST_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/test/guest_test_cli.c b/guest-client/guest-client-3.0.1/guest_client/src/test/guest_test_cli.c new file mode 100755 index 00000000..094ce171 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/test/guest_test_cli.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_test_cli.h" + +#include +#include +#include +#include +#include + +#include "guest_types.h" +#include "guest_debug.h" +#include "guest_selobj.h" +#include "guest_stream.h" + +#include "guest_heartbeat_msg.h" + +static GuestStreamT _stream; + +// **************************************************************************** +// Guest Test CLI - Usage +// ====================== +void guest_test_cli_usage( void ) +{ + printf("***************************************************\n"); + printf("* CLI Options: *\n"); + printf("* 1 - send action request [pause, revocable] *\n"); + printf("* 2 - send action request [pause, irrevocable] *\n"); + printf("***************************************************\n"); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Test CLI - Dispatch +// ========================= +static void guest_test_cli_dispatch( int selobj ) +{ + int msg_size; + int bytes_received; + int result; + GuestErrorT error; + + result = read(STDIN_FILENO, _stream.end_ptr, _stream.avail); + if (0 > result) + { + if (EINTR == errno) + { + DPRINTFD("Interrupted on socket read, error=%s.", strerror(errno)); + return; + + } else { + DPRINTFE("Failed to read from socket, error=%s.", strerror(errno)); + return; + } + } else if (0 == result) { + DPRINTFD("No message received from socket."); + return; + + } else { + DPRINTFV("Received message, msg_size=%i.", result); + bytes_received = result; + } + + _stream.end_ptr += bytes_received; + _stream.avail -= bytes_received; + _stream.size += bytes_received; + + msg_size = guest_stream_get(&_stream); + if (0 <= msg_size) + { + _stream.bytes[msg_size] = '\0'; + DPRINTFD("CLI message: %s, msg_size=%i", _stream.bytes, msg_size); + + switch(_stream.bytes[0]) + { + case 'h': + guest_test_cli_usage(); + break; + + case '1': + error = guest_heartbeat_msg_send_action_notify( + rand(), GUEST_HEARTBEAT_EVENT_PAUSE, + GUEST_HEARTBEAT_NOTIFY_REVOCABLE, 5); + if (GUEST_OKAY != error) + DPRINTFE("Failed to send action notify, error=%s.", + guest_error_str(error)); + break; + + case '2': + error = guest_heartbeat_msg_send_action_notify( + rand(), GUEST_HEARTBEAT_EVENT_PAUSE, + GUEST_HEARTBEAT_NOTIFY_IRREVOCABLE, 5); + if (GUEST_OKAY != error) + DPRINTFE("Failed to send action notify, error=%s.", + guest_error_str(error)); + break; + + default: + break; + } + + guest_stream_advance(msg_size+1, &_stream); + } + + if (0 >= _stream.avail) + guest_stream_reset(&_stream); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Test CLI - Initialize +// =========================== +GuestErrorT guest_test_cli_initialize( void ) +{ + GuestSelObjCallbacksT callbacks; + GuestErrorT error; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.read_callback = guest_test_cli_dispatch; + + error = guest_selobj_register(STDIN_FILENO, &callbacks); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to register stdin selection object, error=%s.", + guest_error_str(error)); + return error; + } + + error = guest_stream_setup("\n", 1, 256*2, &_stream); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to setup stdin stream, error=%s.", + guest_error_str(error)); + return error; + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Test CLI - Finalize +// ========================= +GuestErrorT guest_test_cli_finalize( void ) +{ + GuestErrorT error; + + error = guest_stream_release(&_stream); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed release stream, error=%s.", guest_error_str(error)); + } + + error = guest_selobj_deregister(STDIN_FILENO); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to deregister stdin selection object, error=%s.", + guest_error_str(error)); + } + + return GUEST_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/test/guest_test_cli.h b/guest-client/guest-client-3.0.1/guest_client/src/test/guest_test_cli.h new file mode 100755 index 00000000..82be5861 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/test/guest_test_cli.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_TEST_CLI_H__ +#define __GUEST_TEST_CLI_H__ + +#include "guest_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// **************************************************************************** +// Guest Test CLI - Usage +// ====================== +extern void guest_test_cli_usage( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Test CLI - Initialize +// =========================== +extern GuestErrorT guest_test_cli_initialize( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Test CLI - Finalize +// ========================= +extern GuestErrorT guest_test_cli_finalize( void ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_TEST_CLI_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client/src/test/guest_test_client.c b/guest-client/guest-client-3.0.1/guest_client/src/test/guest_test_client.c new file mode 100755 index 00000000..9626cfec --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/test/guest_test_client.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include + +#include "guest_types.h" +#include "guest_debug.h" + +#include "guest_test_process.h" + +// **************************************************************************** +// Guest Test Client - Main +// ======================== +int main( int argc, char *argv[], char *envp[] ) +{ + GuestErrorT error; + + error = guest_debug_initialize("Guest-Client-Test"); + if (GUEST_OKAY != error) + { + printf("Debug initialization failed, error=%s.\n", + guest_error_str(error)); + return EXIT_FAILURE; + } + + error = guest_test_process_main(argc, argv, envp); + if (GUEST_OKAY != error) + { + printf("Process failure, error=%s.\n", guest_error_str(error)); + return EXIT_FAILURE; + } + + error = guest_debug_finalize(); + if (GUEST_OKAY != error) + { + printf("Debug finalization failed, error=%s.\n", + guest_error_str(error)); + } + + return EXIT_SUCCESS; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/test/guest_test_process.c b/guest-client/guest-client-3.0.1/guest_client/src/test/guest_test_process.c new file mode 100755 index 00000000..e80be3bd --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/test/guest_test_process.c @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_test_process.h" + +#include +#include +#include +#include + +#include "guest_limits.h" +#include "guest_types.h" +#include "guest_debug.h" +#include "guest_signal.h" +#include "guest_config.h" +#include "guest_selobj.h" +#include "guest_timer.h" +#include "guest_channel.h" +#include "guest_stream.h" +#include "guest_test.h" +#include "guest_test_cli.h" + +static sig_atomic_t _stay_on = 1; + +// **************************************************************************** +// Guest Test Process - Signal Handler +// =================================== +static void guest_test_process_signal_handler( int signum ) +{ + switch (signum) + { + case SIGINT: + case SIGTERM: + case SIGQUIT: + _stay_on = 0; + break; + + case SIGCONT: + DPRINTFD("Ignoring signal SIGCONT (%i).", signum); + break; + + case SIGPIPE: + DPRINTFD("Ignoring signal SIGPIPE (%i).", signum); + break; + + default: + DPRINTFD("Signal (%i) ignored.", signum); + break; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Test Process - Initialize +// =============================== +static GuestErrorT guest_test_process_initialize( + int argc, char *argv[], char *envp[] ) +{ + GuestConfigT* config = NULL; + GuestErrorT error; + + error = guest_config_initialize(argc, argv, envp); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to initialize configuration module, error=%s.", + guest_error_str(error)); + guest_config_show_usage(); + return GUEST_FAILED; + } + + error = guest_selobj_initialize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to initialize selection object module, error=%s.", + guest_error_str(error)); + return GUEST_FAILED; + } + + error = guest_timer_initialize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to initialize timer module, error=%s.", + guest_error_str(error)); + return GUEST_FAILED; + } + + error = guest_channel_initialize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to initialize channel module, error=%s.", + guest_error_str(error)); + return GUEST_FAILED; + } + + error = guest_stream_initialize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to initialize stream module, error=%s.", + guest_error_str(error)); + return GUEST_FAILED; + } + + config = guest_config_get(); + + error = guest_test_initialize(config->comm_device); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to initialize test module, error=%s.", + guest_error_str(error)); + return GUEST_FAILED; + } + + error = guest_test_cli_initialize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to initialize test cli module, error=%s.", + guest_error_str(error)); + return GUEST_FAILED; + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Test Process - Finalize +// ============================= +static GuestErrorT guest_test_process_finalize( void ) +{ + GuestErrorT error; + + error = guest_test_cli_finalize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to finalize test cli module, error=%s.", + guest_error_str(error)); + } + + error = guest_test_finalize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to finalize test module, error=%s.", + guest_error_str(error)); + } + + error = guest_stream_finalize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to finalize stream module, error=%s.", + guest_error_str(error)); + } + + error = guest_channel_finalize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to finalize channel module, error=%s.", + guest_error_str(error)); + } + + error = guest_timer_finalize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to finalize timer module, error=%s.", + guest_error_str(error)); + } + + error = guest_selobj_finalize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to finialize selection object module, error=%s.", + guest_error_str(error)); + } + + error = guest_config_finalize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed to finialize configuration module, error=%s.", + guest_error_str(error)); + } + + return GUEST_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Test Process - Main +// ========================= +GuestErrorT guest_test_process_main( int argc, char *argv[], char *envp[] ) +{ + unsigned int next_interval_in_ms; + GuestErrorT error; + + DPRINTFI("Starting."); + + guest_signal_register_handler(SIGINT, guest_test_process_signal_handler); + guest_signal_register_handler(SIGTERM, guest_test_process_signal_handler); + guest_signal_register_handler(SIGQUIT, guest_test_process_signal_handler); + guest_signal_register_handler(SIGCONT, guest_test_process_signal_handler); + guest_signal_register_handler(SIGPIPE, guest_test_process_signal_handler); + + error = guest_test_process_initialize(argc, argv, envp); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed initialize test process, error=%s.", + guest_error_str(error)); + return error; + } + + DPRINTFI("Started."); + + guest_test_cli_usage(); + + while (_stay_on) + { + next_interval_in_ms = guest_timer_schedule(); + + error = guest_selobj_dispatch(next_interval_in_ms); + if (GUEST_OKAY != error) + { + DPRINTFE("Selection object dispatch failed, error=%s.", + guest_error_str(error)); + break; + } + } + + DPRINTFI("Shutting down."); + + error = guest_test_process_finalize(); + if (GUEST_OKAY != error) + { + DPRINTFE("Failed finalize test process, error=%s.", + guest_error_str(error) ); + } + + DPRINTFI("Shutdown complete."); + + return GUEST_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client/src/test/guest_test_process.h b/guest-client/guest-client-3.0.1/guest_client/src/test/guest_test_process.h new file mode 100755 index 00000000..e582093c --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client/src/test/guest_test_process.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_TEST_PROCESS_H__ +#define __GUEST_TEST_PROCESS_H__ + +#include + +#include "guest_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// **************************************************************************** +// Guest Test Process - Main +// ==================== +extern GuestErrorT guest_test_process_main( int argc, char *argv[], char *envp[] ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_TEST_PROCESS_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client_api/Makefile b/guest-client/guest-client-3.0.1/guest_client_api/Makefile new file mode 100755 index 00000000..0e68d8a5 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client_api/Makefile @@ -0,0 +1,123 @@ +# +# Copyright(c) 2013-2016, Wind River Systems, Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Wind River Systems nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +SHELL = /bin/sh + +## Configuration Directory Variables +prefix := /usr/local +exec_prefix := $(prefix) +sysconfdir := $(prefix)/etc +includedir := $(prefix)/include +libdir := $(exec_prefix)/lib +bindir := $(exec_prefix)/bin + +MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) +CURRENT_DIR := $(patsubst %/,%,$(dir $(MAKEFILE_PATH))) +BUILD_DIR := $(CURRENT_DIR)/build +PACKAGE_DIR := $(BUILD_DIR)/package +PACKAGE_ROOT_DIR := $(PACKAGE_DIR)/rootdir + +.PHONY: all build create_build_dir sample common heartbeat clean distclean package + +C_INCLUDES = -I$(CURRENT_DIR) -I$(CURRENT_DIR)/../include + +common_NAME := guest_common_api +common_C_SRCS := guest_api_types.c guest_api_debug.c guest_api_stream.c +common_C_SRCS += guest_api_unix.c +common_C_OBJS := ${common_C_SRCS:.c=.o} +common_LDFLAGS := -fPIC -g -shared +common_LIB_NAME := lib$(common_NAME).so +common_LIB_VER_MJR_NAME := $(common_LIB_NAME).3 +common_LIB_VER_NAME := $(common_LIB_VER_MJR_NAME).0.1 +common_BUILD_OBJS := $(addprefix $(BUILD_DIR)/, $(common_C_OBJS)) + +heartbeat_NAME := guest_heartbeat_api +heartbeat_C_SRCS := guest_heartbeat_api.c +heartbeat_C_OBJS := ${heartbeat_C_SRCS:.c=.o} +heartbeat_LDFLAGS := -fPIC -g -shared +heartbeat_LIB_NAME := lib$(heartbeat_NAME).so +heartbeat_LIB_VER_MJR_NAME := $(heartbeat_LIB_NAME).3 +heartbeat_LIB_VER_NAME := $(heartbeat_LIB_VER_MJR_NAME).0.1 +heartbeat_BUILD_OBJS := $(addprefix $(BUILD_DIR)/, $(heartbeat_C_OBJS)) + +sample_NAME := sample-guest-app +sample_C_SRCS := sample_guest_app.c +sample_C_OBJS := ${sample_C_SRCS:.c=.o} +sample_LDLIBS := -L$(BUILD_DIR) -l$(common_NAME) -l$(heartbeat_NAME) -lrt +sample_BUILD_OBJS := $(addprefix $(BUILD_DIR)/, $(sample_C_OBJS)) + +CFLAGS = -g -O2 -Wall -Werror -Wformat -fPIC -DSYSCONFDIR=$(sysconfdir) + +all: build + +%.o: %.c + $(CC) $(CFLAGS) $(C_INCLUDES) -c $^ -o $(BUILD_DIR)/$@ + +common: $(common_C_OBJS) + $(CC) $(CFLAGS) $(common_BUILD_OBJS) -Wl,-soname,$(common_LIB_VER_MJR_NAME) \ + -o $(BUILD_DIR)/$(common_LIB_VER_NAME) $(common_LDFLAGS) + ln -sf $(common_LIB_VER_NAME) $(BUILD_DIR)/$(common_LIB_NAME) + ln -sf $(common_LIB_VER_NAME) $(BUILD_DIR)/$(common_LIB_VER_MJR_NAME) + +heartbeat: common $(heartbeat_C_OBJS) + $(CC) $(CFLAGS) $(heartbeat_BUILD_OBJS) -Wl,-soname,$(heartbeat_LIB_VER_MJR_NAME) \ + -o $(BUILD_DIR)/$(heartbeat_LIB_VER_NAME) $(heartbeat_LDFLAGS) + ln -sf $(heartbeat_LIB_VER_NAME) $(BUILD_DIR)/$(heartbeat_LIB_NAME) + ln -sf $(heartbeat_LIB_VER_NAME) $(BUILD_DIR)/$(heartbeat_LIB_VER_MJR_NAME) + +create_build_dir: + mkdir -p --mode 755 $(BUILD_DIR) + +build: create_build_dir common heartbeat + +sample: create_build_dir common heartbeat $(sample_C_OBJS) + $(CC) $(CFLAGS) $(sample_BUILD_OBJS) -o $(BUILD_DIR)/$(sample_NAME) $(sample_LDLIBS) + +clean: + @-($(RM) -Rf $(BUILD_DIR)/*) + +distclean: clean + +package: + @(mkdir -p --mode 755 $(PACKAGE_ROOT_DIR)/$(includedir)) + @(mkdir -p --mode 755 $(PACKAGE_ROOT_DIR)/$(libdir)) + @(echo "Packaging guest_api_types.h in $(PACKAGE_ROOT_DIR)/$(includedir)") + @(cp $(CURRENT_DIR)/guest_api_types.h $(PACKAGE_ROOT_DIR)/$(includedir)/guest_api_types.h) + @(echo "Packaging guest_heartbeat_api.h in $(PACKAGE_ROOT_DIR)/$(includedir)") + @(cp $(CURRENT_DIR)/guest_heartbeat_api.h $(PACKAGE_ROOT_DIR)/$(includedir)/guest_heartbeat_api.h) + @(echo "Packaging $(common_LIB_NAME) in $(PACKAGE_ROOT_DIR)/$(libdir)") + @(cp $(BUILD_DIR)/$(common_LIB_VER_NAME) $(PACKAGE_ROOT_DIR)/$(libdir)/$(common_LIB_VER_NAME)) + @(chmod 644 $(PACKAGE_ROOT_DIR)/$(libdir)/$(common_LIB_VER_NAME)) + ln -sf $(common_LIB_VER_NAME) $(PACKAGE_ROOT_DIR)/$(libdir)/$(common_LIB_NAME) + ln -sf $(common_LIB_VER_NAME) $(PACKAGE_ROOT_DIR)/$(libdir)/$(common_LIB_VER_MJR_NAME) + @(echo "Packaging $(heartbeat_LIB_NAME) in $(PACKAGE_ROOT_DIR)/$(libdir)") + @(cp $(BUILD_DIR)/$(heartbeat_LIB_VER_NAME) $(PACKAGE_ROOT_DIR)/$(libdir)/$(heartbeat_LIB_VER_NAME)) + @(chmod 644 $(PACKAGE_ROOT_DIR)/$(libdir)/$(heartbeat_LIB_VER_NAME)) + ln -sf $(heartbeat_LIB_VER_NAME) $(PACKAGE_ROOT_DIR)/$(libdir)/$(heartbeat_LIB_NAME) + ln -sf $(heartbeat_LIB_VER_NAME) $(PACKAGE_ROOT_DIR)/$(libdir)/$(heartbeat_LIB_VER_MJR_NAME) diff --git a/guest-client/guest-client-3.0.1/guest_client_api/guest_api_debug.c b/guest-client/guest-client-3.0.1/guest_client_api/guest_api_debug.c new file mode 100755 index 00000000..cadd2025 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client_api/guest_api_debug.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_api_debug.h" + +#include +#include +#include + +#include "guest_api_types.h" + +#define GUEST_DEBUG_WANT_SYSLOG +#ifdef GUEST_DEBUG_WANT_SYSLOG +#include +#endif + +static char _process_name[30]; +static GuestApiDebugLogLevelT _log_level = GUEST_API_DEBUG_LOG_LEVEL_INFO; + +// **************************************************************************** +// Guest API Debug - Log Level String +// ================================== +const char* guest_api_debug_log_level_str( GuestApiDebugLogLevelT level ) +{ + switch (level) { + case GUEST_API_DEBUG_LOG_LEVEL_ERROR: return "error"; + case GUEST_API_DEBUG_LOG_LEVEL_INFO: return " info"; + case GUEST_API_DEBUG_LOG_LEVEL_DEBUG: return "debug"; + case GUEST_API_DEBUG_LOG_LEVEL_VERBOSE: return " verb"; + default: + return "???"; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest API Debug - Set Log Level +// =============================== +void guest_api_debug_set_log_level( GuestApiDebugLogLevelT level ) +{ + _log_level = level; +} +// **************************************************************************** + +// **************************************************************************** +// Guest API Debug - Want Log +// ========================== +bool guest_api_debug_want_log( GuestApiDebugLogLevelT level ) +{ + return (level <= _log_level); +} +// **************************************************************************** + +// **************************************************************************** +// Guest API Debug - Log +// ===================== +void guest_api_debug_log( const char* format, ... ) +{ + char time_str[80]; + char date_str[32]; + struct tm t_real; + struct timespec ts_real; + va_list arguments; + char log_data[256]; + + va_start(arguments, format); + vsnprintf(log_data, sizeof(log_data), format, arguments); + va_end(arguments); + + clock_gettime(CLOCK_REALTIME, &ts_real); + + if (NULL == localtime_r(&(ts_real.tv_sec), &t_real)) + { + snprintf( time_str, sizeof(time_str), + "YYYY:MM:DD HH:MM:SS.xxx" ); + } else { + strftime( date_str, sizeof(date_str), "%b %e %H:%M:%S", + &t_real ); + snprintf( time_str, sizeof(time_str), "%s.%03ld", date_str, + ts_real.tv_nsec/1000000 ); + } + +#ifdef GUEST_DEBUG_WANT_SYSLOG + syslog(LOG_DEBUG, "%s", log_data); +#else + printf("%s %s: %s\n", time_str, _process_name, log_data); +#endif +} +// **************************************************************************** + +// **************************************************************************** +// Guest API Debug - Initialize +// ============================ +GuestApiErrorT guest_api_debug_initialize( char process_name[] ) +{ + _log_level = GUEST_API_DEBUG_LOG_LEVEL_INFO; + snprintf(_process_name, sizeof(_process_name), "%s", process_name); + + return GUEST_API_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest API Debug - Finalize +// ========================== +GuestApiErrorT guest_api_debug_finalize( void ) +{ + _log_level = GUEST_API_DEBUG_LOG_LEVEL_INFO; + _process_name[0] = '\0'; + + return GUEST_API_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client_api/guest_api_debug.h b/guest-client/guest-client-3.0.1/guest_client_api/guest_api_debug.h new file mode 100755 index 00000000..f276fbae --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client_api/guest_api_debug.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_API_DEBUG_H__ +#define __GUEST_API_DEBUG_H__ + +#include + +#include "guest_api_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + GUEST_API_DEBUG_LOG_LEVEL_ERROR, + GUEST_API_DEBUG_LOG_LEVEL_INFO, + GUEST_API_DEBUG_LOG_LEVEL_DEBUG, + GUEST_API_DEBUG_LOG_LEVEL_VERBOSE, +} GuestApiDebugLogLevelT; + +#define DPRINTF(level, format, args...) \ + if (guest_api_debug_want_log(level)) \ + guest_api_debug_log("%s: %s(%i): " format, \ + guest_api_debug_log_level_str(level), \ + __FILE__, __LINE__, ##args) +#define DPRINTFE(format, args...) \ + DPRINTF(GUEST_API_DEBUG_LOG_LEVEL_ERROR, format, ##args) +#define DPRINTFI(format, args...) \ + DPRINTF(GUEST_API_DEBUG_LOG_LEVEL_INFO, format, ##args) +#define DPRINTFD(format, args...) \ + DPRINTF(GUEST_API_DEBUG_LOG_LEVEL_DEBUG, format, ##args) +#define DPRINTFV(format, args... ) \ + DPRINTF(GUEST_API_DEBUG_LOG_LEVEL_VERBOSE, format, ##args) + +// **************************************************************************** +// Guest API Debug - Log Level String +// ================================== +extern const char* guest_api_debug_log_level_str( GuestApiDebugLogLevelT level ); +// **************************************************************************** + +// **************************************************************************** +// Guest API Debug - Set Log Level +// =============================== +extern void guest_api_debug_set_log_level( GuestApiDebugLogLevelT level ); +// **************************************************************************** + +// **************************************************************************** +// Guest API Debug - Want Log +// ========================== +extern bool guest_api_debug_want_log( GuestApiDebugLogLevelT level ); +// **************************************************************************** + +// **************************************************************************** +// Guest API Debug - Log +// ===================== +extern void guest_api_debug_log( const char* format, ... ); +// **************************************************************************** + +// **************************************************************************** +// Guest API Debug - Initialize +// ============================ +extern GuestApiErrorT guest_api_debug_initialize( char process_name[] ); +// **************************************************************************** + +// **************************************************************************** +// Guest API Debug - Finalize +// ========================== +extern GuestApiErrorT guest_api_debug_finalize( void ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_API_DEBUG_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client_api/guest_api_stream.c b/guest-client/guest-client-3.0.1/guest_client_api/guest_api_stream.c new file mode 100755 index 00000000..5f21a39e --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client_api/guest_api_stream.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_api_stream.h" + +#include +#include +#include + +#include "guest_api_types.h" +#include "guest_api_debug.h" + +// **************************************************************************** +// Guest API Stream - Get +// ====================== +int guest_api_stream_get( GuestApiStreamT* stream ) +{ + char* byte_ptr; + int delimiter_i = 0; + + if (stream->delimiter_size > stream->size) + return -1; + + for (byte_ptr = stream->bytes; byte_ptr != stream->end_ptr; ++byte_ptr) + { + if (stream->delimiter[delimiter_i] == *byte_ptr) + { + ++delimiter_i; + if (delimiter_i == stream->delimiter_size) + { + return (byte_ptr - stream->bytes); + } + } else { + delimiter_i = 0; + } + } + + return -1; +} +// **************************************************************************** + +// **************************************************************************** +// Guest API Stream - Get Next +// =========================== +bool guest_api_stream_get_next( GuestApiStreamT* stream ) +{ + char* byte_ptr; + int delimiter_i = 0; + + if (stream->delimiter_size > stream->size) + return false; + + for (byte_ptr = stream->bytes; byte_ptr != stream->end_ptr; ++byte_ptr) + { + --stream->size; + if (stream->delimiter[delimiter_i] == *byte_ptr) + { + ++delimiter_i; + if (delimiter_i == stream->delimiter_size) + { + byte_ptr -= (stream->delimiter_size-1); + stream->size += stream->delimiter_size; + memmove(stream->bytes, byte_ptr, stream->size); + stream->avail = stream->max_size - stream->size; + stream->end_ptr = stream->bytes + stream->size; + break; + } + } else { + delimiter_i = 0; + } + } + + if (byte_ptr == stream->end_ptr) + { + // Empty the stream + memset(stream->bytes, 0, stream->max_size); + stream->avail = stream->max_size; + stream->size = 0; + stream->end_ptr = stream->bytes; + return false; + } + + return true; +} +// **************************************************************************** + +// **************************************************************************** +// Guest API Stream - Advance +// ========================== +void guest_api_stream_advance( int adv, GuestApiStreamT* stream ) +{ + stream->size -= adv; + memmove(stream->bytes, stream->bytes+adv, stream->size); + stream->avail = stream->max_size - stream->size; + stream->end_ptr = stream->bytes + stream->size; +} +// **************************************************************************** + +// **************************************************************************** +// Guest API Stream - Reset +// ======================== +void guest_api_stream_reset( GuestApiStreamT* stream ) +{ + memset(stream->bytes, 0, stream->max_size); + stream->avail = stream->max_size; + stream->size = 0; + stream->end_ptr = stream->bytes; +} +// **************************************************************************** + +// **************************************************************************** +// Guest API Stream - Setup +// ======================== +GuestApiErrorT guest_api_stream_setup( + const char* delimiter, int delimiter_size, int stream_size, + GuestApiStreamT* stream ) +{ + stream->delimiter = malloc(delimiter_size); + if (NULL == stream->delimiter) + { + DPRINTFE("Failed to allocated delimiter storage, needed=%i.", + delimiter_size); + return GUEST_API_FAILED; + } + + stream->bytes = malloc(stream_size); + if (NULL == stream->delimiter) + { + DPRINTFE("Failed to allocated stream storage, needed=%i.", stream_size); + free(stream->delimiter); + return GUEST_API_FAILED; + } + + memcpy(stream->delimiter, delimiter, delimiter_size); + stream->delimiter_size = delimiter_size; + memset(stream->bytes, 0, stream_size); + stream->end_ptr = stream->bytes; + stream->avail = stream_size; + stream->size = 0; + stream->max_size = stream_size; + + return GUEST_API_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest API Stream - Release +// ========================== +GuestApiErrorT guest_api_stream_release( GuestApiStreamT* stream ) +{ + if (NULL != stream->delimiter) + free(stream->delimiter); + + if (NULL != stream->bytes) + free(stream->bytes); + + memset(stream, 0, sizeof(GuestApiStreamT)); + return GUEST_API_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client_api/guest_api_stream.h b/guest-client/guest-client-3.0.1/guest_client_api/guest_api_stream.h new file mode 100755 index 00000000..3a71ea06 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client_api/guest_api_stream.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_API_STREAM_H__ +#define __GUEST_API_STREAM_H__ + +#include + +#include "guest_api_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + char* delimiter; + int delimiter_size; + char* end_ptr; + char* bytes; + int avail; + int size; + int max_size; +} GuestApiStreamT; + +// **************************************************************************** +// Guest API Stream - Get +// ====================== +extern int guest_api_stream_get( GuestApiStreamT* stream ); +// **************************************************************************** + +// **************************************************************************** +// Guest API Stream - Get Next +// =========================== +extern bool guest_api_stream_get_next( GuestApiStreamT* stream ); +// **************************************************************************** + +// **************************************************************************** +// Guest API Stream - Advance +// ========================== +extern void guest_api_stream_advance( int adv, GuestApiStreamT* stream ); +// **************************************************************************** + +// **************************************************************************** +// Guest API Stream - Reset +// ======================== +extern void guest_api_stream_reset( GuestApiStreamT* stream ); +// **************************************************************************** + +// **************************************************************************** +// Guest API Stream - Setup +// ======================== +extern GuestApiErrorT guest_api_stream_setup( + const char* delimiter, int delimiter_size, int stream_size, + GuestApiStreamT* stream ); +// **************************************************************************** + +// **************************************************************************** +// Guest API Stream - Release +// ========================== +extern GuestApiErrorT guest_api_stream_release( GuestApiStreamT* stream ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_API_STREAM_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client_api/guest_api_types.c b/guest-client/guest-client-3.0.1/guest_client_api/guest_api_types.c new file mode 100755 index 00000000..4a0fe2e9 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client_api/guest_api_types.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_api_types.h" + +// **************************************************************************** +// Guest API - Error String +// ======================== +const char* guest_api_error_str( GuestApiErrorT error ) +{ + switch (error) + { + case GUEST_API_OKAY: return "OKAY"; + case GUEST_API_FAILED: return "FAILED"; + case GUEST_API_INTERRUPTED: return "INTERRUPTED"; + case GUEST_API_TRY_AGAIN: return "TRY_AGAIN"; + default: + return "???"; + } +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client_api/guest_api_types.h b/guest-client/guest-client-3.0.1/guest_client_api/guest_api_types.h new file mode 100755 index 00000000..486c943a --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client_api/guest_api_types.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_API_TYPES_H__ +#define __GUEST_API_TYPES_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + GUEST_API_OKAY, + GUEST_API_FAILED, + GUEST_API_INTERRUPTED, + GUEST_API_TRY_AGAIN, +} GuestApiErrorT; + +// **************************************************************************** +// Guest API - Error String +// ======================== +extern const char* guest_api_error_str( GuestApiErrorT error ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_API_TYPES_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client_api/guest_api_unix.c b/guest-client/guest-client-3.0.1/guest_client_api/guest_api_unix.c new file mode 100755 index 00000000..c6035131 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client_api/guest_api_unix.c @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_api_unix.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "guest_api_types.h" +#include "guest_api_debug.h" + +// **************************************************************************** +// Guest API Unix - Connect +// ======================== +GuestApiErrorT guest_api_unix_connect( int s, char* address ) +{ + struct sockaddr_un remote; + int len, result; + + memset(&remote, 0, sizeof(remote)); + + remote.sun_family = AF_UNIX; + len = sizeof(remote.sun_family); + len += snprintf(remote.sun_path, sizeof(remote.sun_path), "%s", address); + + result = connect(s, (struct sockaddr*) &remote, sizeof(remote)); + if (0 > result) + { + if ((ENOENT == errno) || (ECONNREFUSED == errno)) + { + DPRINTFD("Failed to connect to %s, error=%s.", address, + strerror(errno)); + return GUEST_API_TRY_AGAIN; + } else { + DPRINTFE("Failed to connect to %s, error=%s.", address, + strerror(errno)); + return GUEST_API_FAILED; + } + } + + return GUEST_API_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest API Unix - Send +// ===================== +GuestApiErrorT guest_api_unix_send( int s, void* msg, int msg_size ) +{ + int result; + + result = write(s, msg, msg_size); + if (0 > result) + { + if (errno == EPIPE) + { + DPRINTFI("Failed to write to socket, error=%s.", strerror(errno)); + return GUEST_API_TRY_AGAIN; + } else { + DPRINTFE("Failed to write to socket, error=%s.", strerror(errno)); + return GUEST_API_FAILED; + } + } + return GUEST_API_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest API Unix - Receive +// ======================== +GuestApiErrorT guest_api_unix_receive( + int s, void* msg_buf, int msg_buf_size, int* msg_size ) +{ + int result; + + result = read(s, msg_buf, msg_buf_size); + if (0 > result) + { + if (EINTR == errno) + { + DPRINTFD("Interrupted on socket read, error=%s.", strerror(errno)); + return GUEST_API_INTERRUPTED; + } else if (ECONNRESET == errno) { + DPRINTFD("Peer connection reset, error=%s.", strerror(errno)); + *msg_size = 0; + return GUEST_API_OKAY; + } else{ + DPRINTFE("Failed to read from socket, error=%s.", strerror(errno)); + return GUEST_API_FAILED; + } + } else if (0 == result) { + DPRINTFD("No message received from socket."); + *msg_size = 0; + return GUEST_API_OKAY; + } else { + DPRINTFV("Received message, msg_size=%i.", result); + *msg_size = result; + } + + return GUEST_API_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest API Unix - Open +// ===================== +GuestApiErrorT guest_api_unix_open( int* s ) +{ + int sock; + int reuse_addr = 1; + struct sockaddr_un local; + int result; + + *s = -1; + memset(&local, 0, sizeof(local)); + + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (0 > sock) + { + DPRINTFE("Failed to open socket, error=%s.", strerror(errno)); + return GUEST_API_FAILED; + } + + result = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, + sizeof(reuse_addr)); + if (0 > result) + { + DPRINTFE("Failed to set socket option (REUSEADDR), error=%s.", + strerror(errno)); + close(sock); + return GUEST_API_FAILED; + } + + result = fcntl(sock, F_SETFD, FD_CLOEXEC); + if (0 > result) + { + DPRINTFE("Failed to set to close on exec, error=%s.", strerror(errno)); + close(sock); + return GUEST_API_FAILED; + } + + result = fcntl(sock, F_GETFL); + if (0 > result) + { + DPRINTFE("Failed to get socket options, error=%s.", strerror(errno)); + close(sock); + return GUEST_API_FAILED; + } + + result = fcntl(sock, F_SETFL, result | O_NONBLOCK); + if (0 > result) + { + DPRINTFE("Failed to set socket options, error=%s.", strerror(errno)); + close(sock); + return GUEST_API_FAILED; + } + + *s = sock; + + return GUEST_API_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest API Unix - Close +// ====================== +GuestApiErrorT guest_api_unix_close( int s ) +{ + if (0 <= s) + close(s); + + return GUEST_API_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client_api/guest_api_unix.h b/guest-client/guest-client-3.0.1/guest_client_api/guest_api_unix.h new file mode 100755 index 00000000..6e0b71c5 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client_api/guest_api_unix.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_API_UNIX_H__ +#define __GUEST_API_UNIX_H__ + +#include "guest_api_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// **************************************************************************** +// Guest API Unix - Connect +// ======================== +extern GuestApiErrorT guest_api_unix_connect( int s, char* address ); +// **************************************************************************** + +// **************************************************************************** +// Guest API Unix - Send +// ===================== +extern GuestApiErrorT guest_api_unix_send( int s, void* msg, int msg_size ); +// **************************************************************************** + +// **************************************************************************** +// Guest API Unix - Receive +// ======================== +extern GuestApiErrorT guest_api_unix_receive( + int s, void* msg_buf, int msg_buf_size, int* msg_size ); +// **************************************************************************** + +// **************************************************************************** +// Guest API Unix - Open +// ===================== +extern GuestApiErrorT guest_api_unix_open( int* s ); +// **************************************************************************** + +// **************************************************************************** +// Guest API Unix - Close +// ====================== +extern GuestApiErrorT guest_api_unix_close( int s ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_API_UNIX_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client_api/guest_heartbeat_api.c b/guest-client/guest-client-3.0.1/guest_client_api/guest_heartbeat_api.c new file mode 100755 index 00000000..99e7d31a --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client_api/guest_heartbeat_api.c @@ -0,0 +1,748 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "guest_heartbeat_api.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "guest_api_types.h" +#include "guest_api_debug.h" +#include "guest_api_unix.h" +#include "guest_api_stream.h" + +#include "guest_heartbeat_api_msg_defs.h" + +static int _sock = -1; +static bool _connected = false; +static uint32_t _msg_sequence; +static GuestApiStreamT _stream; +static GuestHeartbeatApiCallbacksT _callbacks; + +// **************************************************************************** +// Guest Heartbeat API - Action String +// =================================== +const char* guest_heartbeat_api_action_str( GuestHeartbeatApiActionT action ) +{ + switch (action) + { + case GUEST_HEARTBEAT_API_ACTION_NONE: return "none"; + case GUEST_HEARTBEAT_API_ACTION_REBOOT: return "reboot"; + case GUEST_HEARTBEAT_API_ACTION_STOP: return "stop"; + case GUEST_HEARTBEAT_API_ACTION_LOG: return "log"; + default: + return "action-???"; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Event String +// ================================== +const char* guest_heartbeat_api_event_str( GuestHeartbeatApiEventT event ) +{ + switch (event) + { + case GUEST_HEARTBEAT_API_EVENT_STOP: return "stop"; + case GUEST_HEARTBEAT_API_EVENT_REBOOT: return "reboot"; + case GUEST_HEARTBEAT_API_EVENT_SUSPEND: return "suspend"; + case GUEST_HEARTBEAT_API_EVENT_PAUSE: return "pause"; + case GUEST_HEARTBEAT_API_EVENT_UNPAUSE: return "unpause"; + case GUEST_HEARTBEAT_API_EVENT_RESUME: return "resume"; + case GUEST_HEARTBEAT_API_EVENT_RESIZE_BEGIN: return "resize-begin"; + case GUEST_HEARTBEAT_API_EVENT_RESIZE_END: return "resize-end"; + case GUEST_HEARTBEAT_API_EVENT_LIVE_MIGRATE_BEGIN: return "live-migrate-begin"; + case GUEST_HEARTBEAT_API_EVENT_LIVE_MIGRATE_END: return "live-migrate-end"; + case GUEST_HEARTBEAT_API_EVENT_COLD_MIGRATE_BEGIN: return "cold-migrate-begin"; + case GUEST_HEARTBEAT_API_EVENT_COLD_MIGRATE_END: return "cold-migrate-end"; + default: + return "event-???"; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Notify String +// =================================== +const char* guest_heartbeat_api_notify_str( + GuestHeartbeatApiNotifyTypeT notify ) +{ + switch (notify) + { + case GUEST_HEARTBEAT_API_NOTIFY_TYPE_REVOCABLE: return "revocable"; + case GUEST_HEARTBEAT_API_NOTIFY_TYPE_IRREVOCABLE: return "irrevocable"; + default: + return "notify-???"; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Vote Result String +// ======================================== +const char* guest_heartbeat_api_vote_result_str( + GuestHeartbeatApiVoteResultT vote_result ) +{ + switch (vote_result) + { + case GUEST_HEARTBEAT_API_VOTE_RESULT_ACCEPT: return "accept"; + case GUEST_HEARTBEAT_API_VOTE_RESULT_REJECT: return "reject"; + case GUEST_HEARTBEAT_API_VOTE_RESULT_COMPLETE: return "complete"; + default: + return "vote-???"; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Action (Host to Network) +// ============================================== +static GuestHeartbeatApiMsgActionT guest_heartbeat_api_action_hton( + GuestHeartbeatApiActionT action ) +{ + switch (action) + { + case GUEST_HEARTBEAT_API_ACTION_NONE: + return GUEST_HEARTBEAT_API_MSG_ACTION_NONE; + case GUEST_HEARTBEAT_API_ACTION_REBOOT: + return GUEST_HEARTBEAT_API_MSG_ACTION_REBOOT; + case GUEST_HEARTBEAT_API_ACTION_STOP: + return GUEST_HEARTBEAT_API_MSG_ACTION_STOP; + case GUEST_HEARTBEAT_API_ACTION_LOG: + return GUEST_HEARTBEAT_API_MSG_ACTION_LOG; + default: + DPRINTFE("Unknown action %i.", action); + return GUEST_HEARTBEAT_API_MSG_ACTION_UNKNOWN; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Event (Host to Network) +// ============================================= +static GuestHeartbeatApiMsgEventT guest_heartbeat_api_event_hton( + GuestHeartbeatApiEventT event ) +{ + switch (event) + { + case GUEST_HEARTBEAT_API_EVENT_STOP: + return GUEST_HEARTBEAT_API_MSG_EVENT_STOP; + case GUEST_HEARTBEAT_API_EVENT_REBOOT: + return GUEST_HEARTBEAT_API_MSG_EVENT_REBOOT; + case GUEST_HEARTBEAT_API_EVENT_SUSPEND: + return GUEST_HEARTBEAT_API_MSG_EVENT_SUSPEND; + case GUEST_HEARTBEAT_API_EVENT_PAUSE: + return GUEST_HEARTBEAT_API_MSG_EVENT_PAUSE; + case GUEST_HEARTBEAT_API_EVENT_UNPAUSE: + return GUEST_HEARTBEAT_API_MSG_EVENT_UNPAUSE; + case GUEST_HEARTBEAT_API_EVENT_RESUME: + return GUEST_HEARTBEAT_API_MSG_EVENT_RESUME; + case GUEST_HEARTBEAT_API_EVENT_RESIZE_BEGIN: + return GUEST_HEARTBEAT_API_MSG_EVENT_RESIZE_BEGIN; + case GUEST_HEARTBEAT_API_EVENT_RESIZE_END: + return GUEST_HEARTBEAT_API_MSG_EVENT_RESIZE_END; + case GUEST_HEARTBEAT_API_EVENT_LIVE_MIGRATE_BEGIN: + return GUEST_HEARTBEAT_API_MSG_EVENT_LIVE_MIGRATE_BEGIN; + case GUEST_HEARTBEAT_API_EVENT_LIVE_MIGRATE_END: + return GUEST_HEARTBEAT_API_MSG_EVENT_LIVE_MIGRATE_END; + case GUEST_HEARTBEAT_API_EVENT_COLD_MIGRATE_BEGIN: + return GUEST_HEARTBEAT_API_MSG_EVENT_COLD_MIGRATE_BEGIN; + case GUEST_HEARTBEAT_API_EVENT_COLD_MIGRATE_END: + return GUEST_HEARTBEAT_API_MSG_EVENT_COLD_MIGRATE_END; + default: + DPRINTFE("Unknown event %i.", event); + return GUEST_HEARTBEAT_API_MSG_EVENT_UNKNOWN; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Event (Network to Host) +// ============================================= +static GuestHeartbeatApiEventT guest_heartbeat_api_event_ntoh( + GuestHeartbeatApiMsgEventT event ) +{ + switch (event) + { + case GUEST_HEARTBEAT_API_MSG_EVENT_STOP: + return GUEST_HEARTBEAT_API_EVENT_STOP; + case GUEST_HEARTBEAT_API_MSG_EVENT_REBOOT: + return GUEST_HEARTBEAT_API_EVENT_REBOOT; + case GUEST_HEARTBEAT_API_MSG_EVENT_SUSPEND: + return GUEST_HEARTBEAT_API_EVENT_SUSPEND; + case GUEST_HEARTBEAT_API_MSG_EVENT_PAUSE: + return GUEST_HEARTBEAT_API_EVENT_PAUSE; + case GUEST_HEARTBEAT_API_MSG_EVENT_UNPAUSE: + return GUEST_HEARTBEAT_API_EVENT_UNPAUSE; + case GUEST_HEARTBEAT_API_MSG_EVENT_RESUME: + return GUEST_HEARTBEAT_API_EVENT_RESUME; + case GUEST_HEARTBEAT_API_MSG_EVENT_RESIZE_BEGIN: + return GUEST_HEARTBEAT_API_EVENT_RESIZE_BEGIN; + case GUEST_HEARTBEAT_API_MSG_EVENT_RESIZE_END: + return GUEST_HEARTBEAT_API_EVENT_RESIZE_END; + case GUEST_HEARTBEAT_API_MSG_EVENT_LIVE_MIGRATE_BEGIN: + return GUEST_HEARTBEAT_API_EVENT_LIVE_MIGRATE_BEGIN; + case GUEST_HEARTBEAT_API_MSG_EVENT_LIVE_MIGRATE_END: + return GUEST_HEARTBEAT_API_EVENT_LIVE_MIGRATE_END; + case GUEST_HEARTBEAT_API_MSG_EVENT_COLD_MIGRATE_BEGIN: + return GUEST_HEARTBEAT_API_EVENT_COLD_MIGRATE_BEGIN; + case GUEST_HEARTBEAT_API_MSG_EVENT_COLD_MIGRATE_END: + return GUEST_HEARTBEAT_API_EVENT_COLD_MIGRATE_END; + default: + DPRINTFE("Unknown event %i.", event); + return GUEST_HEARTBEAT_API_EVENT_UNKNOWN; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Notify (Host to Network) +// ============================================== +static GuestHeartbeatApiMsgNotifyT guest_heartbeat_api_notify_hton( + GuestHeartbeatApiNotifyTypeT notify ) +{ + switch (notify) + { + case GUEST_HEARTBEAT_API_NOTIFY_TYPE_REVOCABLE: + return GUEST_HEARTBEAT_API_MSG_NOTIFY_REVOCABLE; + case GUEST_HEARTBEAT_API_NOTIFY_TYPE_IRREVOCABLE: + return GUEST_HEARTBEAT_API_MSG_NOTIFY_IRREVOCABLE; + default: + DPRINTFE("Unknown notify type %i.", notify); + return GUEST_HEARTBEAT_API_MSG_NOTIFY_UNKNOWN; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Notify (Network to Host) +// ============================================== +static GuestHeartbeatApiNotifyTypeT guest_heartbeat_api_notify_ntoh( + GuestHeartbeatApiMsgNotifyT notify ) +{ + switch (notify) + { + case GUEST_HEARTBEAT_API_MSG_NOTIFY_REVOCABLE: + return GUEST_HEARTBEAT_API_NOTIFY_TYPE_REVOCABLE; + case GUEST_HEARTBEAT_API_MSG_NOTIFY_IRREVOCABLE: + return GUEST_HEARTBEAT_API_NOTIFY_TYPE_IRREVOCABLE; + default: + DPRINTFE("Unknown notify type %i.", notify); + return GUEST_HEARTBEAT_API_NOTIFY_TYPE_UNKNOWN; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Vote Result (Host to Network) +// =================================================== +static GuestHeartbeatApiMsgVoteResultT guest_heartbeat_api_vote_result_hton( + GuestHeartbeatApiVoteResultT vote_result ) +{ + switch (vote_result) + { + case GUEST_HEARTBEAT_API_VOTE_RESULT_ACCEPT: + return GUEST_HEARTBEAT_API_MSG_VOTE_RESULT_ACCEPT; + case GUEST_HEARTBEAT_API_VOTE_RESULT_REJECT: + return GUEST_HEARTBEAT_API_MSG_VOTE_RESULT_REJECT; + case GUEST_HEARTBEAT_API_VOTE_RESULT_COMPLETE: + return GUEST_HEARTBEAT_API_MSG_VOTE_RESULT_COMPLETE; + default: + DPRINTFE("Unknown vote result %i.", vote_result); + return GUEST_HEARTBEAT_API_MSG_VOTE_RESULT_UNKNOWN; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Register +// ============================== +GuestApiErrorT guest_heartbeat_api_register( + GuestHeartbeatApiInitDataT* init_data ) +{ + GuestHeartbeatApiMsgT msg; + GuestHeartbeatApiMsgHeaderT* hdr = &(msg.header); + GuestHeartbeatApiMsgInitT* bdy = &(msg.body.init); + GuestApiErrorT error; + + if (0 > _sock) + { + error = guest_api_unix_open(&_sock); + if (GUEST_API_OKAY != error) + { + DPRINTFE("Failed to open unix socket, error=%s.", + guest_api_error_str(error)); + return error; + } + } + + if (!_connected) + { + error = guest_api_unix_connect(_sock, GUEST_HEARTBEAT_API_MSG_ADDRESS); + if (GUEST_API_OKAY != error) + { + if (GUEST_API_TRY_AGAIN != error) + { + DPRINTFD("Failed to connect unix socket, error=%s.", + guest_api_error_str(error)); + return error; + } else { + DPRINTFE("Failed to connect unix socket, error=%s.", + guest_api_error_str(error)); + return error; + } + } + + _connected = true; + } + + memset(&msg, 0, sizeof(msg)); + + memcpy(&(hdr->magic), GUEST_HEARTBEAT_API_MSG_MAGIC_VALUE, + GUEST_HEARTBEAT_API_MSG_MAGIC_SIZE); + hdr->version = GUEST_HEARTBEAT_API_MSG_VERSION_CURRENT; + hdr->revision = GUEST_HEARTBEAT_API_MSG_REVISION_CURRENT; + hdr->msg_type = GUEST_HEARTBEAT_API_MSG_INIT; + hdr->sequence = ++_msg_sequence; + hdr->size = sizeof(msg); + + snprintf(bdy->application_name, + GUEST_HEARTBEAT_API_MSG_MAX_APPLICATION_NAME_SIZE, "%s", + init_data->application_name); + bdy->heartbeat_interval_ms = init_data->heartbeat_interval_ms; + bdy->vote_secs = init_data->vote_secs; + bdy->shutdown_notice_secs = init_data->shutdown_notice_secs; + bdy->suspend_notice_secs = init_data->suspend_notice_secs; + bdy->resume_notice_secs = init_data->resume_notice_secs; + bdy->corrective_action + = guest_heartbeat_api_action_hton(init_data->corrective_action); + + error = guest_api_unix_send(_sock, &msg, sizeof(msg)); + if (GUEST_API_OKAY != error) + { + DPRINTFE("Failed to send guest heartbeat register message, error=%s.", + guest_api_error_str(error)); + return error; + } + + DPRINTFD("Sent register request."); + return GUEST_API_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Deregister +// ================================ +GuestApiErrorT guest_heartbeat_api_deregister( char log_msg[] ) +{ + GuestHeartbeatApiMsgT msg; + GuestHeartbeatApiMsgHeaderT* hdr = &(msg.header); + GuestHeartbeatApiMsgFinalT* bdy = &(msg.body.final); + GuestApiErrorT error; + + if (!_connected) + { + DPRINTFD("Not connected."); + return GUEST_API_OKAY; + } + + memset(&msg, 0, sizeof(msg)); + + memcpy(&(hdr->magic), GUEST_HEARTBEAT_API_MSG_MAGIC_VALUE, + GUEST_HEARTBEAT_API_MSG_MAGIC_SIZE); + hdr->version = GUEST_HEARTBEAT_API_MSG_VERSION_CURRENT; + hdr->revision = GUEST_HEARTBEAT_API_MSG_REVISION_CURRENT; + hdr->msg_type = GUEST_HEARTBEAT_API_MSG_FINAL; + hdr->sequence = ++_msg_sequence; + hdr->size = sizeof(msg); + + snprintf(bdy->log_msg, GUEST_HEARTBEAT_API_MSG_MAX_LOG_SIZE, "%s", log_msg); + + error = guest_api_unix_send(_sock, &msg, sizeof(msg)); + if (GUEST_API_OKAY != error) + { + DPRINTFE("Failed to send guest heartbeat api deregister message, " + "error=%s.", guest_api_error_str(error)); + return error; + } + + return GUEST_API_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Send Action Response +// ========================================== +GuestApiErrorT guest_heartbeat_api_send_action_response( + int invocation_id, GuestHeartbeatApiEventT event, + GuestHeartbeatApiNotifyTypeT notify_type, + GuestHeartbeatApiVoteResultT vote_result, char log_msg[] ) +{ + GuestHeartbeatApiMsgT msg; + GuestHeartbeatApiMsgHeaderT* hdr = &(msg.header); + GuestHeartbeatApiMsgActionResponseT* bdy = &(msg.body.action_response); + GuestApiErrorT error; + + if (!_connected) + { + DPRINTFD("Not connected."); + return GUEST_API_OKAY; + } + + memset(&msg, 0, sizeof(msg)); + + memcpy(&(hdr->magic), GUEST_HEARTBEAT_API_MSG_MAGIC_VALUE, + GUEST_HEARTBEAT_API_MSG_MAGIC_SIZE); + hdr->version = GUEST_HEARTBEAT_API_MSG_VERSION_CURRENT; + hdr->revision = GUEST_HEARTBEAT_API_MSG_REVISION_CURRENT; + hdr->msg_type = GUEST_HEARTBEAT_API_MSG_ACTION_RESPONSE; + hdr->sequence = ++_msg_sequence; + hdr->size = sizeof(msg); + + bdy->invocation_id = invocation_id; + bdy->event_type = guest_heartbeat_api_event_hton(event); + bdy->notification_type = guest_heartbeat_api_notify_hton(notify_type); + bdy->vote_result = guest_heartbeat_api_vote_result_hton(vote_result); + snprintf(bdy->log_msg, GUEST_HEARTBEAT_API_MSG_MAX_LOG_SIZE, "%s", log_msg); + + error = guest_api_unix_send(_sock, &msg, sizeof(msg)); + if (GUEST_API_OKAY != error) + { + DPRINTFE("Failed to send guest heartbeat api action response message, " + "error=%s.", guest_api_error_str(error)); + return error; + } + + return GUEST_API_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Api - Receive Init Ack +// ====================================== +static void guest_heartbeat_api_recv_init_ack( void ) +{ + uint32_t accepted; + char* ptr = _stream.bytes + sizeof(GuestHeartbeatApiMsgHeaderT); + + accepted = *(uint32_t*) ptr; + ptr += sizeof(uint32_t); + + DPRINTFI("Registration %s.", accepted ? "accepted" : "not accepted"); + + if (NULL != _callbacks.register_state) + _callbacks.register_state(accepted); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Api - Receive Challenge +// ======================================= +static void guest_heartbeat_api_recv_challenge( void ) +{ + bool health = true; + GuestHeartbeatApiActionT corrective_action = GUEST_HEARTBEAT_API_ACTION_NONE; + char log_msg[GUEST_HEARTBEAT_API_LOG_MAX] = ""; + int heartbeat_challenge; + GuestHeartbeatApiMsgT msg; + GuestHeartbeatApiMsgHeaderT* hdr = &(msg.header); + GuestHeartbeatApiMsgChallengeResponseT* bdy = &(msg.body.challenge_response); + GuestApiErrorT error; + char* ptr = _stream.bytes + sizeof(GuestHeartbeatApiMsgHeaderT); + + heartbeat_challenge = *(uint32_t*) ptr; + ptr += sizeof(uint32_t); + + if (NULL != _callbacks.health_check) + _callbacks.health_check(&health, &corrective_action, log_msg); + + memset(&msg, 0, sizeof(msg)); + + memcpy(&(hdr->magic), GUEST_HEARTBEAT_API_MSG_MAGIC_VALUE, + GUEST_HEARTBEAT_API_MSG_MAGIC_SIZE); + hdr->version = GUEST_HEARTBEAT_API_MSG_VERSION_CURRENT; + hdr->revision = GUEST_HEARTBEAT_API_MSG_REVISION_CURRENT; + hdr->msg_type = GUEST_HEARTBEAT_API_MSG_CHALLENGE_RESPONSE; + hdr->sequence = ++_msg_sequence; + hdr->size = sizeof(msg); + + bdy->heartbeat_response = heartbeat_challenge; + bdy->health = health; + bdy->corrective_action + = guest_heartbeat_api_action_hton(corrective_action); + snprintf(bdy->log_msg, GUEST_HEARTBEAT_API_MSG_MAX_LOG_SIZE, "%s", log_msg); + + error = guest_api_unix_send(_sock, &msg, sizeof(msg)); + if (GUEST_API_OKAY != error) + { + DPRINTFE("Failed to send guest heartbeat api challenge response " + "message, error=%s.", guest_api_error_str(error)); + return; + } + + DPRINTFD("Sent guest heartbeat api challenge response sent."); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat Api - Receive Action Notify +// =========================================== +static void guest_heartbeat_api_recv_action_notify( void ) +{ + int invocation_id; + GuestHeartbeatApiEventT event; + GuestHeartbeatApiNotifyTypeT notify_type; + char* ptr = _stream.bytes + sizeof(GuestHeartbeatApiMsgHeaderT); + + invocation_id = *(uint32_t*) ptr; + ptr += sizeof(uint32_t); + event = guest_heartbeat_api_event_ntoh(*(uint32_t*) ptr); + ptr += sizeof(uint32_t); + notify_type = guest_heartbeat_api_notify_ntoh(*(uint32_t*) ptr); + ptr += sizeof(uint32_t); + + if (NULL != _callbacks.action_notify) + _callbacks.action_notify(invocation_id, event, notify_type); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Dispatch +// ============================== +void guest_heartbeat_api_dispatch( int selobj ) +{ + static bool have_start = false; + static bool have_header = false; + static GuestHeartbeatApiMsgHeaderT hdr; + + bool more; + int bytes_received; + GuestApiErrorT error; + + if (selobj != _sock) + return; + + error = guest_api_unix_receive(_sock, _stream.end_ptr, _stream.avail, + &bytes_received); + if (GUEST_API_OKAY != error) + { + DPRINTFE("Failed to receive message, error=%s.", + guest_api_error_str(error)); + return; + } + + if (0 == bytes_received) + { + DPRINTFI("Registration dropped."); + _connected = false; + + error = guest_api_unix_close(_sock); + if (GUEST_API_OKAY != error) + { + DPRINTFE("Failed to close unix socket, error=%s.", + guest_api_error_str(error)); + } + _sock = -1; + + if (NULL != _callbacks.register_state) + _callbacks.register_state(false); + } + + DPRINTFV("Bytes received is %i.", bytes_received); + + _stream.end_ptr += bytes_received; + _stream.avail -= bytes_received; + _stream.size += bytes_received; + + do + { + more = false; + + if (!have_start) + { + memset(&hdr, 0, sizeof(GuestHeartbeatApiMsgHeaderT)); + have_start = guest_api_stream_get_next(&_stream); + } + + if (have_start && !have_header) + { + if (sizeof(GuestHeartbeatApiMsgHeaderT) <= _stream.size) + { + char *ptr = _stream.bytes + GUEST_HEARTBEAT_API_MSG_MAGIC_SIZE; + + hdr.version = *(uint8_t*) ptr; + ptr += sizeof(uint8_t); + hdr.revision = *(uint8_t*) ptr; + ptr += sizeof(uint8_t); + hdr.msg_type = *(uint16_t*) ptr; + ptr += sizeof(uint16_t); + hdr.sequence = *(uint32_t*) ptr; + ptr += sizeof(uint32_t); + hdr.size = *(uint32_t*) ptr; + ptr += sizeof(uint32_t); + + DPRINTFD("Message header: version=%i, revision=%i, " + "msg_type=%i, sequence=%u, size=%u", hdr.version, + hdr.revision, hdr.msg_type, hdr.sequence, hdr.size); + + if (GUEST_HEARTBEAT_API_MSG_VERSION_CURRENT == hdr.version) + { + have_header = true; + } else { + have_start = false; + have_header = false; + guest_api_stream_advance(GUEST_HEARTBEAT_API_MSG_MAGIC_SIZE, + &_stream); + more = true; + } + } + } + + if (have_start && have_header) + { + if (sizeof(GuestHeartbeatApiMsgT) <= _stream.size) + { + switch (hdr.msg_type) + { + case GUEST_HEARTBEAT_API_MSG_INIT_ACK: + guest_heartbeat_api_recv_init_ack(); + break; + + case GUEST_HEARTBEAT_API_MSG_CHALLENGE: + guest_heartbeat_api_recv_challenge(); + break; + + case GUEST_HEARTBEAT_API_MSG_ACTION_NOTIFY: + guest_heartbeat_api_recv_action_notify(); + break; + + default: + DPRINTFV("Unknown message type %i.", + (int) hdr.msg_type); + break; + } + + have_start = false; + have_header = false; + guest_api_stream_advance(sizeof(GuestHeartbeatApiMsgT), + &_stream); + more = true; + } + } + } while (more); + + if (0 >= _stream.avail) + guest_api_stream_reset(&_stream); +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Get Selection Object +// ========================================== +int guest_heartbeat_api_get_selobj( void ) +{ + return _sock; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Initialize +// ================================ +GuestApiErrorT guest_heartbeat_api_initialize( + GuestHeartbeatApiCallbacksT* callbacks ) +{ + int stream_size; + GuestApiErrorT error; + + if ((NULL == callbacks->register_state) || + (NULL == callbacks->health_check) || + (NULL == callbacks->action_notify)) + { + DPRINTFE("Not all callbacks are valid."); + return GUEST_API_FAILED; + } + + _sock = -1; + + stream_size = sizeof(GuestHeartbeatApiMsgT)*4; + if (8192 > stream_size) + stream_size = 8192; + + error = guest_api_stream_setup(GUEST_HEARTBEAT_API_MSG_MAGIC_VALUE, + GUEST_HEARTBEAT_API_MSG_MAGIC_SIZE, + stream_size, &_stream); + if (GUEST_API_OKAY != error) + { + DPRINTFE("Failed to setup stream, error=%s.", + guest_api_error_str(error)); + return error; + } + + memcpy(&_callbacks, callbacks, sizeof(_callbacks)); + return GUEST_API_OKAY; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Finalize +// ============================== +GuestApiErrorT guest_heartbeat_api_finalize( void ) +{ + GuestApiErrorT error; + + memset(&_callbacks, 0, sizeof(_callbacks)); + + error = guest_api_stream_release(&_stream); + if (GUEST_API_OKAY != error) + { + DPRINTFE("Failed release stream, error=%s.", + guest_api_error_str(error)); + } + + if (0 <= _sock) + { + error = guest_api_unix_close(_sock); + if (GUEST_API_OKAY != error) + { + DPRINTFE("Failed to close unix socket, error=%s.", + guest_api_error_str(error)); + } + _sock = -1; + } + + return GUEST_API_OKAY; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/guest_client_api/guest_heartbeat_api.h b/guest-client/guest-client-3.0.1/guest_client_api/guest_heartbeat_api.h new file mode 100755 index 00000000..5f03365b --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client_api/guest_heartbeat_api.h @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_HERATBEAT_API_H__ +#define __GUEST_HEARTBEAT_API_H__ + +#include + +#include "guest_api_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define GUEST_HEARTBEAT_API_APPLICATION_NAME_MAX 40 +#define GUEST_HEARTBEAT_API_LOG_MAX 192 + +typedef enum { + + GUEST_HEARTBEAT_API_ACTION_UNKNOWN, + GUEST_HEARTBEAT_API_ACTION_NONE, + GUEST_HEARTBEAT_API_ACTION_REBOOT, + GUEST_HEARTBEAT_API_ACTION_STOP, + GUEST_HEARTBEAT_API_ACTION_LOG, + GUEST_HEARTBEAT_API_ACTION_MAX, +} GuestHeartbeatApiActionT; + +typedef enum { + GUEST_HEARTBEAT_API_EVENT_UNKNOWN, + GUEST_HEARTBEAT_API_EVENT_STOP, + GUEST_HEARTBEAT_API_EVENT_REBOOT, + GUEST_HEARTBEAT_API_EVENT_SUSPEND, + GUEST_HEARTBEAT_API_EVENT_PAUSE, + GUEST_HEARTBEAT_API_EVENT_UNPAUSE, + GUEST_HEARTBEAT_API_EVENT_RESUME, + GUEST_HEARTBEAT_API_EVENT_RESIZE_BEGIN, + GUEST_HEARTBEAT_API_EVENT_RESIZE_END, + GUEST_HEARTBEAT_API_EVENT_LIVE_MIGRATE_BEGIN, + GUEST_HEARTBEAT_API_EVENT_LIVE_MIGRATE_END, + GUEST_HEARTBEAT_API_EVENT_COLD_MIGRATE_BEGIN, + GUEST_HEARTBEAT_API_EVENT_COLD_MIGRATE_END, + GUEST_HEARTBEAT_API_EVENT_MAX, +} GuestHeartbeatApiEventT; + +typedef enum { + GUEST_HEARTBEAT_API_NOTIFY_TYPE_UNKNOWN, + GUEST_HEARTBEAT_API_NOTIFY_TYPE_REVOCABLE, // vote on an action + GUEST_HEARTBEAT_API_NOTIFY_TYPE_IRREVOCABLE, // notification of an action + GUEST_HEARTBEAT_API_NOTIFY_TYPE_MAX, +} GuestHeartbeatApiNotifyTypeT; + +typedef enum { +// + GUEST_HEARTBEAT_API_VOTE_RESULT_UNKNOWN, + GUEST_HEARTBEAT_API_VOTE_RESULT_ACCEPT, // vote to accept an action + GUEST_HEARTBEAT_API_VOTE_RESULT_REJECT, // vote to reject an action + GUEST_HEARTBEAT_API_VOTE_RESULT_COMPLETE, // ready for action + GUEST_HEARTBEAT_API_VOTE_RESULT_MAX, +} GuestHeartbeatApiVoteResultT; + +// **************************************************************************** +// Guest Heartbeat API - Initialization Data +// ========================================= +// Description: +// Configuration data used on registration. +// +// Fields: +// application_name name of the application, used for logging +// heartbeat_interval_ms the interval for heartbeat challenges +// vote_secs maximum time to wait for a vote to complete +// shutdown_notice_secs maximum time to wait for a shutdown prep +// suspend_notice_secs maximum time to wait for a suspend prep +// resume_notice_secs maximum time to wait for a resume prep +// corrective_action corrective action on heartbeat timeouts +// +// Note: minimum heartbeat interval is 400 milliseconds. Anything below this +// interval will cause the registration to be rejected. +// +typedef struct { + char application_name[GUEST_HEARTBEAT_API_APPLICATION_NAME_MAX]; + int heartbeat_interval_ms; + int vote_secs; + int shutdown_notice_secs; + int suspend_notice_secs; + int resume_notice_secs; + GuestHeartbeatApiActionT corrective_action; +} GuestHeartbeatApiInitDataT; +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Register State Callback +// ============================================= +// Description: +// Called when the registration with the Guest-Client changes. Situations +// this callback can be invoked are the following: +// - Guest-Client accepts the registration, +// - Guest-Client rejects the registration, and +// - Guest-Client connection fails. +// +// If the registration state is False, the application needs to register +// again with the Guest-Client. +// +// Parameters: +// state the registration state of the application. +// +typedef void (*GuestHeartbeatApiRegisterStateCallbackT) + (bool state); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Health Check Callback +// =========================================== +// Description: +// Called by the Guest-Client to request the current health of the +// application. +// +// Parameters: +// health the health of the application +// corrective_action the corrective action to be taken when unhealthy +// log_msg an indication of why the application is unhealthy. +// +typedef void (*GuestHeartbeatApiHealthCheckCallbackT) + (bool* health, GuestHeartbeatApiActionT* corrective_action, + char log_msg[]); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Action Notify Callback +// ============================================ +// Description: +// Called when the Guest-Client wants to notify the application of an +// action. The notification type indicates if this is a vote or a +// notification. +// +// Parameters: +// invocation_id the unique identifier for the action. +// event the type of event for the action. +// notify_type the type of notification for the action. +// +typedef void (*GuestHeartbeatApiActionNotifyCallbackT) + (int invocation_id, GuestHeartbeatApiEventT event, + GuestHeartbeatApiNotifyTypeT notify_type); +// **************************************************************************** + +typedef struct { + GuestHeartbeatApiRegisterStateCallbackT register_state; + GuestHeartbeatApiHealthCheckCallbackT health_check; + GuestHeartbeatApiActionNotifyCallbackT action_notify; +} GuestHeartbeatApiCallbacksT; + +// **************************************************************************** +// Guest Heartbeat API - Action String +// =================================== +extern const char* guest_heartbeat_api_action_str( + GuestHeartbeatApiActionT action ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Event String +// ================================== +extern const char* guest_heartbeat_api_event_str( + GuestHeartbeatApiEventT event ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Notify String +// =================================== +extern const char* guest_heartbeat_api_notify_str( + GuestHeartbeatApiNotifyTypeT notify ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Vote Result String +// ======================================== +extern const char* guest_heartbeat_api_vote_result_str( + GuestHeartbeatApiVoteResultT vote_result ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Register +// ============================== +// Description: +// Sends a registration request to the Guest-Client. A try-again can be +// returned which indicates that registration should be attempted again +// at a later time. +// +// Parameters: +// init_data configuration parameters and timeout values for +// this application. +// +// Returns: +// GUEST_API_OKAY on success, GUEST_API_TRY_AGAIN if Guest-Client could +// not be reached, otherwise failure. +// +extern GuestApiErrorT guest_heartbeat_api_register( + GuestHeartbeatApiInitDataT* init_data ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Deregister +// ================================ +// Description: +// Sends a deregister to the Guest-Client. +// +// Parameters: +// log_msg indication of the reason for the de-registration. +// +// Returns: +// GUEST_API_OKAY on success, otherwise failure. +// +extern GuestApiErrorT guest_heartbeat_api_deregister( char log_msg[] ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Send Action Response +// ========================================== +// Description: +// Sends an action response to the Guest-Client indicating the action +// is accepted, rejected or completed. +// +// Parameters: +// invocation_id the unique identifier from the action callback. +// event the type of event from the action callback. +// notify_type the type of notification from the action callback. +// vote_result indication of acceptance of the action. +// log_msg an indication of why the action was rejected. +// +// Returns: +// GUEST_API_OKAY on success, otherwise failure. +// +extern GuestApiErrorT guest_heartbeat_api_send_action_response( + int invocation_id, GuestHeartbeatApiEventT event, + GuestHeartbeatApiNotifyTypeT notify_type, + GuestHeartbeatApiVoteResultT vote_result, char log_msg[] ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Dispatch +// ============================== +// Description: +// Called when the selection object returned by guest_heartbeat_api_get_selobj +// becomes readable. +// +// Parameters: +// selobj the selection object that has become readable. +// +// Returns: +// Nothing +// +extern void guest_heartbeat_api_dispatch( int selobj ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Get Selection Object +// ========================================== +// Description: +// Returns a selection object that can be used with poll or select. +// +// Parameters: +// None +// +// Returns: +// A valid selection object, otherwise -1. +// +extern int guest_heartbeat_api_get_selobj( void ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Initialize +// ================================ +// Description: +// Initialize the Guest Heartbeat API library for use. +// +// Parameters: +// callbacks a listing of callbacks for receiving registration +// state changes, health checks, and action notifications +// (all are required to be non-NULL). +// +// Returns: +// GUEST_API_OKAY on success, otherwise failure. +// +extern GuestApiErrorT guest_heartbeat_api_initialize( + GuestHeartbeatApiCallbacksT* callbacks ); +// **************************************************************************** + +// **************************************************************************** +// Guest Heartbeat API - Finalize +// ============================== +// Description: +// Finalize the Guest Heartbeat API library. +// +// Parameters: +// None +// +// Returns: +// GUEST_API_OKAY on success, otherwise failure. +// +extern GuestApiErrorT guest_heartbeat_api_finalize( void ); +// **************************************************************************** + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_HEARTBEAT_API_H__ */ diff --git a/guest-client/guest-client-3.0.1/guest_client_api/sample_guest_app.c b/guest-client/guest-client-3.0.1/guest_client_api/sample_guest_app.c new file mode 100755 index 00000000..c9ae5b77 --- /dev/null +++ b/guest-client/guest-client-3.0.1/guest_client_api/sample_guest_app.c @@ -0,0 +1,360 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "guest_api_types.h" +#include "guest_api_debug.h" +#include "guest_heartbeat_api.h" + +static int _heartbeat_selobj = -1; +static bool _heartbeat_registered = false; +static bool _heartbeat_registering = false; +static char _application_name[40] = "sample-guest-app"; +static GuestHeartbeatApiActionT _corrective_action; + +static sig_atomic_t _stay_on = 1; + +// **************************************************************************** +// Guest Application - Register State Callback +// =========================================== +static void guest_app_register_state_callback( bool state ) +{ + if (state) + _heartbeat_registering = false; + + _heartbeat_registered = state; +} +// **************************************************************************** + +// **************************************************************************** +// Guest Application - Health Check Callback +// ========================================= +static void guest_app_health_check_callback( + bool* healthy, GuestHeartbeatApiActionT* corrective_action, + char log_msg[GUEST_HEARTBEAT_API_LOG_MAX] ) +{ + char filename[80]; + int result; + + snprintf(filename, sizeof(filename), "/tmp/%s_unhealthy", + _application_name); + + result = access(filename, F_OK); + if (0 == result) + { + *healthy = false; + *corrective_action = _corrective_action; + snprintf(log_msg, GUEST_HEARTBEAT_API_LOG_MAX, "File %s exists.", + filename); + } else { + *healthy = true; + *corrective_action = GUEST_HEARTBEAT_API_ACTION_NONE; + log_msg[0] = '\0'; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Application - Action Notify Callback +// ========================================== +static void guest_app_action_notify_callback( + int invocation_id, GuestHeartbeatApiEventT event, + GuestHeartbeatApiNotifyTypeT notify_type ) +{ + char filename[80]; + GuestHeartbeatApiVoteResultT vote_result; + char log_msg[GUEST_HEARTBEAT_API_LOG_MAX]; + GuestApiErrorT error; + int result; + + snprintf(filename, sizeof(filename), "/tmp/%s_event_timeout", + _application_name); + + result = access(filename, F_OK); + if (0 == result) + return; + + if (GUEST_HEARTBEAT_API_NOTIFY_TYPE_REVOCABLE == notify_type) + { + switch (event) + { + case GUEST_HEARTBEAT_API_EVENT_STOP: + snprintf(filename, sizeof(filename), + "/tmp/%s_vote_no_to_stop", _application_name); + break; + + case GUEST_HEARTBEAT_API_EVENT_REBOOT: + snprintf(filename, sizeof(filename), + "/tmp/%s_vote_no_to_reboot", _application_name); + break; + case GUEST_HEARTBEAT_API_EVENT_SUSPEND: + case GUEST_HEARTBEAT_API_EVENT_PAUSE: + snprintf(filename, sizeof(filename), + "/tmp/%s_vote_no_to_suspend", _application_name); + break; + case GUEST_HEARTBEAT_API_EVENT_RESIZE_BEGIN: + snprintf(filename, sizeof(filename), + "/tmp/%s_vote_no_to_resize", _application_name); + break; + case GUEST_HEARTBEAT_API_EVENT_LIVE_MIGRATE_BEGIN: + case GUEST_HEARTBEAT_API_EVENT_COLD_MIGRATE_BEGIN: + snprintf(filename, sizeof(filename), + "/tmp/%s_vote_no_to_migrate", _application_name); + break; + default: + DPRINTFE("Should never be asked to vote on event %s.", + guest_heartbeat_api_event_str(event)); + return; + } + + result = access(filename, F_OK); + if (0 == result) + { + vote_result = GUEST_HEARTBEAT_API_VOTE_RESULT_REJECT; + snprintf(log_msg, GUEST_HEARTBEAT_API_LOG_MAX, "File %s exists.", + filename); + } else { + vote_result = GUEST_HEARTBEAT_API_VOTE_RESULT_ACCEPT; + log_msg[0] = '\0'; + } + } else { + vote_result = GUEST_HEARTBEAT_API_VOTE_RESULT_COMPLETE; + } + + error = guest_heartbeat_api_send_action_response(invocation_id, event, + notify_type, vote_result, log_msg); + if (GUEST_API_OKAY != error) + { + DPRINTFE("Failed to send action response, error=%s.", + guest_api_error_str(error)); + return; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Application - Signal Handler +// ================================== +static void guest_app_signal_handler( int signum ) +{ + switch (signum) + { + case SIGINT: + case SIGTERM: + case SIGQUIT: + _stay_on = 0; + break; + + case SIGCONT: + DPRINTFD("Ignoring signal SIGCONT (%i).", signum); + break; + + case SIGPIPE: + DPRINTFD("Ignoring signal SIGPIPE (%i).", signum); + break; + + default: + DPRINTFD("Signal (%i) ignored.", signum); + break; + } +} +// **************************************************************************** + +// **************************************************************************** +// Guest Application - Main +// ======================== +int main( int argc, char *argv[], char *envp[] ) +{ + GuestHeartbeatApiCallbacksT callbacks; + GuestApiErrorT error; + + error = guest_api_debug_initialize("Guest-Application"); + if (GUEST_API_OKAY != error) + { + printf("Debug initialization failed, error=%s.\n", + guest_api_error_str(error)); + return EXIT_FAILURE; + } + + DPRINTFI("Starting."); + + signal(SIGINT, guest_app_signal_handler); + signal(SIGTERM, guest_app_signal_handler); + signal(SIGQUIT, guest_app_signal_handler); + signal(SIGCONT, guest_app_signal_handler); + signal(SIGPIPE, guest_app_signal_handler); + + _corrective_action = GUEST_HEARTBEAT_API_ACTION_REBOOT; + + unsigned int arg_i; + for (arg_i=1; arg_i < argc; ++arg_i) + { + if (0 == strcmp("--name", argv[arg_i])) + { + arg_i++; + if (arg_i < argc) + snprintf(_application_name, sizeof(_application_name), "%s", + argv[arg_i]); + + } else if (0 == strcmp("--corrective-action", argv[arg_i])) { + arg_i++; + if (arg_i < argc) + { + if (0 == strcmp("reboot", argv[arg_i])) + { + _corrective_action = GUEST_HEARTBEAT_API_ACTION_REBOOT; + + } else if (0 == strcmp("stop", argv[arg_i])) { + _corrective_action = GUEST_HEARTBEAT_API_ACTION_STOP; + + } else if (0 == strcmp("log", argv[arg_i])) { + _corrective_action = GUEST_HEARTBEAT_API_ACTION_LOG; + } + } + } + } + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.register_state = guest_app_register_state_callback; + callbacks.health_check = guest_app_health_check_callback; + callbacks.action_notify = guest_app_action_notify_callback; + + error = guest_heartbeat_api_initialize(&callbacks); + if (GUEST_API_OKAY != error) + { + DPRINTFE("Failed to initialize guest heartbeat api, error=%s.", + guest_api_error_str(error)); + return EXIT_FAILURE; + } + + DPRINTFI("Started."); + + while (_stay_on) + { + int num_fds; + fd_set fds; + struct timeval tv; + int result; + + tv.tv_sec = 1; + tv.tv_usec = 0; + + num_fds = 0; + FD_ZERO(&fds); + + if (!_heartbeat_registered) + { + GuestHeartbeatApiInitDataT init_data; + + snprintf(init_data.application_name, + sizeof(init_data.application_name), "%s", + _application_name); + init_data.heartbeat_interval_ms = 1000; + init_data.vote_secs = 8; + init_data.shutdown_notice_secs = 5; + init_data.suspend_notice_secs = 5; + init_data.resume_notice_secs = 5; + init_data.corrective_action = _corrective_action; + + error = guest_heartbeat_api_register(&init_data); + if (GUEST_API_OKAY == error) + { + _heartbeat_registering = true; + } else { + _heartbeat_registering = false; + + if (GUEST_API_TRY_AGAIN != error) + { + DPRINTFE("Failed to register for guest heartbeating, " + "error=%s.", guest_api_error_str(error)); + return EXIT_FAILURE; + } + } + } + + if (_heartbeat_registering || _heartbeat_registered) + { + _heartbeat_selobj = guest_heartbeat_api_get_selobj(); + FD_SET(_heartbeat_selobj, &fds); + num_fds = _heartbeat_selobj; + } + + result = select(num_fds+1, &fds, NULL, NULL, &tv); + if (0 > result) + { + if (errno == EINTR) + { + DPRINTFD("Interrupted by a signal."); + } else { + DPRINTFE("Select failed, error=%s.", strerror(errno)); + } + } else if (0 == result) { + DPRINTFD("Nothing selected."); + } else { + if (FD_ISSET(_heartbeat_selobj, &fds)) + { + guest_heartbeat_api_dispatch(_heartbeat_selobj); + } + } + } + + DPRINTFI("Shutting down."); + + error = guest_heartbeat_api_deregister("Exiting"); + if (GUEST_API_OKAY != error) + { + DPRINTFE("Failed to deregister from guest heartbeat api, error=%s.", + guest_api_error_str(error)); + } + + error = guest_heartbeat_api_finalize(); + if (GUEST_API_OKAY != error) + { + DPRINTFE("Failed to finalize guest heartbeat api, error=%s.", + guest_api_error_str(error)); + } + + DPRINTFI("Shutdown complete."); + + error = guest_api_debug_finalize(); + if (GUEST_API_OKAY != error) + { + printf("Debug finalization failed, error=%s.\n", + guest_api_error_str(error)); + } + + return EXIT_SUCCESS; +} +// **************************************************************************** diff --git a/guest-client/guest-client-3.0.1/include/guest_heartbeat_api_msg_defs.h b/guest-client/guest-client-3.0.1/include/guest_heartbeat_api_msg_defs.h new file mode 100755 index 00000000..f178c618 --- /dev/null +++ b/guest-client/guest-client-3.0.1/include/guest_heartbeat_api_msg_defs.h @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __GUEST_HEARTBEAT_API_MESSAGE_DEFINITIONS_H__ +#define __GUEST_HEARTBEAT_API_MESSAGE_DEFINITIONS_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define GUEST_HEARTBEAT_API_MSG_ADDRESS "/var/run/.guest_heartbeat_api" + +#define GUEST_HEARTBEAT_API_MSG_MAGIC_VALUE "FDFDA5A5" +#define GUEST_HEARTBEAT_API_MSG_MAGIC_SIZE 8 +#define GUEST_HEARTBEAT_API_MSG_VERSION_CURRENT 2 +#define GUEST_HEARTBEAT_API_MSG_REVISION_CURRENT 1 + +#define GUEST_HEARTBEAT_API_MSG_MAX_APPLICATION_NAME_SIZE 40 +#define GUEST_HEARTBEAT_API_MSG_MAX_LOG_SIZE 192 + +typedef enum { + GUEST_HEARTBEAT_API_MSG_ACTION_UNKNOWN, + GUEST_HEARTBEAT_API_MSG_ACTION_NONE, + GUEST_HEARTBEAT_API_MSG_ACTION_REBOOT, + GUEST_HEARTBEAT_API_MSG_ACTION_STOP, + GUEST_HEARTBEAT_API_MSG_ACTION_LOG, + GUEST_HEARTBEAT_API_MSG_ACTION_MAX, +} GuestHeartbeatApiMsgActionT; + +typedef enum { + GUEST_HEARTBEAT_API_MSG_EVENT_UNKNOWN, + GUEST_HEARTBEAT_API_MSG_EVENT_STOP, + GUEST_HEARTBEAT_API_MSG_EVENT_REBOOT, + GUEST_HEARTBEAT_API_MSG_EVENT_SUSPEND, + GUEST_HEARTBEAT_API_MSG_EVENT_PAUSE, + GUEST_HEARTBEAT_API_MSG_EVENT_UNPAUSE, + GUEST_HEARTBEAT_API_MSG_EVENT_RESUME, + GUEST_HEARTBEAT_API_MSG_EVENT_RESIZE_BEGIN, + GUEST_HEARTBEAT_API_MSG_EVENT_RESIZE_END, + GUEST_HEARTBEAT_API_MSG_EVENT_LIVE_MIGRATE_BEGIN, + GUEST_HEARTBEAT_API_MSG_EVENT_LIVE_MIGRATE_END, + GUEST_HEARTBEAT_API_MSG_EVENT_COLD_MIGRATE_BEGIN, + GUEST_HEARTBEAT_API_MSG_EVENT_COLD_MIGRATE_END, + GUEST_HEARTBEAT_API_MSG_EVENT_MAX, +} GuestHeartbeatApiMsgEventT; + +typedef enum { + GUEST_HEARTBEAT_API_MSG_NOTIFY_UNKNOWN, + GUEST_HEARTBEAT_API_MSG_NOTIFY_REVOCABLE, + GUEST_HEARTBEAT_API_MSG_NOTIFY_IRREVOCABLE, + GUEST_HEARTBEAT_API_MSG_NOTIFY_MAX, +} GuestHeartbeatApiMsgNotifyT; + +typedef enum { + GUEST_HEARTBEAT_API_MSG_VOTE_RESULT_UNKNOWN, + GUEST_HEARTBEAT_API_MSG_VOTE_RESULT_ACCEPT, + GUEST_HEARTBEAT_API_MSG_VOTE_RESULT_REJECT, + GUEST_HEARTBEAT_API_MSG_VOTE_RESULT_COMPLETE, + GUEST_HEARTBEAT_API_MSG_VOTE_RESULT_MAX, +} GuestHeartbeatApiMsgVoteResultT; + +typedef enum { + GUEST_HEARTBEAT_API_MSG_INIT, + GUEST_HEARTBEAT_API_MSG_INIT_ACK, + GUEST_HEARTBEAT_API_MSG_FINAL, + GUEST_HEARTBEAT_API_MSG_CHALLENGE, + GUEST_HEARTBEAT_API_MSG_CHALLENGE_RESPONSE, + GUEST_HEARTBEAT_API_MSG_ACTION_NOTIFY, + GUEST_HEARTBEAT_API_MSG_ACTION_RESPONSE, + GUEST_HEARTBEAT_API_MSG_TYPE_MAX, +} GuestHeartbeatApiMsgTypeT; + +typedef struct { + char magic[GUEST_HEARTBEAT_API_MSG_MAGIC_SIZE]; + uint8_t version; + uint8_t revision; + uint16_t msg_type; + uint32_t sequence; + uint32_t size; +} GuestHeartbeatApiMsgHeaderT; + +typedef struct { + char application_name[GUEST_HEARTBEAT_API_MSG_MAX_APPLICATION_NAME_SIZE]; + uint32_t heartbeat_interval_ms; + uint32_t vote_secs; + uint32_t shutdown_notice_secs; + uint32_t suspend_notice_secs; + uint32_t resume_notice_secs; + uint32_t corrective_action; +} GuestHeartbeatApiMsgInitT; + +typedef struct { + uint32_t accepted; +} GuestHeartbeatApiMsgInitAckT; + +typedef struct { + char log_msg[GUEST_HEARTBEAT_API_MSG_MAX_LOG_SIZE]; +} GuestHeartbeatApiMsgFinalT; + +typedef struct { + uint32_t heartbeat_challenge; +} GuestHeartbeatApiMsgChallengeT; + +typedef struct { + uint32_t heartbeat_response; + uint32_t health; + uint32_t corrective_action; + char log_msg[GUEST_HEARTBEAT_API_MSG_MAX_LOG_SIZE]; +} GuestHeartbeatApiMsgChallengeResponseT; + +typedef struct { + uint32_t invocation_id; + uint32_t event_type; + uint32_t notification_type; +} GuestHeartbeatApiMsgActionNotifyT; + +typedef struct { + uint32_t invocation_id; + uint32_t event_type; + uint32_t notification_type; + uint32_t vote_result; + char log_msg[GUEST_HEARTBEAT_API_MSG_MAX_LOG_SIZE]; +} GuestHeartbeatApiMsgActionResponseT; + +typedef union { + GuestHeartbeatApiMsgInitT init; + GuestHeartbeatApiMsgInitAckT init_ack; + GuestHeartbeatApiMsgFinalT final; + GuestHeartbeatApiMsgChallengeT challenge; + GuestHeartbeatApiMsgChallengeResponseT challenge_response; + GuestHeartbeatApiMsgActionNotifyT action_notify; + GuestHeartbeatApiMsgActionResponseT action_response; +} GuestHeartbeatApiMsgBodyT; + +typedef struct { + GuestHeartbeatApiMsgHeaderT header; + GuestHeartbeatApiMsgBodyT body; +} GuestHeartbeatApiMsgT; + +#ifdef __cplusplus +} +#endif + +#endif /* __GUEST_HEARTBEAT_API_MESSAGE_DEFINITIONS_H__ */ diff --git a/guest-client/guest-client-3.0.1/packaging/guest-client.pkg b/guest-client/guest-client-3.0.1/packaging/guest-client.pkg new file mode 100755 index 00000000..cd3df1e8 --- /dev/null +++ b/guest-client/guest-client-3.0.1/packaging/guest-client.pkg @@ -0,0 +1,108 @@ +#! /bin/bash +# +# Copyright(c) 2013-2016, Wind River Systems, Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Wind River Systems nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +RETVAL=0 + +CMD=$1 +INIT_TYPE=$2 +DESTDIR=$3 + +# Note: the variable, DESTDIR has to be an absolute path. + +if [ -z ${INIT_TYPE} ] +then + INIT_TYPE="sysv" +fi + +if [ -z ${DESTDIR} ] +then + DESTDIR="/" +fi + +case "$CMD" in + install) + find ./rootdir/ -type d | while read source_dir; do + target_dir=${source_dir#./rootdir/} + mkdir -p $DESTDIR/$target_dir + done + + cp --preserve=links -R ./rootdir/* $DESTDIR + + if [ "$INIT_TYPE" = "systemd" ] + then + mkdir -p --mode 755 $DESTDIR/etc/guest-client/ + cp guest-client.systemd $DESTDIR/etc/guest-client/ + mkdir -p --mode 755 $DESTDIR/etc/systemd/system + cp guest-client.service $DESTDIR/lib/systemd/system/guest-client.service + ln -s $DESTDIR/lib/systemd/system/guest-client.service $DESTDIR/etc/systemd/system/guest-client.service + + elif [ "$INIT_TYPE" = "sysv" ] + then + mkdir -p --mode 755 $DESTDIR/etc/init.d + cp guest-client.init $DESTDIR/etc/init.d/guest-client + + else + echo "Unknown init-type given, INIT_TYPE=$INIT_TYPE" + fi + ;; + + uninstall) + if [ "$INIT_TYPE" = "systemd" ] + then + rm $DESTDIR/etc/guest-client/guest-client.systemd + rm $DESTDIR/lib/systemd/system/guest-client.service + rm $DESTDIR/etc/systemd/system/guest-client.service + + elif [ "$INIT_TYPE" = "sysv" ] + then + rm $DESTDIR/etc/init.d/guest-client + + else + echo "Unknown init type given, INIT_TYPE=$INIT_TYPE" + fi + + find ./rootdir -type l | while read source_file; do + target_file=${source_file#./rootdir/} + rm $DESTDIR/$target_file + done + + find ./rootdir -type f | while read source_file; do + target_file=${source_file#./rootdir/} + rm $DESTDIR/$target_file + done + ;; + + *) + echo "usage: $0 { install | uninstall }" + ;; +esac + +exit ${RETVAL} diff --git a/guest-comm/PKG-INFO b/guest-comm/PKG-INFO new file mode 100644 index 00000000..df921ec5 --- /dev/null +++ b/guest-comm/PKG-INFO @@ -0,0 +1,12 @@ +Metadata-Version: 1.1 +Name: host-guest-comm +Version: 2.0 +Summary: Titanium Cloud host guest messaging agents, lib, apps +Home-page: +Author: Windriver +Author-email: info@windriver.com +License: Apache-2.0 + +Description: Titanium Cloud host guest messaging agents, lib, apps + +Platform: UNKNOWN diff --git a/guest-comm/centos/build_srpm.data b/guest-comm/centos/build_srpm.data new file mode 100644 index 00000000..6c5b4dcf --- /dev/null +++ b/guest-comm/centos/build_srpm.data @@ -0,0 +1,2 @@ +SRC_DIR="host-guest-comm-2.0" +TIS_PATCH_VER=5 diff --git a/guest-comm/centos/host-guest-comm.spec b/guest-comm/centos/host-guest-comm.spec new file mode 100644 index 00000000..178c0b5b --- /dev/null +++ b/guest-comm/centos/host-guest-comm.spec @@ -0,0 +1,230 @@ +Summary: Titanium Cloud host guest messaging agents, lib, apps +Name: host-guest-comm +Version: 2.0 +%define patchlevel %{tis_patch_ver} +Release: %{tis_patch_ver}%{?_tis_dist} + +License: Apache-2.0 +Group: base +Packager: Wind River +URL: unknown + +Source0: %{name}-%{version}.tar.gz + +%define cgcs_sdk_deploy_dir /opt/deploy/cgcs_sdk + +BuildRequires: json-c +BuildRequires: json-c-devel +BuildRequires: systemd-devel + +Requires: rtld(GNU_HASH) +Requires: /bin/sh +Requires: /usr/bin/systemctl + +%description +Titanium Cloud host/guest messaging agents, guest app library, guest app + +%package -n guest-host-comm +Summary: Titanium Cloud host guest messaging agents, lib, apps +Group: base +Requires: rtld(GNU_HASH) +Requires(post): rtld(GNU_HASH) +Requires: systemd +Requires(post): systemd +Requires(preun): systemd + +%description -n guest-host-comm +Titanium Cloud host/guest messaging agents, guest app library, guest app + +%package -n guest-host-comm-dev +Summary: Titanium Cloud host guest messaging agents, lib, apps +Group: base +Requires: guest-host-comm + +%description -n guest-host-comm-dev +Titanium Cloud host/guest messaging agents, guest app library, guest app + +%package -n %{name}-cgts-sdk +Summary: Titanium Cloud host guest messaging SDK files +Group: devel + +%description -n %{name}-cgts-sdk +Titanium Cloud host guest messaging SDK files + +%package -n host-guest-comm-dbg +Summary: Titanium Cloud host guest messaging agents, lib, apps - Debugging files +Group: devel + +%description -n host-guest-comm-dbg +Titanium Cloud host/guest messaging agents, guest app library, guest app. This +package contains ELF symbols and related sources for debugging purposes. + +%package -n host-guest-comm-dev +Summary: Titanium Cloud host guest messaging agents, lib, apps - Development files +Group: devel +Requires: host-guest-comm = %{version}-%{release} + +%description -n host-guest-comm-dev +Titanium Cloud host/guest messaging agents, guest app library, guest app This +package contains symbolic links, header files, and related items necessary +for software development. + +%prep +%setup + +%build +VER=%{version} +MAJOR=`echo $VER | awk -F . '{print $1}'` +MINOR=`echo $VER | awk -F . '{print $2}'` +PATCH=%{patchlevel} + +make all VER=${VER} MAJOR=${MAJOR} MINOR=${MINOR} PATCH=${PATCH} +find . +find . -name "*.tgz" + +%global _buildsubdir %{_builddir}/%{name}-%{version} + +%install +VER=%{version} +MAJOR=`echo $VER | awk -F . '{print $1}'` +MINOR=`echo $VER | awk -F . '{print $2}'` +PATCH=%{patchlevel} + +install -m 750 -d %{buildroot}/usr/sbin +install -m 755 -d %{buildroot}/usr/lib64 +install -m 755 -d %{buildroot}/usr/include +install -m 755 -d %{buildroot}/usr/include/cgcs +install -m 750 -d %{buildroot}%{_sysconfdir}/init.d +install -m 750 -d %{buildroot}%{_sysconfdir}/pmon.d +install -m 750 -d %{buildroot}%{_unitdir} + +install -m 750 -d %{buildroot}/usr +install -m 750 -d %{buildroot}/usr/src +install -m 750 -d %{buildroot}/usr/src/debug +install -m 750 -d %{buildroot}/usr/src/debug/host-guest-comm-%{version} + + +install -m 644 %{_buildsubdir}/host_guest_msg_type.h %{buildroot}/usr/src/debug/host-guest-comm-%{version}/host_guest_msg_type.h +install -m 644 %{_buildsubdir}/server_group_app.c %{buildroot}/usr/src/debug/host-guest-comm-%{version}/server_group_app.c +install -m 644 %{_buildsubdir}/server_group.c %{buildroot}/usr/src/debug/host-guest-comm-%{version}/server_group.c +install -m 644 %{_buildsubdir}/guest_agent.c %{buildroot}/usr/src/debug/host-guest-comm-%{version}/guest_agent.c +install -m 644 %{_buildsubdir}/lib_host_guest_msg.c %{buildroot}/usr/src/debug/host-guest-comm-%{version}/lib_host_guest_msg.c +install -m 644 %{_buildsubdir}/host_guest_msg.c %{buildroot}/usr/src/debug/host-guest-comm-%{version}/host_guest_msg.c +install -m 644 %{_buildsubdir}/lib_guest_host_msg.c %{buildroot}/usr/src/debug/host-guest-comm-%{version}/lib_guest_host_msg.c +install -m 644 %{_buildsubdir}/host_instance_mgmt.h %{buildroot}/usr/src/debug/host-guest-comm-%{version}/host_instance_mgmt.h +install -m 644 %{_buildsubdir}/host_instance_mgmt.c %{buildroot}/usr/src/debug/host-guest-comm-%{version}/host_instance_mgmt.c +install -m 644 %{_buildsubdir}/guest_host_msg.h %{buildroot}/usr/src/debug/host-guest-comm-%{version}/guest_host_msg.h +install -m 644 %{_buildsubdir}/host_guest_msg.h %{buildroot}/usr/src/debug/host-guest-comm-%{version}/host_guest_msg.h +install -m 644 %{_buildsubdir}/host_agent.c %{buildroot}/usr/src/debug/host-guest-comm-%{version}/host_agent.c +install -m 644 %{_buildsubdir}/server_group.h %{buildroot}/usr/src/debug/host-guest-comm-%{version}/server_group.h + +install -m 750 %{_buildsubdir}/scripts/host_agent %{buildroot}%{_sysconfdir}/init.d/host_agent +install -m 640 %{_buildsubdir}/scripts/host_agent.service %{buildroot}%{_unitdir}/host_agent.service +install -m 644 %{_buildsubdir}/scripts/guest-agent.service %{buildroot}%{_unitdir}/guest-agent.service +install -m 640 %{_buildsubdir}/scripts/host_agent.conf %{buildroot}%{_sysconfdir}/pmon.d/host_agent.conf +install -m 750 %{_buildsubdir}/bin/host_agent %{buildroot}/usr/sbin/host_agent +install -m 750 %{_buildsubdir}/bin/guest_agent %{buildroot}/usr/sbin/guest_agent +install -m 750 %{_buildsubdir}/bin/server_group_app %{buildroot}/usr/sbin/server_group_app +install -m 644 %{_buildsubdir}/guest_host_msg.h %{buildroot}/usr/include/cgcs/guest_host_msg.h +install -m 644 %{_buildsubdir}/host_guest_msg.h %{buildroot}/usr/include/cgcs/host_guest_msg.h + +# Deploy to the SDK deployment directory + +find . +install -d %{buildroot}%{cgcs_sdk_deploy_dir} +install -m 644 sdk/wrs-server-group-%{version}.%{patchlevel}.tgz %{buildroot}%{cgcs_sdk_deploy_dir}/wrs-server-group-%{version}.%{patchlevel}.tgz + + +install -m 755 -p -D %{_buildsubdir}/lib/libguesthostmsg.so.${MAJOR}.${MINOR}.${PATCH} %{buildroot}%{_libdir}/libguesthostmsg.so.${MAJOR}.${MINOR}.${PATCH} +cd %{buildroot}%{_libdir} ; ln -s libguesthostmsg.so.$MAJOR.$MINOR.${PATCH} libguesthostmsg.so.$MAJOR.${MINOR} +cd %{buildroot}%{_libdir} ; ln -s libguesthostmsg.so.$MAJOR.$MINOR.${PATCH} libguesthostmsg.so.$MAJOR +cd %{buildroot}%{_libdir} ; ln -s libguesthostmsg.so.$MAJOR.$MINOR.${PATCH} libguesthostmsg.so + +install -m 755 -p -D %{_buildsubdir}/lib/libhostguestmsg.so.${MAJOR}.${MINOR}.${PATCH} %{buildroot}%{_libdir}/libhostguestmsg.so.${MAJOR}.${MINOR}.${PATCH} +cd %{buildroot}%{_libdir} ; ln -s libhostguestmsg.so.$MAJOR.$MINOR.${PATCH} libhostguestmsg.so.$MAJOR.${MINOR} +cd %{buildroot}%{_libdir} ; ln -s libhostguestmsg.so.$MAJOR.$MINOR.${PATCH} libhostguestmsg.so.$MAJOR +cd %{buildroot}%{_libdir} ; ln -s libhostguestmsg.so.$MAJOR.$MINOR.${PATCH} libhostguestmsg.so + +install -m 755 -p -D %{_buildsubdir}/lib/libservergroup.so.${MAJOR}.${MINOR}.${PATCH} %{buildroot}%{_libdir}/libservergroup.so.${MAJOR}.${MINOR}.${PATCH} +cd %{buildroot}%{_libdir} ; ln -s libservergroup.so.$MAJOR.$MINOR.${PATCH} libservergroup.so.$MAJOR.${MINOR} +cd %{buildroot}%{_libdir} ; ln -s libservergroup.so.$MAJOR.$MINOR.${PATCH} libservergroup.so.$MAJOR +cd %{buildroot}%{_libdir} ; ln -s libservergroup.so.$MAJOR.$MINOR.${PATCH} libservergroup.so + +%post +/usr/bin/systemctl enable host_agent.service + +%postun +/usr/bin/systemctl disable host_agent.service + +%files +%defattr(-,root,root,-) + +/usr/lib64/libhostguestmsg.so.2.0.%{patchlevel} +/usr/lib64/libhostguestmsg.so.2.0 +/usr/lib64/libhostguestmsg.so.2 +/usr/sbin/host_agent +/etc/pmon.d/host_agent.conf +/etc/init.d/host_agent +%{_unitdir}/host_agent.service + +%files -n guest-host-comm +%defattr(-,root,root,-) + +/usr/lib64/libguesthostmsg.so.2.0.%{patchlevel} +/usr/lib64/libguesthostmsg.so.2.0 +/usr/lib64/libguesthostmsg.so.2 +/usr/lib64/libservergroup.so.2.0.%{patchlevel} +/usr/lib64/libservergroup.so.2.0 +/usr/lib64/libservergroup.so.2 +/usr/sbin/server_group_app +/usr/sbin/guest_agent +%{_unitdir}/guest-agent.service + +%preun -n guest-host-comm +%systemd_preun guest-agent.service + +%post -n guest-host-comm +%systemd_post guest-agent.service +/usr/bin/systemctl enable guest-agent.service >/dev/null 2>&1 + +%files -n guest-host-comm-dev +%defattr(-,root,root,-) + +/usr/include/cgcs/guest_host_msg.h +/usr/lib64/libguesthostmsg.so.2.0.%{patchlevel} +/usr/lib64/libguesthostmsg.so.2.0 +/usr/lib64/libguesthostmsg.so.2 +/usr/lib64/libguesthostmsg.so +/usr/lib64/libservergroup.so.2.0.%{patchlevel} +/usr/lib64/libservergroup.so.2.0 +/usr/lib64/libservergroup.so.2 +/usr/lib64/libservergroup.so + +%files -n host-guest-comm-dbg +%defattr(-,root,root,-) + +/usr/src/debug/host-guest-comm-2.0/server_group_app.c +/usr/src/debug/host-guest-comm-2.0/server_group.c +/usr/src/debug/host-guest-comm-2.0/guest_agent.c +/usr/src/debug/host-guest-comm-2.0/lib_host_guest_msg.c +/usr/src/debug/host-guest-comm-2.0/host_guest_msg.c +/usr/src/debug/host-guest-comm-2.0/lib_guest_host_msg.c +/usr/src/debug/host-guest-comm-2.0/host_guest_msg_type.h +/usr/src/debug/host-guest-comm-2.0/host_instance_mgmt.h +/usr/src/debug/host-guest-comm-2.0/host_instance_mgmt.c +/usr/src/debug/host-guest-comm-2.0/guest_host_msg.h +/usr/src/debug/host-guest-comm-2.0/host_guest_msg.h +/usr/src/debug/host-guest-comm-2.0/host_agent.c +/usr/src/debug/host-guest-comm-2.0/server_group.h + +%files -n host-guest-comm-dev +%defattr(-,root,root,-) + +/usr/include/cgcs/host_guest_msg.h +/usr/lib64/libhostguestmsg.so.2.0.%{patchlevel} +/usr/lib64/libhostguestmsg.so.2.0 +/usr/lib64/libhostguestmsg.so.2 +/usr/lib64/libhostguestmsg.so + +%files -n %{name}-cgts-sdk +%{cgcs_sdk_deploy_dir}/wrs-server-group-%{version}.%{patchlevel}.tgz diff --git a/guest-comm/host-guest-comm-2.0/LICENSE b/guest-comm/host-guest-comm-2.0/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/guest-comm/host-guest-comm-2.0/LICENSE @@ -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. diff --git a/guest-comm/host-guest-comm-2.0/Makefile b/guest-comm/host-guest-comm-2.0/Makefile new file mode 100644 index 00000000..f7b23905 --- /dev/null +++ b/guest-comm/host-guest-comm-2.0/Makefile @@ -0,0 +1,107 @@ +# +# Copyright(c) 2013-2016, Wind River Systems, Inc. All rights reserved. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Wind River Systems nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +# Note: if you change either Makefile or Makefile.sdk you need to manually +# do a "make clean" and then "make". + +# Import the Makefile that we give to the customer so we eat our own dogfood +include Makefile.sdk + +# This needs to be extended from what the customer will build. +EXECS += host_agent +BINEXECS=$(addprefix $(BINDIR)/, $(EXECS)) + +HOST_AGENT_SRCS = host_guest_msg.c host_agent.c host_instance_mgmt.c +HOST_AGENT_OBJ := $(patsubst %,$(ODIR)/%,$(HOST_AGENT_SRCS:.c=.o)) + +LIBHOSTGUESTMSG_SRCS := lib_host_guest_msg.c +LIBHOSTGUESTMSG_OBJ := $(patsubst %,$(ODIR)/%,$(LIBHOSTGUESTMSG_SRCS:.c=.o)) +LIBHOSTGUESTMSG := hostguestmsg +LIBHOSTGUESTMSG_SO := $(patsubst %,lib%.so,$(LIBHOSTGUESTMSG)) +LIBHOSTGUESTMSG_DEP := $(patsubst %,$(LIBDIR)/%,$(LIBHOSTGUESTMSG_SO)) + +LIBS= $(LIBHOSTGUESTMSG) +LIBDEPS:=$(patsubst %,$(LIBDIR)/lib%.so,$(LIBS)) + +EXPORT_SDK := wrs-server-group-$(MAJOR).$(MINOR).$(PATCH) + +# Add the host build as a dependency to the SDK build +all: host_all +host_all: $(BINEXECS) $(LIBDEPS) sdk/$(EXPORT_SDK).tgz + +OBJS = $(HOST_AGENT_OBJ) +DEPS = $(OBJS:.o=.d) sdk/sdk.d +-include $(DEPS) + +# The dependency tracking for the SDK tarball is rather crude. If any +# file changes they're all copied over again. This is quick, so it's +# not worth getting fancy. +sdk/$(EXPORT_SDK).tgz: + @echo "Making SDK" + @mkdir -p sdk/$(EXPORT_SDK) + @mkdir -p sdk/$(EXPORT_SDK)/lib + @mkdir -p sdk/$(EXPORT_SDK)/bin + @mkdir -p sdk/$(EXPORT_SDK)/obj + @mkdir -p sdk/$(EXPORT_SDK)/scripts + @cp scripts/guest-agent.service sdk/$(EXPORT_SDK)/scripts + @echo "MAJOR=$(MAJOR)" > sdk/$(EXPORT_SDK)/Makefile + @echo "MINOR=$(MINOR)" >> sdk/$(EXPORT_SDK)/Makefile + @echo "PATCH=$(PATCH)" >> sdk/$(EXPORT_SDK)/Makefile + @cat Makefile.sdk >> sdk/$(EXPORT_SDK)/Makefile + @cp LICENSE sdk/$(EXPORT_SDK) + @cp docs/README.txt sdk/$(EXPORT_SDK) + @cp docs/TiS-Guest-Server-Group-Messaging.pdf sdk/$(EXPORT_SDK) + @cp $(GUEST_CLIENT_APP_SRCS) sdk/$(EXPORT_SDK) + @cp $(LIBSERVERGROUP_SRCS) sdk/$(EXPORT_SDK) + @cp $(LIBGUESTHOSTMSG_SRCS) sdk/$(EXPORT_SDK) + @cp $(GUEST_AGENT_SRCS) sdk/$(EXPORT_SDK) + @cp host_guest_msg_type.h server_group.h misc.h guest_host_msg.h sdk/$(EXPORT_SDK) + + @cd sdk && tar czf $(EXPORT_SDK).tgz $(EXPORT_SDK) + @echo -n "sdk/$(EXPORT_SDK).tgz: Makefile.sdk docs/README.txt docs/TiS-Guest-Server-Group-Messaging.pdf " > sdk/sdk.d + @echo -n "scripts/guest-agent.service" >> sdk/sdk.d + @echo -n "$(GUEST_CLIENT_APP_SRCS) $(LIBSERVERGROUP_SRCS) $(GUEST_AGENT_SRCS) $(LIBGUESTHOSTMSG_SRCS)" >> sdk/sdk.d + @echo " host_guest_msg.h server_group.h misc.h" >> sdk/sdk.d + @echo '*' > sdk/.gitignore + +$(LIBHOSTGUESTMSG_DEP): CFLAGS2 = -fPIC +$(LIBHOSTGUESTMSG_DEP): $(LIBHOSTGUESTMSG_OBJ) + $(CC) -shared -Wl,-soname,$(LIBHOSTGUESTMSG_SO).$(MAJOR) $^ -o $(LIBHOSTGUESTMSG_DEP).$(MAJOR).$(MINOR).$(PATCH) + ln -sf $(LIBHOSTGUESTMSG_SO).$(MAJOR).$(MINOR).$(PATCH) $(LIBHOSTGUESTMSG_DEP).$(MAJOR) + ln -sf $(LIBHOSTGUESTMSG_SO).$(MAJOR) $(LIBHOSTGUESTMSG_DEP) + +$(BINDIR)/host_agent: $(HOST_AGENT_OBJ) + $(CC) -o $@ $^ $(LDFLAGS) -lrt -ljson-c + +# Add the host clean as a dependency to the SDK stuff +clean: host_clean +host_clean: + rm -rf sdk diff --git a/guest-comm/host-guest-comm-2.0/Makefile.sdk b/guest-comm/host-guest-comm-2.0/Makefile.sdk new file mode 100644 index 00000000..84d4c62f --- /dev/null +++ b/guest-comm/host-guest-comm-2.0/Makefile.sdk @@ -0,0 +1,118 @@ +# +# Copyright(c) 2013-2016, Wind River Systems, Inc. All rights reserved. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Wind River Systems nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +.PHONY: all clean + +EXECS= guest_agent server_group_app + +CFLAGS= -g -Wall +LDFLAGS = + +ODIR=obj +BINDIR=bin +LIBDIR=lib +INCDIR=include/cgcs + +_DEPS = *.h +DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS)) + +GUEST_AGENT_SRCS = host_guest_msg.c guest_agent.c +GUEST_AGENT_OBJ := $(patsubst %,$(ODIR)/%,$(GUEST_AGENT_SRCS:.c=.o)) + +GUEST_CLIENT_APP_SRCS = server_group_app.c +GUEST_CLIENT_APP_OBJ := $(patsubst %,$(ODIR)/%,$(GUEST_CLIENT_APP_SRCS:.c=.o)) + +LIBGUESTHOSTMSG_SRCS := lib_guest_host_msg.c +LIBGUESTHOSTMSG_INCLUDES := guest_host_msg.h +LIBGUESTHOSTMSG_OBJ := $(patsubst %,$(ODIR)/%,$(LIBGUESTHOSTMSG_SRCS:.c=.o)) +LIBGUESTHOSTMSG := guesthostmsg +LIBGUESTHOSTMSG_SO := $(patsubst %,lib%.so,$(LIBGUESTHOSTMSG)) +LIBGUESTHOSTMSG_DEP := $(patsubst %,$(LIBDIR)/%,$(LIBGUESTHOSTMSG_SO)) + +LIBSERVERGROUP_SRCS := server_group.c +LIBSERVERGROUP_INCLUDES := server_group.h +LIBSERVERGROUP_OBJ := $(patsubst %,$(ODIR)/%,$(LIBSERVERGROUP_SRCS:.c=.o)) +LIBSERVERGROUP := servergroup +LIBSERVERGROUP_SO := $(patsubst %,lib%.so,$(LIBSERVERGROUP)) +LIBSERVERGROUP_DEP := $(patsubst %,$(LIBDIR)/%,$(LIBSERVERGROUP_SO)) + +LIBS= $(LIBSERVERGROUP) $(LIBGUESTHOSTMSG) +INCLUDES= $(LIBGUESTHOSTMSG_INCLUDES) $(LIBSERVERGROUP_INCLUDES) + +BINEXECS=$(addprefix $(BINDIR)/, $(EXECS)) + +LIBDEPS:=$(patsubst %,$(LIBDIR)/lib%.so,$(LIBS)) + +INCDEPS:=$(addprefix $(INCDIR)/, $(INCLUDES)) + +all: $(BINEXECS) $(LIBDEPS) $(INCDIR) $(INCDEPS) + +OBJS = $(GUEST_AGENT_OBJ) $(LIBSERVERGROUP_OBJ) $(GUEST_CLIENT_APP_OBJ) +DEPS = $(OBJS:.o=.d) +-include $(DEPS) + +$(ODIR)/%.o: %.c + $(CC) -c $(CFLAGS) $(CFLAGS2) -MMD -o $@ $< + +$(INCDIR): + mkdir -p $(INCDIR) + +$(INCDIR)/%.h: %.h $(INCDIR) + cp $< $@ + +vpath %.h . + +# the version of make in wrl5 seems to have a problem with target-specific +# variable values. It can't handle just doing "CFLAGS += -fPIC". Same thing +# for LDFLAGS below. This seems to work as a workaround. +$(LIBSERVERGROUP_DEP): CFLAGS2 = -fPIC +$(LIBSERVERGROUP_DEP): $(LIBSERVERGROUP_OBJ) + $(CC) -shared -Wl,-soname,$(LIBSERVERGROUP_SO).$(MAJOR) $^ -o $(LIBSERVERGROUP_DEP).$(MAJOR).$(MINOR).$(PATCH) + ln -sf $(LIBSERVERGROUP_SO).$(MAJOR).$(MINOR).$(PATCH) $(LIBSERVERGROUP_DEP).$(MAJOR) + ln -sf $(LIBSERVERGROUP_SO).$(MAJOR) $(LIBSERVERGROUP_DEP) + +$(LIBGUESTHOSTMSG_DEP): CFLAGS2 = -fPIC +$(LIBGUESTHOSTMSG_DEP): $(LIBGUESTHOSTMSG_OBJ) + $(CC) -shared -Wl,-soname,$(LIBGUESTHOSTMSG_SO).$(MAJOR) $^ -o $(LIBGUESTHOSTMSG_DEP).$(MAJOR).$(MINOR).$(PATCH) + ln -sf $(LIBGUESTHOSTMSG_SO).$(MAJOR).$(MINOR).$(PATCH) $(LIBGUESTHOSTMSG_DEP).$(MAJOR) + ln -sf $(LIBGUESTHOSTMSG_SO).$(MAJOR) $(LIBGUESTHOSTMSG_DEP) + +$(BINDIR)/guest_agent: LDFLAGS2 = -rdynamic +$(BINDIR)/guest_agent: $(GUEST_AGENT_OBJ) + $(CC) -o $@ $^ $(LDFLAGS) $(LDFLAGS2) -ljson-c + +$(BINDIR)/server_group_app: LDFLAGS2 = -l$(LIBSERVERGROUP) -L$(LIBDIR) +$(BINDIR)/server_group_app: $(GUEST_CLIENT_APP_OBJ) $(LIBSERVERGROUP_DEP) + $(CC) -o $@ $^ $(LDFLAGS) $(LDFLAGS2) -ljson-c + + +clean: + rm -rf $(ODIR)/* *~ core $(BINDIR)/* $(LIBDIR)/* diff --git a/guest-comm/host-guest-comm-2.0/bin/.gitignore b/guest-comm/host-guest-comm-2.0/bin/.gitignore new file mode 100644 index 00000000..5e7d2734 --- /dev/null +++ b/guest-comm/host-guest-comm-2.0/bin/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/guest-comm/host-guest-comm-2.0/docs/README.txt b/guest-comm/host-guest-comm-2.0/docs/README.txt new file mode 100644 index 00000000..9e17948a --- /dev/null +++ b/guest-comm/host-guest-comm-2.0/docs/README.txt @@ -0,0 +1,257 @@ + +Copyright(c) 2013-2016, Wind River Systems, Inc. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of Wind River Systems nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------- + +DESCRIPTION +=========== + +Server Group Messaging is a service to provide simple low-bandwidth datagram +messaging and notifications for servers that are part of the same server group. +This messaging channel is available regardless of whether IP networking is +functional within the server, and it requires no knowledge within the server +about the other members of the group. + +The service provides three types of messaging: +1) Broadcast: this allows a server to send a datagram (size of up to 3050 bytes) + to all other servers within the server group. + +2) Notification: this provides servers with information about changes to the + state of other servers within the server group. + +3) Status: this allows a server to query the current state of all servers within + the server group (including itself). + +This service is not intended for high bandwidth or low-latency operations. It +is best-effort, not reliable. Applications should do end-to-end acks and +retries if they care about reliability. + + +REQUIREMENTS +============ + Compilation: + Linux OS, x86_64 architecture + gcc compiler + development libraries and headers for glibc + development libraries and headers for json-c + + VM Runtime: + Linux OS, x86_64 architecture + runtime libraries for glibc + runtime libraries for json-c + + The code has been tested with glibc 2.15, gcc 4.6 and json-c 0.12.99 but it + should run on other versions without difficulty. + + +DELIVERABLE +=========== +The Server Group Messaging service is delivered as source with the +required makefiles in a compressed tarball called +"wrs-server-group-2.0.0.tgz", such that it can be compiled for the applicable +guest linux distribution. + + +COMPILE +======= +Install the prerequisites: + On CentOS/RHEL: yum install json-c-devel make gcc glibc-devel + On Ubuntu/Debian: sudo apt-get install build-essential libjson0-dev + +Extract the tarball contents: + tar xvf wrs-server-group-#.#.#.tgz + +To compile: + cd wrs-server-group-#.#.# + make + export WRS_SERVER_GROUP_DIR=${PWD} + +This will produce: + +1) An executable "bin/guest_agent". This acts as a relay between the guest and +the host. This executable must be installed into the guest and configured to +run at startup. It should be configured to respawn (via /etc/inittab or some +other process monitor) if it dies for any reason. + +Typical manual installation commands: +sudo cp bin/* /usr/sbin +sudo cp scripts/guest-agent.service /lib/systemd/system/ +sudo systemctl enable guest-agent.service +sudo systemctl start guest-agent.service + +2) A library "lib/libservergroup.so.2.0.*". This encapsulates the details of +talking to the guest agent. This can be used with the header file +"server_group.h" to create custom applications that can make use of the +messaging service. This should be installed within the guest in a suitable +library location such that the linker can find it. It should also be installed +in the build system where custom applications can link against it. The +"libservergroup.so.2" symlink should be created in the build system and the +guest, and the "libservergroup.so" symlink should be created in the build +system, either by ldconfig or some other means. + +Typical manual installation commands: +sudo cp -P lib/libservergroup.* /usr/lib/ +sudo ldconfig + +3) A header file "include/cgcs/server_group.h". This is used along with the library to +create custom applications that can make use of the messaging service. This +should be installed in the build system where custom applications can include +it. + +4) A sample program "bin/server_group_app" is created in order to show the use +of the APIs and validate that they are working properly in the guest. + +5) A library "lib/libguesthostmsg.so.2.0.*". This library is used by +wrs-guest-scale component packaged separately. This can be used with the +header file "guest_host_msg.h". This should be installed within +the guest in a suitable library location such that the linker can find it. +It should also be installed in the build system where custom applications can +link against it. The "libguesthostmsg.so.2" symlink should be created in the +build system and the guest, and the "libguesthostmsg.so" symlink should be +created in the build system, either by ldconfig or some other means. + +Typical manual installation commands: +sudo cp -P lib/libguesthostmsg.* /usr/lib/ +sudo ldconfig + +6) A header file "include/cgcs/guest_host_msg.h". This is used along with +the library to create custom applications that can make use of the messaging +service. This should be installed in the build system where custom +applications can include it. (Setting the WRS_SERVER_GROUP_DIR environment as above +variable will allow it to be found when building the wrs-guest-scale component.) + + +Note: +The inclusion of the files into the build system and the guest image and the +configuration of the guest startup scripts is left up to the user to allow for +different build systems and init subsystems in the guest. + + +INSTALL +======= +Installing in a running VM: + + As the root user + 1) Copy "bin/guest_agent" and any other desired binaries such as + "bin/server_group_app" to /usr/sbin in the VM. + + 2) Run "cp -P lib/* /usr/lib" (or /usr/lib64 as appropriate) in the VM + to install the libraries. + + 3) Run "ldconfig". + + 4) Copy scripts/guest-agent.service to /lib/systemd/system/ and then run + "systemctl enable guest-agent.service" and "systemctl start guest-agent.service". + This should cause the "guest-agent" binary to start running. + + 5) Applications can now make use of the service. + + 6) Run "cp -r include/cgcs /usr/include/" to set up the headers for anyone + wanting to link against libservergroup or libguesthostmsg. + +As part of building the VM ISO Image: + + 1) Ensure "bin/guest_agent" and any other desired binaries get installed + to /usr/sbin in the VM filesystem. + + 2) Ensure "lib/*" gets installed to /usr/lib64 in the VM filesystem. + + 3) Ensure that "ldconfig" will run at VM startup. + + 4) Ensure that "/usr/sbin/guest_agent" is configured to run automatically + at VM startup and to respawn if it dies for any reason. The scripts/ + guest_agent.service file is provided for use by systemd. + + +USAGE +===== +The service is designed to be simple to use. A basic description is given +below, but more details are provided in the header file. + +First, the application must call init_sg(). This call takes three function +pointers corresponding to callbacks for the various message types. If an +application doesn't intend to use one or more of the message types then the +appropriate function pointers can be specified as NULL. The function returns a +socket that must be monitored for activity using select/poll/etc. + +When the socket becomes readable, the application must call process_sg_msg(). +This may result in callbacks being called so be careful about deadlock. + +In order to send a broadcast message to every server in the server group, the +application can call sg_msg_broadcast(). + +In order to request the status of all servers in the group, the application can +call sg_request_status(). + +The sg_broadcast_msg_handler_t() callback will be called when receiving a server +group broadcast message. It will be passed the message as well as a +null-terminated string containing the instance name of the sender. + +The sg_notification_msg_handler_t() callback will be called when receiving a +notification message indicating a status change in one of the members of the +server group. The msg is JSON-formatted, essentially the normal notification +that gets sent out by OpenStack's notification service, but with some +non-relevant information removed to keep the message shorter. + +The sg_status_msg_handler_t() callback will be called when receiving the +response to a status query. The message is a JSON-formatted list of +dictionaries, each of which represents a single server. + + +SAMPLE APPLICATION +================== +The "server_group_app" sample application can be used to test the various types +of messages. It takes one optional argument, consisting of a character string +in quotes. When run, it behaves as follows: + +1) It registers for all three callbacks. +2) If the optional argument was specified it sends that string as a server group + broadcast message. +3) It requests the group status. +4) It then goes into a loop waiting for incoming messages. Any incoming message + will be printed out along with information about the type of message. + + +The service can be validated as follows using the "server_group_app" sample +application: +a) Create a server group with the "affinity" scheduler policy. +b) Start up a server within the server group. +c) Run "server_group_app" in the first server. You should immediately see the + status response message containing information about that server. +d) Start up a second server within the server group. You should see + notification messages being received in the first server. +e) Run 'server_group_app "this is a test"' on the second server. You should see + "this is a test" received as a broadcast message on the first server. The + second server should show a status response with information about both + servers +f) Start up a third server within the server group. You should see + notification messages being received in the other two servers. +g) Run 'server_group_app "final test"' on the second server. You should see + "final test" received as a broadcast message on the other two servers. The + second server should show a status response with information about all three + servers diff --git a/guest-comm/host-guest-comm-2.0/docs/TiS-Guest-Server-Group-Messaging.doc b/guest-comm/host-guest-comm-2.0/docs/TiS-Guest-Server-Group-Messaging.doc new file mode 100644 index 00000000..f8205d9b Binary files /dev/null and b/guest-comm/host-guest-comm-2.0/docs/TiS-Guest-Server-Group-Messaging.doc differ diff --git a/guest-comm/host-guest-comm-2.0/docs/TiS-Guest-Server-Group-Messaging.pdf b/guest-comm/host-guest-comm-2.0/docs/TiS-Guest-Server-Group-Messaging.pdf new file mode 100644 index 00000000..becafd04 Binary files /dev/null and b/guest-comm/host-guest-comm-2.0/docs/TiS-Guest-Server-Group-Messaging.pdf differ diff --git a/guest-comm/host-guest-comm-2.0/guest_agent.c b/guest-comm/host-guest-comm-2.0/guest_agent.c new file mode 100644 index 00000000..144e86f0 --- /dev/null +++ b/guest-comm/host-guest-comm-2.0/guest_agent.c @@ -0,0 +1,503 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** +*/ + +#define _GNU_SOURCE /* for memmem() */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "host_guest_msg_type.h" + +/* +Notes on virtio-serial in guest: +1) The fact that POLLHUP is always set in revents when host connection is down +means that the only way to get event-driven notification of connection is to +register for SIGIO. However, then we get a SIGIO every time the device becomes +readable. The solution is to selectively block SIGIO as long as we think the link +is up, then unblock it so we get notified when the link comes back. + +2) If the host disconnects we will still process any buffered messages from it. + +3) If read() returns 0 or write() returns -1 with errno of EAGAIN that means +the host is disconnected. +*/ + +// Tokener serves as reassembly buffer for host connection. +struct json_tokener* tok; + +int host_fd; +int app_fd; + +volatile sig_atomic_t check_host_connection=1; +volatile sig_atomic_t initial_connection=1; + +// Currently we only use 2 fds, one for server group messaging and one for the +// connection to the host. +#define MAX_FDS_GUEST 10 + +// Message has arrived from the host. +// This assumes the message has been validated +void process_msg(json_object *jobj_msg, int fd) +{ + int rc; + struct sockaddr_un cliaddr; + int addrlen; + + // parse version + struct json_object *jobj_version; + if (!json_object_object_get_ex(jobj_msg, VERSION, &jobj_version)) { + PRINT_ERR("failed to parse version\n"); + return; + } + int version = json_object_get_int(jobj_version); + if (version != CUR_VERSION) { + PRINT_ERR("received version %d, expected %d\n", version, CUR_VERSION); + return; + } + + // parse source addr + struct json_object *jobj_source_addr; + if (!json_object_object_get_ex(jobj_msg, SOURCE_ADDR, &jobj_source_addr)) { + PRINT_ERR("failed to parse source_addr\n"); + return; + } + + // parse dest addr + struct json_object *jobj_dest_addr; + if (!json_object_object_get_ex(jobj_msg, DEST_ADDR, &jobj_dest_addr)) { + PRINT_ERR("failed to parse dest_addr\n"); + return; + } + const char *dest_addr = json_object_get_string(jobj_dest_addr); + + + // parse msg data + struct json_object *jobj_data; + if (!json_object_object_get_ex(jobj_msg, DATA, &jobj_data)) { + PRINT_ERR("failed to parse data\n"); + return; + } + + //create outgoing message + struct json_object *jobj_outmsg = json_object_new_object(); + if (jobj_outmsg == NULL) { + PRINT_ERR("failed to allocate json object for jobj_outmsg\n"); + return; + } + + json_object_object_add(jobj_outmsg, DATA, jobj_data); + json_object_object_add(jobj_outmsg, VERSION, json_object_new_int(CUR_VERSION)); + json_object_object_add(jobj_outmsg, SOURCE_ADDR, jobj_source_addr); + + const char *outmsg = json_object_to_json_string_ext(jobj_outmsg, JSON_C_TO_STRING_PLAIN); + + // Set up destination address + memset(&cliaddr, 0, sizeof(struct sockaddr_un)); + cliaddr.sun_family = AF_UNIX; + cliaddr.sun_path[0] = '\0'; + strncpy(cliaddr.sun_path+1, dest_addr, strlen(dest_addr)); + addrlen = sizeof(sa_family_t) + strlen(dest_addr) + 1; + + // Send the message to the client. + // This will get transparently restarted if interrupted by signal. + ssize_t outlen = strlen(outmsg); + rc = sendto(app_fd, outmsg, outlen, 0, (struct sockaddr *) &cliaddr, + addrlen); + if (rc == -1) { + PRINT_ERR("unable to send msg to %.*s: %m\n", UNIX_ADDR_LEN, cliaddr.sun_path+1); + } else if (rc != outlen) { + PRINT_ERR("sendto didn't send the whole message\n"); + } + + json_object_put(jobj_outmsg); +} + +void handle_app_msg(const char *msg, struct sockaddr_un *cliaddr, + socklen_t cliaddrlen) +{ + int rc; + char *app_addr; + + //parse incoming msg + struct json_object *jobj_msg = json_tokener_parse(msg); + if (jobj_msg == NULL) { + PRINT_ERR("failed to parse msg\n"); + return; + } + + // parse version + struct json_object *jobj_version; + if (!json_object_object_get_ex(jobj_msg, VERSION, &jobj_version)) { + PRINT_ERR("failed to parse version\n"); + goto done; + } + int version = json_object_get_int(jobj_version); + + if (version != CUR_VERSION) { + PRINT_ERR("message from app version %d, expected %d, dropping\n", + version, CUR_VERSION); + goto done; + } + + // parse dest instance + struct json_object *jobj_dest_addr; + if (!json_object_object_get_ex(jobj_msg, DEST_ADDR, &jobj_dest_addr)) { + PRINT_ERR("failed to parse dest_address\n"); + goto done; + } + + // parse data + struct json_object *jobj_data; + if (!json_object_object_get_ex(jobj_msg, DATA, &jobj_data)) { + PRINT_ERR("failed to parse data\n"); + goto done; + } + + if (cliaddr->sun_path[0] == '\0') { + app_addr = cliaddr->sun_path+1; + // get length without family or leading null from abstract namespace + cliaddrlen = cliaddrlen - sizeof(sa_family_t) - 1; + app_addr[cliaddrlen] = '\0'; + } else { + PRINT_INFO("client address not in abstract namespace, dropping\n"); + goto done; + } + + struct json_object *jobj_outmsg = json_object_new_object(); + if (jobj_outmsg == NULL) { + PRINT_ERR("failed to allocate json object for outmsg\n"); + goto done; + } + + json_object_object_add(jobj_outmsg, DATA, jobj_data); + json_object_object_add(jobj_outmsg, VERSION, jobj_version); + json_object_object_add(jobj_outmsg, DEST_ADDR, jobj_dest_addr); + json_object_object_add(jobj_outmsg, SOURCE_ADDR, json_object_new_string(app_addr)); + + const char *outmsg = json_object_to_json_string_ext(jobj_outmsg, JSON_C_TO_STRING_PLAIN); + + // use '\n' to delimit JSON string: mark the beginning + rc = write(host_fd, "\n", 1); + if (rc == -1) { + PRINT_ERR("unable to send \\n \n"); + } + + // send to host + ssize_t outlen = strlen(outmsg); + rc = write(host_fd, outmsg, outlen); + if (rc == -1) { + PRINT_ERR("unable to send msg from %.*s: %m\n", UNIX_ADDR_LEN, app_addr); + } else if (rc != outlen) { + PRINT_ERR("write didn't write the whole message to host\n"); + } + + // use '\n' to delimit JSON string: mark the ending + rc = write(host_fd, "\n", 1); + if (rc == -1) { + PRINT_ERR("unable to send \\n \n"); + } + + json_object_put(jobj_outmsg); +done: + json_object_put(jobj_msg); +} + + +void unmask_sigio(void) +{ + sigset_t mask; + sigemptyset (&mask); + sigaddset (&mask, SIGIO); + sigprocmask(SIG_UNBLOCK, &mask, 0); +} + +void mask_sigio(void) +{ + sigset_t mask; + sigemptyset (&mask); + sigaddset (&mask, SIGIO); + sigprocmask(SIG_BLOCK, &mask, 0); +} + +void handle_host_conn_down() +{ + check_host_connection = 0; + unmask_sigio(); +} + +void scan_host_fd(struct pollfd *pfd) +{ + char buf[10000]; + ssize_t rc; + + if (pfd->revents & POLLIN) { + // Read all messages from the host device + while(1) { + rc = read(pfd->fd, buf, sizeof(buf)); + if (rc == 0) { + // Connection to host has gone down + handle_host_conn_down(); + return; + } else if (rc < 0) { + if (errno == EAGAIN) + // We've read all the messages + return; + else { + PRINT_ERR("read from host: %m"); + return; + } + } + handle_virtio_serial_msg(buf, rc, pfd->fd, tok); + } + } +} + +void scan_app_fd(struct pollfd *pfd) +{ + char buf[10000]; + struct sockaddr_un cliaddr; + ssize_t rc; + + // Read all messages from the app socket + if (pfd->revents & POLLIN) { + while(1) { + socklen_t addrlen = sizeof(struct sockaddr_un); + rc = recvfrom(pfd->fd, buf, sizeof(buf), 0, + (struct sockaddr *) &cliaddr, &addrlen); + if (rc < 0) { + if (errno == EAGAIN) + // We've read all the messages + return; + else { + PRINT_ERR("recvfrom from app: %m"); + return; + } + } + handle_app_msg(buf, &cliaddr, addrlen); + } + } +} + +//we get a SIGIO if the host connection connects/disconnects +static void sigio_handler(int signal) +{ + struct pollfd pfd; + pfd.fd = host_fd; + pfd.events = POLLIN; + poll(&pfd, 1, 0); + + // if host is not connected, just exit + if (pfd.revents & POLLHUP) + return; + + // host is connected so check it in main loop + check_host_connection = 1; + initial_connection = 1; +} + +//dump stack trace on segfault +static void segv_handler(int signum) +{ + int count; + void *syms[100]; + int fd = open("/var/log/guest_agent_backtrace.log", O_RDWR|O_APPEND|O_CREAT, S_IRWXU); + if (fd == -1) { + PRINT_ERR("Unable to open guest agent backtrace file: %m"); + goto out; + } + + write(fd, "\n", 1); + count = backtrace(syms, 100); + if (count == 0) { + char *log = "Got zero items in backtrace.\n"; + write(fd, log, strlen(log)); + goto out; + } + + backtrace_symbols_fd(syms, count, fd); +out: + fflush(NULL); + exit(-1); +} + +int main(int argc, char **argv) +{ + int flags; + int rc; + struct sockaddr_un svaddr; + int addrlen; + + PRINT_INFO("%s starting up\n", *argv); + + // optional arg for log level. Higher number means more logs + if (argc > 1) { + char *endptr, *str; + long val; + str = argv[1]; + errno = 0; + val = strtol(str, &endptr, 0); + + if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) + || (errno != 0 && val == 0)) { + PRINT_ERR("error parsing log level arg: strtol: %m"); + exit(-1); + } + + if (endptr == str) { + PRINT_ERR("No digits were found\n"); + exit(EXIT_FAILURE); + } + + if (val > LOG_DEBUG) + val = LOG_DEBUG; + + setlogmask(LOG_UPTO(val)); + } else + setlogmask(LOG_UPTO(LOG_WARNING)); + + signal(SIGIO, sigio_handler); + signal(SIGSEGV, segv_handler); + + // set up fd for talking to host + host_fd = open("/dev/virtio-ports/cgcs.messaging", O_RDWR|O_NONBLOCK); + if (host_fd == -1) { + PRINT_ERR("problem with open: %m"); + exit(-1); + } + + flags = fcntl(host_fd, F_GETFL); + rc = fcntl(host_fd, F_SETFL, flags | O_ASYNC); + if (rc == -1) { + PRINT_ERR("problem setting host_fd async: %m"); + exit(-1); + } + + rc = fcntl(host_fd, F_SETOWN, getpid()); + if (rc == -1) { + PRINT_ERR("problem owning host_fd: %m"); + exit(-1); + } + + // set up socket for talking to apps + app_fd = socket(AF_UNIX, SOCK_DGRAM, 0); + if (app_fd == -1) { + PRINT_ERR("problem with socket: %m"); + exit(-1); + } + + flags = fcntl(app_fd, F_GETFL, 0); + fcntl(app_fd, F_SETFL, flags | O_NONBLOCK); + + memset(&svaddr, 0, sizeof(struct sockaddr_un)); + svaddr.sun_family = AF_UNIX; + svaddr.sun_path[0] = '\0'; + strncpy(svaddr.sun_path+1, AGENT_ADDR, sizeof(svaddr.sun_path) - 2); + + addrlen = sizeof(sa_family_t) + strlen(AGENT_ADDR) + 1; + if (bind(app_fd, (struct sockaddr *) &svaddr, addrlen) == -1) { + PRINT_ERR("problem with bind: %m"); + exit(-1); + } + + tok = json_tokener_new(); + + while(1) { + struct pollfd pollfds[MAX_FDS_GUEST]; + int i; + int nfds = 0; + + if (check_host_connection) { + // we think the host connection is up + + if (initial_connection) { + //mask SIGIO if we haven't already + mask_sigio(); + initial_connection=0; + } + + pollfds[nfds].fd = host_fd; + pollfds[nfds].events = POLLIN; + nfds++; + + } + + pollfds[nfds].fd = app_fd; + pollfds[nfds].events = POLLIN; + nfds++; + + if (nfds > 0) { + rc = poll(pollfds, nfds, -1); + + if (rc == -1) { + if (errno == EINTR) + continue; + PRINT_ERR("problem with poll: %m"); + free(tok); + exit(-1); + } + + for(i=0;i +#include + +/* Function signature for the guest-host messaging callback function. + * source_addr is a null-terminated string representing the host source address. + * The message contents are up to the sender of the message. + */ + +typedef void (*gh_msg_handler_t)(const char *source_addr, struct json_object *jobj_msg); + +#define GH_ERRORSIZE 400 +typedef struct { + int sock; // socket for talking to guest agent + struct sockaddr_un svaddr; // address of guest agent + int svaddrlen; // length of guest agent address + char errorbuf[GH_ERRORSIZE]; + gh_msg_handler_t gh_msg_handler; +} gh_info_t; + + + +/* Allocate socket, set up callbacks, etc. This must be called once before + * any other API calls. "addr" is a null-terminated string of 16 chars or less + * (including the null) that is unique within this guest. "info" is the address + * of a value-result pointer that will be updated during the call. + * + * On success returns a socket and "info" is updated to point to an allocated chunk of memory. + * On error will return -1. If it was unable to allocate memory then "info" will be + * NULL. If it was able to allocate memory but something else failed then "info" will + * be non-NULL and you can call gh_get_error() to get an error message. + */ +int gh_init(gh_msg_handler_t msg_handler, char *addr, gh_info_t **info); + +/* This should be called when the socket becomes readable. This may result in + * callbacks being called. Returns 0 on success. + * A negative return value indicates an error of some kind. + */ +int gh_process_msg(gh_info_t *info); + +/* Send a message to an address on the host. + * Returns 0 on success. + * A negative return value indicates an error of some kind. + * The message must be a null-terminated string without embedded newlines. + */ +int gh_send_msg(gh_info_t *info, const char *dest_addr, const char *msg); + +/* Get error message from most recent library call that returned an error. */ +char *gh_get_error(gh_info_t *info); + + + +#endif diff --git a/guest-comm/host-guest-comm-2.0/host_agent.c b/guest-comm/host-guest-comm-2.0/host_agent.c new file mode 100644 index 00000000..d29b315d --- /dev/null +++ b/guest-comm/host-guest-comm-2.0/host_agent.c @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* +This is a backchannel messaging agent that will run on the host in order to +pass messages between the host and the guest. + +When a new instance appears (detected by a new unix socket of the specified +format being added to the watched directory) we open a unix stream socket +and connect to the instance, storing the mapping from instance to socket. + +When an instance dies we will close the socket and remove the mapping. + +We will monitor the connections to the instances as well as a unix datagram +socket used to communicate with other apps on the host. Messages coming +from an instance will be forwarded to the appropriate app, and vice versa. + +If we try to send a message to a guest socket that has nothing listening +within the guest, by default the message will get queued up without giving +us any indication that there is no listener. These messages can get bundled +together when they get delivered to the guest. +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "misc.h" +#include "host_guest_msg_type.h" +#include "host_instance_mgmt.h" + +int app_fd; + +// One tokener for each instance connection serve as reassembly buffer +struct json_tokener* tok[MAX_FDS_HOST]; + +#define SERVER_SOCKET_FORMAT \ + "/var/lib/libvirt/qemu/cgcs.messaging.%s.sock" + +// Message has arrived from the guest. +// This assumes the message has been validated +void process_msg(json_object *jobj_msg, int fd) +{ + int rc; + struct sockaddr_un cliaddr; + char *name; + int addrlen; + + name = instance_name_by_fd(fd); + if (!name) { + PRINT_ERR("whoops, can't get instance name from fd, dropping\n"); + return; + } + + // parse version + struct json_object *jobj_version; + if (!json_object_object_get_ex(jobj_msg, VERSION, &jobj_version)) { + PRINT_ERR("failed to parse version\n"); + return; + } + int version = json_object_get_int(jobj_version); + if (version != CUR_VERSION) { + PRINT_ERR("received version %d, expected %d\n", version, CUR_VERSION); + return; + } + + // parse source addr + struct json_object *jobj_source_addr; + if (!json_object_object_get_ex(jobj_msg, SOURCE_ADDR, &jobj_source_addr)) { + PRINT_ERR("failed to parse source_addr\n"); + return; + } + + // parse dest addr + struct json_object *jobj_dest_addr; + if (!json_object_object_get_ex(jobj_msg, DEST_ADDR, &jobj_dest_addr)) { + PRINT_ERR("failed to parse dest_addr\n"); + return; + } + const char *dest_addr = json_object_get_string(jobj_dest_addr); + + // parse data. data is a json object that is nested inside the msg + struct json_object *jobj_data; + if (!json_object_object_get_ex(jobj_msg, DATA, &jobj_data)) { + PRINT_ERR("failed to parse data\n"); + return; + } + + //create outgoing message + struct json_object *jobj_outmsg = json_object_new_object(); + if (jobj_outmsg == NULL) { + PRINT_ERR("failed to allocate json object for jobj_outmsg\n"); + return; + } + + json_object_object_add(jobj_outmsg, DATA, jobj_data); + json_object_object_add(jobj_outmsg, VERSION, json_object_new_int(CUR_VERSION)); + json_object_object_add(jobj_outmsg, SOURCE_ADDR, jobj_source_addr); + json_object_object_add(jobj_outmsg, SOURCE_INSTANCE, json_object_new_string(name)); + + const char *outmsg = json_object_to_json_string_ext(jobj_outmsg, JSON_C_TO_STRING_PLAIN); + ssize_t outlen = strlen(outmsg); + + // Set up destination address + memset(&cliaddr, 0, sizeof(struct sockaddr_un)); + cliaddr.sun_family = AF_UNIX; + cliaddr.sun_path[0] = '\0'; + strncpy(cliaddr.sun_path+1, dest_addr, strlen(dest_addr)); + addrlen = sizeof(sa_family_t) + strlen(dest_addr) + 1; + + // Send the message to the client. + // This will get transparently restarted if interrupted by signal. + rc = sendto(app_fd, outmsg, outlen, 0, (struct sockaddr *) &cliaddr, + addrlen); + if (rc == -1) { + PRINT_ERR("unable to send msg to client %.*s: %m\n", UNIX_ADDR_LEN, + cliaddr.sun_path+1); + } else if (rc != outlen) { + PRINT_ERR("sendto didn't send the whole message to client\n"); + } + json_object_put(jobj_outmsg); +} + +// Read and process all messages from the guest. If the guest dies, tear +// down the connection. +void scan_guest_fd(struct pollfd *pfd) +{ + char buf[10000]; + ssize_t rc; + + if (pfd->revents & POLLHUP) { + // The only known cause of this is the death of the qemu process. + // Tear everything down. + disconnect_guest(pfd->fd); + } else if (pfd->revents & POLLIN) { + // Read a message from the guest socket. + // We assume that all the data arrives in one packet. + // To handle arbitrary messages sizes we should receive into a buffer + // with knowledge of message length, etc. Can extend later if needed. + rc = read(pfd->fd, buf, sizeof(buf)); + if (rc == 0) { + PRINT_INFO("got read of 0 bytes on guest fd\n"); + return; + } else if (rc < 0) { + if (errno == EAGAIN) + // Odd, should have been a message + return; + else { + PRINT_ERR("read from guest: %m"); + return; + } + } + handle_virtio_serial_msg(buf, rc, pfd->fd, tok[pfd->fd]); + } +} + + +// validate header and send message to specified guest +void handle_app_msg(const char *msg, struct sockaddr_un *cliaddr, + socklen_t cliaddrlen) +{ + int rc; + char *app_addr; + int sock; + + //parse incoming msg + struct json_object *jobj_msg = json_tokener_parse(msg); + if (jobj_msg == NULL) { + PRINT_ERR("failed to parse msg\n"); + return; + } + + // parse version + struct json_object *jobj_version; + int version; + if (!json_object_object_get_ex(jobj_msg, VERSION, &jobj_version)) { + PRINT_ERR("failed to parse version\n"); + goto done; + } + version = json_object_get_int(jobj_version); + + if (version != CUR_VERSION) { + PRINT_ERR("message from app version %d, expected %d, dropping\n", + version, CUR_VERSION); + goto done; + } + + // parse dest addr + struct json_object *jobj_dest_addr; + if (!json_object_object_get_ex(jobj_msg, DEST_ADDR, &jobj_dest_addr)) { + PRINT_ERR("failed to parse dest_address\n"); + goto done; + } + + // parse dest instance + struct json_object *jobj_dest_instance; + if (!json_object_object_get_ex(jobj_msg, DEST_INSTANCE, &jobj_dest_instance)) { + PRINT_ERR("failed to parse dest_instance\n"); + goto done; + } + const char *dest_instance = json_object_get_string(jobj_dest_instance); + + // parse data + struct json_object *jobj_data; + if (!json_object_object_get_ex(jobj_msg, DATA, &jobj_data)) { + PRINT_ERR("failed to parse data\n"); + goto done; + } + + if (cliaddr->sun_path[0] == '\0') { + app_addr = cliaddr->sun_path+1; + // get length without family or leading null from abstract namespace + cliaddrlen = cliaddrlen - sizeof(sa_family_t) - 1; + app_addr[cliaddrlen] = '\0'; + } else { + PRINT_INFO("client address not in abstract namespace, dropping\n"); + goto done; + } + + // look up the guest socket based on the instance name + sock = fd_find_by_instance_name((char *)dest_instance); + if (sock == -1) { + PRINT_INFO("unable to find instance connection socket for %.20s\n", + dest_instance); + goto done; + } + + + struct json_object *jobj_outmsg = json_object_new_object(); + if (jobj_outmsg == NULL) { + PRINT_ERR("failed to allocate json object for outmsg\n"); + goto done; + } + + json_object_object_add(jobj_outmsg, DATA, jobj_data); + json_object_object_add(jobj_outmsg, VERSION, jobj_version); + json_object_object_add(jobj_outmsg, DEST_ADDR, jobj_dest_addr); + json_object_object_add(jobj_outmsg, SOURCE_ADDR, json_object_new_string(app_addr)); + + const char *outmsg = json_object_to_json_string_ext(jobj_outmsg, JSON_C_TO_STRING_PLAIN); + + // send to guest + ssize_t outlen = strlen(outmsg); + rc = send(sock, outmsg, outlen, 0); + if (rc == -1) { + PRINT_ERR("unable to send msg from %.*s: %m\n", UNIX_ADDR_LEN, app_addr); + } else if (rc != outlen) { + PRINT_ERR("write didn't write the whole message\n"); + } + + // use '\n' to delimit JSON string + rc = send(sock, "\n", 1, 0); + if (rc == -1) { + PRINT_ERR("unable to send \\n \n"); + } + + json_object_put(jobj_outmsg); + +done: + json_object_put(jobj_msg); +} + +// Read and process a message from the application socket +void scan_app_fd() +{ + char buf[10000]; + struct sockaddr_un cliaddr; + ssize_t rc; + + // Process a message from the app socket + socklen_t addrlen = sizeof(struct sockaddr_un); + rc = recvfrom(app_fd, buf, sizeof(buf), 0, + (struct sockaddr *) &cliaddr, &addrlen); + if (rc < 0) { + if (errno == EAGAIN) + // Odd, should have been a message + return; + else { + PRINT_ERR("error in recvfrom from app: %m"); + return; + } + } + handle_app_msg(buf, &cliaddr, addrlen); +} + +//dump stack trace on segfault +static void segv_handler(int signum) +{ + int count; + void *syms[100]; + int fd = open("/var/log/host_agent_backtrace.log", O_RDWR|O_APPEND|O_CREAT, S_IRWXU); + if (fd == -1) { + PRINT_ERR("Unable to open host agent backtrace file: %m"); + goto out; + } + + write(fd, "\n", 1); + count = backtrace(syms, 100); + if (count == 0) { + char *log = "Got zero items in backtrace.\n"; + write(fd, log, strlen(log)); + goto out; + } + + backtrace_symbols_fd(syms, count, fd); +out: + fflush(NULL); + exit(-1); +} + +void free_tok() +{ + int i; + for (i=0; i 1) { + char *endptr, *str; + long val; + str = argv[1]; + errno = 0; + val = strtol(str, &endptr, 0); + + if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) + || (errno != 0 && val == 0)) { + PRINT_ERR("error parsing log level arg: strtol: %m"); + exit(-1); + } + + if (endptr == str) { + PRINT_ERR("No digits were found\n"); + exit(EXIT_FAILURE); + } + + if (val > LOG_DEBUG) + val = LOG_DEBUG; + + setlogmask(LOG_UPTO(val)); + } else + setlogmask(LOG_UPTO(LOG_WARNING)); + + signal(SIGSEGV, segv_handler); + + // set up socket for talking to apps + app_fd = socket(AF_UNIX, SOCK_DGRAM, 0); + if (app_fd == -1) { + PRINT_ERR("problem with socket: %m"); + exit(-1); + } + + flags = fcntl(app_fd, F_GETFL, 0); + fcntl(app_fd, F_SETFL, flags | O_NONBLOCK); + + memset(&svaddr, 0, sizeof(struct sockaddr_un)); + svaddr.sun_family = AF_UNIX; + svaddr.sun_path[0] = '\0'; + strncpy(svaddr.sun_path+1, AGENT_ADDR, sizeof(svaddr.sun_path) - 2); + + addrlen = sizeof(sa_family_t) + strlen(AGENT_ADDR) + 1; + if (bind(app_fd, (struct sockaddr *) &svaddr, addrlen) == -1) { + PRINT_ERR("problem with bind to agent addr: %m"); + exit(-1); + } + + pollfd_add(app_fd, POLLIN); + + // This will set up monitoring for new/deleted instances + // and will set up connections for existing instances. + if (server_scan_init() < 0) + return -1; + + int i; + for (i=0; i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "host_guest_msg_type.h" + +/* + Multiple messages from the host can be bundled together into a single "read" + so we need to check message boundaries and handle breaking the message apart. + Assume a valid message does not contain newline '\n', and newline is added to + the beginning and end of each message by the sender to delimit the boundaries. +*/ + +void parser(void *buf, ssize_t len, int fd, json_tokener* tok, int newline_found) +{ + json_object *jobj = json_tokener_parse_ex(tok, buf, len); + enum json_tokener_error jerr = json_tokener_get_error(tok); + + if (jerr == json_tokener_success) { + process_msg(jobj, fd); + json_object_put(jobj); + return; + } + + else if (jerr == json_tokener_continue) { + // partial JSON is parsed , continue to read from socket. + if (newline_found) { + // if newline was found in the middle of the buffer, the message + // should be completed at this point. Throw out incomplete message + // by resetting tokener. + json_tokener_reset(tok); + } + } + else + { + // parsing error + json_tokener_reset(tok); + } +} + + +void handle_virtio_serial_msg(void *buf, ssize_t len, int fd, json_tokener* tok) +{ + void *newline; + ssize_t len_head; + +next: + if (len == 0) + return; + + // search for newline as delimiter + newline = memchr(buf, '\n', len); + + if (newline) { + // split buffer to head and tail at the location of newline. + // feed the head to the parser and recursively process the tail. + len_head = newline-buf; + + // parse head + if (len_head > 0) + parser(buf, len_head, fd, tok, 1); + + // start of the tail: skip newline + buf += len_head+1; + // length of the tail: deduct 1 for the newline character + len -= len_head+1; + + // continue to process the tail. + goto next; + } + else { + parser(buf, len, fd, tok, 0); + } +} diff --git a/guest-comm/host-guest-comm-2.0/host_guest_msg.h b/guest-comm/host-guest-comm-2.0/host_guest_msg.h new file mode 100644 index 00000000..7ab412a5 --- /dev/null +++ b/guest-comm/host-guest-comm-2.0/host_guest_msg.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HOSTGUEST_MSG_H +#define HOSTGUEST_MSG_H + +/* This implements a library for generic guest-host messaging. + * + * This handles some of the boilerplate code that would be common to + * all applications using the generic guest/host communications. That way + * any changes can be made in a single place. + * + * This should be threadsafe as long as multiple threads don't try to + * simultaneously process incoming messages using the same gh_info_t pointer. + * + */ + +#include +#include + +/* Function signature for the guest-host messaging callback function. + * source_addr is a null-terminated string representing the host source address. + * The message contents are entirely up to the sender of the message. +*/ +typedef void (*hg_msg_handler_t)(const char *source_addr, const char *source_instance, + struct json_object *jobj_msg); + +#define HG_ERRORSIZE 400 +typedef struct { + int sock; // socket for talking to guest agent + struct sockaddr_un svaddr; // address of guest agent + int svaddrlen; // length of guest agent address + char errorbuf[HG_ERRORSIZE]; + hg_msg_handler_t hg_msg_handler; +} hg_info_t; + + + +/* Allocate socket, set up callbacks, etc. This must be called once before + * any other API calls. "addr" is a null-terminated string of 16 chars or less + * (including the null) that is unique within this guest. "info" is the address + * of a value-result pointer that will be updated during the call. + * + * On success returns a socket and "info" is updated to point to an allocated chunk of memory. + * On error will return -1. If it was unable to allocate memory then "info" will be + * NULL. If it was able to allocate memory but something else failed then "info" will + * be non-NULL and you can call hg_get_error() to get an error message. + */ +int hg_init(hg_msg_handler_t msg_handler, char *addr, hg_info_t **info); + +/* This should be called when the socket becomes readable. This may result in + * callbacks being called. Returns 0 on success. + * A negative return value indicates an error of some kind. + */ +int hg_process_msg(hg_info_t *info); + +/* Send a message to an address on the host. + * Returns 0 on success. + * A negative return value indicates an error of some kind. + * The message must be a null-terminated string without embedded newlines. + */ +int hg_send_msg(hg_info_t *info, const char *dest_addr, const char *dest_instance, + const char *msg); + +/* Get error message from most recent library call that returned an error. */ +char *hg_get_error(hg_info_t *info); + + + +#endif diff --git a/guest-comm/host-guest-comm-2.0/host_guest_msg_type.h b/guest-comm/host-guest-comm-2.0/host_guest_msg_type.h new file mode 100644 index 00000000..aa0cd83e --- /dev/null +++ b/guest-comm/host-guest-comm-2.0/host_guest_msg_type.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HOST_GUEST_MSG_TYPE_H +#define HOST_GUEST_MSG_TYPE_H + +#include +#include + +#include "misc.h" + +#define CUR_VERSION 2 + +// The server group message is JSON encoded null-terminated string without +// embedded newlines. +// keys for messages: +#define VERSION "version" +#define SOURCE_ADDR "source_addr" +#define DEST_ADDR "dest_addr" +#define SOURCE_INSTANCE "source_instance" +#define DEST_INSTANCE "dest_instance" +#define DATA "data" + +// Used by the message reassembly code. +// The underlying implementation uses page-size buffers +#define HOST_GUEST_BUFSIZE 4096 + +#define MAX_INSTANCES 100 +#define MAX_FDS_HOST (MAX_INSTANCES+10) +#define INSTANCE_NAME_SIZE 20 + +// unix socket abstract namespace address of both host and guest agent +#define AGENT_ADDR "cgcs.messaging" + +#define UNIX_ADDR_LEN 16 + +// Initialize connection message reassembly buffer +void init_msgbuf(int fd); + +/* Used to store partial host_guest messages */ +typedef struct { + unsigned short len; + char buf[HOST_GUEST_BUFSIZE]; +} msgbuf_t; + +void handle_virtio_serial_msg(void *buf, ssize_t len, int fd, json_tokener* tok); +void process_msg(json_object *jobj_msg, int fd); + +#endif diff --git a/guest-comm/host-guest-comm-2.0/host_instance_mgmt.c b/guest-comm/host-guest-comm-2.0/host_instance_mgmt.c new file mode 100644 index 00000000..5d6f1d78 --- /dev/null +++ b/guest-comm/host-guest-comm-2.0/host_instance_mgmt.c @@ -0,0 +1,621 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "misc.h" +#include "host_instance_mgmt.h" + + +/* When a new instance starts up it will create a named unix socket in a known +directory with a known filename format. + +We will monitor the directory using inotify. On detection of a new instance we +will add the instance to our list of instances, create a socket, and connect it +to the named socket. On detection of a deleted instance we will tear it all +down. + +We need to be able to map from socket to instance name and vice versa. +*/ + +// Main poll structure management stuff +struct pollfd pollfds[MAX_INSTANCES]; +int nfds; + +int inotify_fd; +static int inotify_watch_id; +static struct inotify_event *event_ptr; +static int event_size; + +// Directory to watch for sockets +const char* host_virtio_dir = "/var/lib/libvirt/qemu"; + +// Mapping between instance name and fd for its connection +typedef struct { + int fd; + int pollfd_index; + char name[INSTANCE_NAME_SIZE]; +} instance_t; + +static int max_instance=-1; +static instance_t instances[MAX_INSTANCES]; + + +// Lookup table indexed by instance connection fd. +// Assumes fd re-use otherwise the table would need to be bigger. +static instance_t *instance_ptrs[MAX_FDS_HOST]; + + +// Linked list nodes for queue of connections to be retried. +struct conn_retry { + struct conn_retry *next; + unsigned long long next_try; // next retry time in nanoseconds + int sock; + int addrlen; + struct sockaddr_un un; + char instance_name[INSTANCE_NAME_SIZE]; + char file_name[]; +}; +typedef struct conn_retry conn_retry_t; + +// Head/tail pointers for queue of connections to be retried. +static conn_retry_t *retry_list; +static conn_retry_t *retry_list_tail; + + + +// Look up the instance given the fd +char *instance_name_by_fd(int fd) +{ + if (fd < MAX_FDS_HOST) + return instance_ptrs[fd]->name; + else + return NULL; +} + +// Look up the instance given the instance name +instance_t *instance_find_by_instance_name(char *name) +{ + int i; + for (i=0;i<=max_instance;i++) { + instance_t *instance = instances + i; + + // Skip the entry if it has been invalidated. + if (instance->fd == -1) + continue; + if (strncmp(instance->name, name, INSTANCE_NAME_SIZE) == 0) + return instance; + } + return NULL; +} + +// Look up the connection socket given the instance name +int fd_find_by_instance_name(char *name) +{ + instance_t *instance = instance_find_by_instance_name(name); + if (instance) + return instance->fd; + else + return -1; +} + + +// Get the next available slot in the instance table. +instance_t *instance_get_entry() +{ + int i; + + // Use empty element in the existing array if possible. + for (i=0;i<=max_instance;i++) { + instance_t *instance = instances+i; + if (instance->fd == -1) + return instance; + } + // No empty elements, use a new one. + if (max_instance <= MAX_INSTANCES) { + max_instance++; + return instances+max_instance; + } + PRINT_ERR("unable to add fd, already at limit\n"); + return NULL; +} + +// Return the instance to the instance table. +void instance_put_entry(instance_t *instance) +{ + int i; + instance->fd = -1; + i = (instance - instances)/sizeof(*instance); + if (i == max_instance) + max_instance--; +} + + +void init_pollfd(struct pollfd *pfd, int fd, short events) +{ + pfd->fd = fd; + pfd->events = events; + pfd->revents = 0; +} + +// Add a new file descriptor to be monitored, return the index +// in the pollfds table. +int pollfd_add(int fd, short events) +{ + int i; + // Use empty element in the existing array if possible. + for (i=0;i= MAX_FDS_HOST) { + PRINT_ERR("fd too large to store"); + return -1; + } + + // Check if the instance is already in our list; + instance_t *ptr = instance_find_by_instance_name(name); + if (ptr) { + PRINT_INFO("instance %.20s already in table", name); + return 0; + } else { + instance_t *instance; + int idx; + if (max_instance == MAX_INSTANCES-1) { + PRINT_ERR("hit max number of instances"); + return -1; + } + + idx = pollfd_add(fd, events); + if (idx == -1) + return -1; + + PRINT_DEBUG("adding instance %.20s at pollfd index %d\n", name, idx); + + // Claim a new instance struct element. + instance = instance_get_entry(); + instance->fd = fd; + instance->pollfd_index = idx; + strncpy(instance->name, name, sizeof(instance->name)); + + // Index the new element for easy access later. + instance_ptrs[fd] = instance; + + return 0; + } +} + +// The instance socket has disappeared, tear it all down. +void vio_full_disconnect(instance_t *instance) +{ + close(instance->fd); + + // Clear the lookup table entry. + instance_ptrs[instance->fd] = NULL; + + // Clear the pollfd table entry. + pollfd_del_idx(instance->pollfd_index); + + // Clear the instance table entry + instance_put_entry(instance); +} + + +/* + * Check a filename against the expected pattern for a cgcs messaging socket. + * If satisfied, writes to the passed-in instance_name arg; + * + * Returns a pointer to the instance name on success, or NULL on failure. + */ +char *file_to_instance_name(char *filename, char* instance_name) { + int rc; + rc = sscanf(filename, "cgcs.messaging.%[^.].sock", instance_name); + if (rc == 1) + return instance_name; + else + return NULL; +} + +// poll() has told us the socket has been disconnected +void disconnect_guest(int fd) +{ + instance_t *instance = instance_ptrs[fd]; + + // Sanity check + if (instance->fd != fd) + return; + + PRINT_INFO("Detected disconnect of instance '%.20s'\n", instance->name); + vio_full_disconnect(instance); +} + + + +// Inotify has told us that a file has been deleted. +static void socket_deleted(char *fn) +{ + char buf[INSTANCE_NAME_SIZE]; + char* instance_name; + instance_t * instance; + + if (!fn) + return; + + instance_name = file_to_instance_name(fn, buf); + if (!instance_name) + // Not a file we care about. + return; + + instance = instance_find_by_instance_name(instance_name); + if (!instance) { + PRINT_ERR("Couldn't find record for instance %.20s\n", instance_name); + return; + } + + PRINT_INFO("Detected deletion of vio file '%s'\n", fn); + vio_full_disconnect(instance); +} + +// Get time in nanoseconds +static unsigned long long gettime() +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (unsigned long long)ts.tv_sec * 1000000000ULL + ts.tv_nsec; +} + +// Time till next connection retry in milliseconds, for use as poll() timeout. +int next_connection_retry_interval() +{ + unsigned long long time_ns; + if (!retry_list) + return -1; // infinite timeout + + time_ns = gettime(); + if (time_ns > retry_list->next_try) + return 0; // immediate timeout + else + return (retry_list->next_try - time_ns)/1000000; // calculated timeout +} + +// We always enqueue at the list tail +static void enqueue_retry(conn_retry_t *retry) +{ + if (retry_list_tail) + retry_list_tail->next = retry; + else + retry_list = retry; + retry_list_tail = retry; + retry->next = NULL; +} + +// We always dequeue from the list head +static conn_retry_t *dequeue_retry() +{ + conn_retry_t *tmp = retry_list; + if (retry_list) + retry_list = retry_list->next; + if (!retry_list) + retry_list_tail = NULL; + return tmp; +} + +// The initial connection attempt failed, add the socket to the connection +// retry list. +static void queue_connection_retry(int sock, char *filename, + char *instance_name, struct sockaddr_un *un, int addrlen) +{ + PRINT_DEBUG("attempting to queue '%s' for connection retry\n", filename); + conn_retry_t *retry = malloc(sizeof(*retry) + strlen(filename) + 1); + if (!retry) { + PRINT_ERR("unable to allocate memory for connection retry\n"); + return; + } + + retry->sock = sock; + strncpy(retry->instance_name, instance_name, sizeof(retry->instance_name)); + strcpy(retry->file_name, filename); + retry->next_try = gettime() + 1000000000ULL; // delay for one second + memcpy(&retry->un, un, sizeof(retry->un)); + retry->addrlen = addrlen; + + enqueue_retry(retry); +} + + +// We've detected a new socket corresponding to a VM. Try and open +// a connection to it. +static int socket_added(char *filename) + { + int rc; + int addrlen; + int sock; + struct sockaddr_un un; + char pathname[PATH_MAX]; + char *instance_name; + char namebuf[INSTANCE_NAME_SIZE]; + instance_t * instance; + int flags; + + if (!filename) { + PRINT_ERR("socket_added called with null filename\n"); + return -1; + } + + instance_name = file_to_instance_name(filename, namebuf); + if (!instance_name) { + // Not a bug, just not a file we care about. + return -1; + } + + instance = instance_find_by_instance_name(instance_name); + if (instance) { + PRINT_DEBUG("'%s' is already known\n", instance_name); + return 0; + } + + snprintf(pathname, sizeof(pathname), "%s/%s", host_virtio_dir, filename); + + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { + PRINT_ERR("failed to get socket: %m\n"); + return -1; + } + + flags = fcntl(sock, F_GETFL, 0); + rc = fcntl(sock, F_SETFL, flags | O_NONBLOCK); + if (rc < 0) { + PRINT_ERR("fcntl failed: %s\n", strerror(errno)); + close(sock); + return -1; + } + + un.sun_family = AF_UNIX; + snprintf(pathname, sizeof(pathname), "%s/%s", host_virtio_dir, filename); + strcpy(un.sun_path, pathname); + addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(pathname); + + rc = connect(sock, (struct sockaddr *)&un, addrlen); + if (rc < 0) { + if (errno == ECONNREFUSED || errno == EAGAIN) { + // temporary glitch, retry later + queue_connection_retry(sock, filename, instance_name, &un, addrlen); + return 0; + } else { + PRINT_ERR("connect to '%s' failed: %m\n", pathname); + close(sock); + return -1; + } + } + + PRINT_INFO("Connection accepted to '%s'\n", pathname); + + rc = add_instance(sock, POLLIN, instance_name); + if (rc < 0) { + close(sock); + return -1; + } + + PRINT_INFO("added instance at '%s'\n", filename); + PRINT_INFO("registered instance sock %d\n", sock); + return 0; +} + +// process any outstanding connection retries +void process_connection_retries() +{ + int rc; + conn_retry_t *retry; + unsigned long long time; + + while (retry_list) { + time = gettime(); + if (time < retry_list->next_try) + return; + + retry = dequeue_retry(); + PRINT_DEBUG("dequeued '%s' for connect() retry\n", retry->file_name); + rc = connect(retry->sock, (struct sockaddr *)&retry->un, + retry->addrlen); + if (rc < 0) { + if (errno == ECONNREFUSED || errno == EAGAIN) { + // VM hasn't registered socket yet, retry later + retry->next_try = time + 1000000000ULL; //try again in a second + PRINT_DEBUG("connection for '%s' refused, enqueuing for retry\n", + retry->file_name); + enqueue_retry(retry); + continue; + } else { + PRINT_ERR("connect() for '%s' failed: %m\n", retry->file_name); + close(retry->sock); + free(retry); + continue; + } + } + + PRINT_INFO("Connection accepted to '%s'\n", retry->file_name); + + rc = add_instance(retry->sock, POLLIN, retry->instance_name); + if (rc < 0) { + close(retry->sock); + free(retry); + continue; + } + + // Success path + PRINT_INFO("registered instance sock %d\n", retry->sock); + + PRINT_DEBUG("freeing retry struct for '%s'\n", retry->un.sun_path); + free(retry); + } +} + +void handle_inotify_event() +{ + int len; + int bufsize = sizeof(struct inotify_event) + PATH_MAX + 1; + char buf[bufsize]; + int offset = 0; + + len = read(inotify_fd, buf, bufsize); + if (len < 0) + return; + + // There can be multiple events returned in a single buffer, need + // to process all of them. + while (len-offset > sizeof(struct inotify_event)) { + struct inotify_event *in_event_p = (struct inotify_event *)(buf+offset); + + if ((in_event_p->mask & IN_CREATE) == IN_CREATE) { + PRINT_DEBUG("inotify creation event for '%s'\n", in_event_p->name); + socket_added(in_event_p->name); + } else if ((in_event_p->mask & IN_DELETE) == IN_DELETE) { + PRINT_DEBUG("inotify deletion event for '%s'\n", in_event_p->name); + socket_deleted(in_event_p->name); + } + + // Now skip to the next inotify event if there is one + offset += (sizeof(struct inotify_event) + in_event_p->len); + } +} + +static void server_scan() +{ + DIR *dirp; + struct dirent entry; + struct dirent *result; + int rc; + + dirp = opendir(host_virtio_dir); + if (!dirp) { + PRINT_ERR("opendir %s failed: %m\n", host_virtio_dir); + return; + } + + while(0 == readdir_r(dirp, &entry, &result)) { + if (!result) + break; + + rc = socket_added(result->d_name); + if (rc == 0) { + PRINT_DEBUG("added '%s'\n", result->d_name); + } + } + + closedir(dirp); +} + + +int server_scan_init() +{ + event_size = sizeof(struct inotify_event) + PATH_MAX + 1; + event_ptr = malloc(event_size); + inotify_fd = inotify_init(); + if (inotify_fd < 0) { + PRINT_ERR("inotify_init failed: %m\n"); + return -1; + } + + inotify_watch_id = inotify_add_watch(inotify_fd, host_virtio_dir, IN_CREATE | IN_DELETE); + if (inotify_watch_id < 0) { + PRINT_ERR("vio_add_watch failed: %s\n", strerror(errno)); + close(inotify_fd); + inotify_fd = -1; + return -1; + } + + pollfd_add(inotify_fd, POLLIN); + PRINT_INFO("registered vio inotify sock %d\n", inotify_fd); + + // Do initial scan to pick up existing instance connections + server_scan(); + + return 0; +} diff --git a/guest-comm/host-guest-comm-2.0/host_instance_mgmt.h b/guest-comm/host-guest-comm-2.0/host_instance_mgmt.h new file mode 100644 index 00000000..f501945f --- /dev/null +++ b/guest-comm/host-guest-comm-2.0/host_instance_mgmt.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HOST_INSTANCE_MGMT_H +#define HOST_INSTANCE_MGMT_H + +#include "host_guest_msg_type.h" + +// Main poll structure management stuff +extern int nfds; +extern struct pollfd pollfds[MAX_INSTANCES]; +int pollfd_add(int fd, short events); + +// Used to scan for added/deleted instances +extern int inotify_fd; + +// Server connection added/deleted +void handle_inotify_event(); + +// Set up inotify-based monitoring +int server_scan_init(void); + +// Map from connection fd to instance name +char *instance_name_by_fd(int fd); + +// Map from instance name to connection fd +int fd_find_by_instance_name(char *name); + +// Handle disconnection of guest +void disconnect_guest(int fd); + +// Handle connection retries +void process_connection_retries(); + +// Time till next connection retry in milliseconds. +int next_connection_retry_interval(); + + +#endif diff --git a/guest-comm/host-guest-comm-2.0/lib/.gitignore b/guest-comm/host-guest-comm-2.0/lib/.gitignore new file mode 100644 index 00000000..5e7d2734 --- /dev/null +++ b/guest-comm/host-guest-comm-2.0/lib/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/guest-comm/host-guest-comm-2.0/lib_guest_host_msg.c b/guest-comm/host-guest-comm-2.0/lib_guest_host_msg.c new file mode 100644 index 00000000..558bafe7 --- /dev/null +++ b/guest-comm/host-guest-comm-2.0/lib_guest_host_msg.c @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +/* This implements a library to be used for guest/host clients to interface with + * non-standard functionality on the host/guest using the backchannel + * communications pathway. (The same library can be used for both directions.) + * + * The general idea is that everything that goes through this is multiplexed + * over a single unix socket so that the guest app only needs to monitor one + * socket for activity. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "guest_host_msg.h" +#include "host_guest_msg_type.h" + + +/* Send a message to an address on the host. + * Returns 0 on success. + * A negative return value indicates an error of some kind. + */ +int gh_send_msg(gh_info_t *info, const char *dest_addr, const char *msg) +{ + int rc; + + //parse msg data + struct json_object *jobj_data = json_tokener_parse(msg); + if (jobj_data == NULL) { + snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "failed to parse msg"); + return -1; + } + + struct json_object *jobj_outmsg = json_object_new_object(); + if (jobj_outmsg == NULL) { + snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "failed to allocate json object for outmsg"); + json_object_put(jobj_data); + return -1; + } + + json_object_object_add(jobj_outmsg, DATA, jobj_data); + json_object_object_add(jobj_outmsg, VERSION, json_object_new_int(CUR_VERSION)); + json_object_object_add(jobj_outmsg, DEST_ADDR, json_object_new_string(dest_addr)); + + + const char *outmsg = json_object_to_json_string_ext(jobj_outmsg, JSON_C_TO_STRING_PLAIN); + int msglen = strlen(outmsg); + + rc = sendto(info->sock, outmsg, msglen, 0, (struct sockaddr *) &info->svaddr, + info->svaddrlen); + if (rc != msglen) { + if (rc > 0) { + snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "sendto returned %d, expected %d", + rc, msglen); + } else + snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "sendto: %m"); + goto failed; + } + + json_object_put(jobj_outmsg); + return 0; +failed: + json_object_put(jobj_outmsg); + return -1; +} + + + +/* Read a message from the socket and process it. */ +int gh_process_msg(gh_info_t *info) +{ + char buf[HOST_GUEST_BUFSIZE]; + int len; + + len = recv(info->sock, buf, sizeof(buf), 0); + if (len == -1) { + if (errno == EAGAIN) + return 0; + else { + snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "error receiving msg: %m"); + return -1; + } + } + + struct json_object *jobj_msg = json_tokener_parse(buf); + if (jobj_msg == NULL) { + snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "failed to parse msg"); + return -1; + } + + // parse version + struct json_object *jobj_version; + if (!json_object_object_get_ex(jobj_msg, VERSION, &jobj_version)) { + snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "failed to parse version"); + goto failed; + } + int version = json_object_get_int(jobj_version); + + if (version != CUR_VERSION) { + snprintf(info->errorbuf, sizeof(info->errorbuf)-1, + "invalid version %d, expecting %d", version, CUR_VERSION); + goto failed; + } + + // parse source address + struct json_object *jobj_source_addr; + if (!json_object_object_get_ex(jobj_msg, SOURCE_ADDR, &jobj_source_addr)) { + snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "failed to parse source_addr"); + goto failed; + } + const char *source_addr = json_object_get_string(jobj_source_addr); + + // parse data. data is a json object that is nested inside the msg + struct json_object *jobj_data; + if (!json_object_object_get_ex(jobj_msg, DATA, &jobj_data)) { + snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "failed to parse data"); + goto failed; + } + + if (info->gh_msg_handler) + info->gh_msg_handler(source_addr, jobj_data); + + json_object_put(jobj_msg); + return 0; + +failed: + json_object_put(jobj_msg); + return -1; +} + + +/* Allocate socket, set up callbacks, etc. This must be called once before + * any other API calls. "addr" is a null-terminated string of 16 chars or less + * (including the null) that is unique within this guest. "info" is the address + * of a value-result pointer that will be updated during the call. + * + * On success returns a socket and "info" is updated to point to an allocated chunk of memory. + * On error will return -1. If it was unable to allocate memory then "info" will be + * NULL. If it was able to allocate memory but something else failed then "info" will + * be non-NULL and you can call gh_get_error() to get an error message. + */ + +int gh_init(gh_msg_handler_t msg_handler, char *addr, gh_info_t **in_info) +{ + int flags; + int addrlen; + struct sockaddr_un cliaddr; + gh_info_t *info; + + *in_info = malloc(sizeof(**in_info)); + if (!*in_info) + /* unable to allocate memory */ + return -1; + + info = *in_info; + + /* socket for talking to guest agent */ + info->sock = socket(AF_UNIX, SOCK_DGRAM, 0); + if (info->sock == -1) { + snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "unable to open socket: %m"); + goto free_out; + } + + flags = fcntl(info->sock, F_GETFL, 0); + fcntl(info->sock, F_SETFL, flags | O_NONBLOCK); + + /* our address */ + memset(&cliaddr, 0, sizeof(struct sockaddr_un)); + cliaddr.sun_family = AF_UNIX; + cliaddr.sun_path[0] = '\0'; + strncpy(cliaddr.sun_path+1, addr, + sizeof(cliaddr.sun_path) - 2); + addrlen = sizeof(sa_family_t) + strlen(addr) + 1; + + if (bind(info->sock, (struct sockaddr *) &cliaddr, addrlen) == -1) { + snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "unable to bind socket: %m"); + goto close_out; + } + + /* guest agent address */ + memset(&info->svaddr, 0, sizeof(struct sockaddr_un)); + info->svaddr.sun_family = AF_UNIX; + info->svaddr.sun_path[0] = '\0'; + strncpy(info->svaddr.sun_path+1, AGENT_ADDR, sizeof(info->svaddr.sun_path) - 2); + info->svaddrlen = sizeof(sa_family_t) + strlen(AGENT_ADDR) + 1; + + /* set up callback pointers */ + info->gh_msg_handler = msg_handler; + + return info->sock; + +close_out: + close(info->sock); +free_out: + free(info); + return -1; +} + +/* Provide access to the error message if the most recent call failed. */ +char *gh_get_error(gh_info_t *info) +{ + return info->errorbuf; +} diff --git a/guest-comm/host-guest-comm-2.0/lib_host_guest_msg.c b/guest-comm/host-guest-comm-2.0/lib_host_guest_msg.c new file mode 100644 index 00000000..830fc18c --- /dev/null +++ b/guest-comm/host-guest-comm-2.0/lib_host_guest_msg.c @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +/* This implements a library to be used for host clients to interface with + * non-standard functionality on the guest using the backchannel + * communications pathway. + * + * The general idea is that everything that goes through this is multiplexed + * over a single unix socket so that the guest app only needs to monitor one + * socket for activity. + * + * This is almost identical to lib_guest_host_msg.c but some data structures + * are different. Might be possible to extract some common stuff. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "host_guest_msg_type.h" +#include "host_guest_msg.h" + + +/* Send a message to an address on the host. + * Returns 0 on success. + * A negative return value indicates an error of some kind. + */ +int hg_send_msg(hg_info_t *info, const char *dest_addr, const char *dest_instance, + const char *msg) +{ + int rc; + + //parse msg data + struct json_object *jobj_data = json_tokener_parse(msg); + if (jobj_data == NULL) { + snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "failed to parse msg"); + return -1; + } + + struct json_object *jobj_outmsg = json_object_new_object(); + if (jobj_outmsg == NULL) { + snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "failed to allocate json object for outmsg"); + json_object_put(jobj_data); + return -1; + } + + json_object_object_add(jobj_outmsg, DATA, jobj_data); + json_object_object_add(jobj_outmsg, VERSION, json_object_new_int(CUR_VERSION)); + json_object_object_add(jobj_outmsg, DEST_ADDR, json_object_new_string(dest_addr)); + json_object_object_add(jobj_outmsg, DEST_INSTANCE, json_object_new_string(dest_instance)); + + const char *outmsg = json_object_to_json_string_ext(jobj_outmsg, JSON_C_TO_STRING_PLAIN); + int msglen = strlen(outmsg); + + rc = sendto(info->sock, outmsg, msglen, 0, (struct sockaddr *) &info->svaddr, + info->svaddrlen); + if (rc != msglen) { + if (rc > 0) { + snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "sendto returned %d, expected %d", + rc, msglen); + } else + snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "sendto: %m"); + goto failed; + } + + json_object_put(jobj_outmsg); + return 0; +failed: + json_object_put(jobj_outmsg); + return -1; +} + + + +/* Read a message from the socket and process it. */ +int hg_process_msg(hg_info_t *info) +{ + char buf[HOST_GUEST_BUFSIZE]; + int len; + + len = recv(info->sock, buf, sizeof(buf), 0); + if (len == -1) { + if (errno == EAGAIN) + return 0; + else { + snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "error receiving msg: %m"); + return -1; + } + } + + struct json_object *jobj_msg = json_tokener_parse(buf); + if (jobj_msg == NULL) { + snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "failed to parse msg"); + return -1; + } + + // parse version + struct json_object *jobj_version; + if (!json_object_object_get_ex(jobj_msg, VERSION, &jobj_version)) { + snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "failed to parse version"); + goto failed; + } + int version = json_object_get_int(jobj_version); + + if (version != CUR_VERSION) { + snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "invalid version"); + goto failed; + } + + // parse source address + struct json_object *jobj_source_addr; + if (!json_object_object_get_ex(jobj_msg, SOURCE_ADDR, &jobj_source_addr)) { + snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "failed to parse source_addr"); + goto failed; + } + const char *source_addr = json_object_get_string(jobj_source_addr); + + // parse source instance + struct json_object *jobj_source_instance; + if (!json_object_object_get_ex(jobj_msg, SOURCE_INSTANCE, &jobj_source_instance)) { + snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "failed to parse source_instance"); + goto failed; + } + const char *source_instance = json_object_get_string(jobj_source_instance); + + // parse data. data is a json object that is nested inside the msg + struct json_object *jobj_data; + if (!json_object_object_get_ex(jobj_msg, DATA, &jobj_data)) { + snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "failed to parse data"); + goto failed; + } + + if (info->hg_msg_handler) + info->hg_msg_handler(source_addr, source_instance, jobj_data); + + json_object_put(jobj_msg); + return 0; + +failed: + json_object_put(jobj_msg); + return -1; +} + + +/* Allocate socket, set up callbacks, etc. This must be called once before + * any other API calls. "addr" is a null-terminated string of 16 chars or less + * (including the null) that is unique within this guest. "info" is the address + * of a value-result pointer that will be updated during the call. + * + * On success returns a socket and "info" is updated to point to an allocated chunk of memory. + * On error will return -1. If it was unable to allocate memory then "info" will be + * NULL. If it was able to allocate memory but something else failed then "info" will + * be non-NULL and you can call hg_get_error() to get an error message. + */ + +int hg_init(hg_msg_handler_t msg_handler, char *addr, hg_info_t **in_info) +{ + int flags; + int addrlen; + struct sockaddr_un cliaddr; + hg_info_t *info; + + *in_info = malloc(sizeof(**in_info)); + if (!*in_info) + /* unable to allocate memory */ + return -1; + + info = *in_info; + + /* socket for talking to guest agent */ + info->sock = socket(AF_UNIX, SOCK_DGRAM, 0); + if (info->sock == -1) { + snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "unable to open socket: %m"); + goto free_out; + } + + flags = fcntl(info->sock, F_GETFL, 0); + fcntl(info->sock, F_SETFL, flags | O_NONBLOCK); + + /* our address */ + memset(&cliaddr, 0, sizeof(struct sockaddr_un)); + cliaddr.sun_family = AF_UNIX; + cliaddr.sun_path[0] = '\0'; + strncpy(cliaddr.sun_path+1, addr, + sizeof(cliaddr.sun_path) - 2); + addrlen = sizeof(sa_family_t) + strlen(addr) + 1; + + if (bind(info->sock, (struct sockaddr *) &cliaddr, addrlen) == -1) { + snprintf(info->errorbuf, sizeof(info->errorbuf)-1, "unable to bind socket: %m"); + goto close_out; + } + + /* guest agent address */ + memset(&info->svaddr, 0, sizeof(struct sockaddr_un)); + info->svaddr.sun_family = AF_UNIX; + info->svaddr.sun_path[0] = '\0'; + strncpy(info->svaddr.sun_path+1, AGENT_ADDR, sizeof(info->svaddr.sun_path) - 2); + info->svaddrlen = sizeof(sa_family_t) + strlen(AGENT_ADDR) + 1; + + /* set up callback pointers */ + info->hg_msg_handler = msg_handler; + + return info->sock; + +close_out: + close(info->sock); +free_out: + free(info); + return -1; +} + +/* Provide access to the error message if the most recent call failed. */ +char *hg_get_error(hg_info_t *info) +{ + return info->errorbuf; +} diff --git a/guest-comm/host-guest-comm-2.0/misc.h b/guest-comm/host-guest-comm-2.0/misc.h new file mode 100644 index 00000000..e8434ed3 --- /dev/null +++ b/guest-comm/host-guest-comm-2.0/misc.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CGCS_MSG_MISC_H +#define CGCS_MSG_MISC_H + +#include + +#define PRINT(level, format, ...) \ + syslog(level, "%s(%d): " format, __FILE__, __LINE__, ##__VA_ARGS__) + +#define PRINT_ERR(format, ...) PRINT(LOG_ERR, format, ##__VA_ARGS__) +#define PRINT_INFO(format, ...) PRINT(LOG_INFO, format, ##__VA_ARGS__) +#define PRINT_DEBUG(format, ...) PRINT(LOG_DEBUG, format, ##__VA_ARGS__) + +#endif diff --git a/guest-comm/host-guest-comm-2.0/obj/.gitignore b/guest-comm/host-guest-comm-2.0/obj/.gitignore new file mode 100644 index 00000000..5e7d2734 --- /dev/null +++ b/guest-comm/host-guest-comm-2.0/obj/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/guest-comm/host-guest-comm-2.0/scripts/guest-agent.service b/guest-comm/host-guest-comm-2.0/scripts/guest-agent.service new file mode 100644 index 00000000..f37042be --- /dev/null +++ b/guest-comm/host-guest-comm-2.0/scripts/guest-agent.service @@ -0,0 +1,12 @@ +[Unit] +Description=Titanium Cloud Host Guest Messaging Agent +After=cloud-init.service + +[Service] +ExecStart=/usr/sbin/guest_agent +Type=simple +Restart=always + +[Install] +WantedBy=multi-user.target + diff --git a/guest-comm/host-guest-comm-2.0/scripts/host_agent b/guest-comm/host-guest-comm-2.0/scripts/host_agent new file mode 100644 index 00000000..78648dca --- /dev/null +++ b/guest-comm/host-guest-comm-2.0/scripts/host_agent @@ -0,0 +1,78 @@ +#!/bin/sh +# +# Copyright(c) 2013-2016, Wind River Systems, Inc. All rights reserved. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Wind River Systems nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +DAEMON=/usr/sbin/host_agent +NAME=host_agent +DESC="agent to communicate with guest" +ARGS= +PIDFILE=/var/run/host_agent.pid + +test -f $DAEMON || exit 0 + +case "$1" in + start) + echo -n "starting $DESC: $NAME... " + test -d /var/lib/misc/ || mkdir /var/lib/misc/ + start-stop-daemon -S -m -b -x $DAEMON --pidfile=$PIDFILE -- $ARGS + echo "done." + ;; + stop) + echo -n "stopping $DESC: $NAME... " + start-stop-daemon -K -x $DAEMON --pidfile=$PIDFILE + echo "done." + ;; + status) + echo -n "$NAME " + start-stop-daemon -q -K -t -x $DAEMON --pidfile=$PIDFILE + RET=$? + if [ "$RET" = "0" ]; then + PID=`cat $PIDFILE` + echo "($PID) is running" + else + echo "is not running" + # For lsb compliance return 3 if process not running + exit 3 + fi + ;; + restart) + echo "restarting $DESC: $NAME... " + $0 stop + $0 start + echo "done." + ;; + *) + echo "Usage: $0 {start|stop|status|restart}" + exit 1 + ;; +esac + +exit 0 diff --git a/guest-comm/host-guest-comm-2.0/scripts/host_agent.conf b/guest-comm/host-guest-comm-2.0/scripts/host_agent.conf new file mode 100644 index 00000000..c6f2f47d --- /dev/null +++ b/guest-comm/host-guest-comm-2.0/scripts/host_agent.conf @@ -0,0 +1,24 @@ +[process] +process = host_agent +pidfile = /var/run/host_agent.pid +script = /etc/init.d/host_agent +style = lsb ; ocf or lsb +severity = major ; minor, major, critical +restarts = 3 ; restart retries before error assertion +interval = 1 ; number of seconds to wait between restarts +debounce = 20 ; number of seconds that a process needs to remain + ; running before degrade is removed and retry count + ; is cleared. +startuptime = 5 ; Seconds to wait after process start before starting the debounce monitor +mode = passive ; Monitoring mode: passive (default or if mode field is missing) or active + ; passive: process death monitoring (default: always) + ; active: heartbeat monitoring, i.e. request / response messaging + ; ignore: do not monitor or stop monitoring process +subfunction = compute ; Optional label. + ; Manage this process in the context of a combo host subfunction + ; Choices: compute or storage. + ; when specified pmond will wait for + ; /var/run/.compute_config_complete or + ; /var/run/.storage_config_complete + ; ... before managing this process with the specified subfunction + ; Excluding this label will cause this process to be managed by default on startup diff --git a/guest-comm/host-guest-comm-2.0/scripts/host_agent.service b/guest-comm/host-guest-comm-2.0/scripts/host_agent.service new file mode 100644 index 00000000..7e58b4c1 --- /dev/null +++ b/guest-comm/host-guest-comm-2.0/scripts/host_agent.service @@ -0,0 +1,13 @@ +[Unit] +Description=Titanium Cloud Host Guest Messaging Agent +After=config.service +Before=pmon.service + +[Service] +Type=forking +ExecStart=/etc/init.d/host_agent start +ExecStop=/etc/init.d/host_agent stop +PIDFile=/var/run/host_agent.pid + +[Install] +WantedBy=multi-user.target diff --git a/guest-comm/host-guest-comm-2.0/server_group.c b/guest-comm/host-guest-comm-2.0/server_group.c new file mode 100644 index 00000000..11033962 --- /dev/null +++ b/guest-comm/host-guest-comm-2.0/server_group.c @@ -0,0 +1,497 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +/* This implements a library to be used for guest clients to interface with + * non-standard functionality on the host using the backchannel + * communications pathway. + * + * The general idea is that everything that goes through this is multiplexed + * over a single unix socket so that the guest app only needs to monitor one + * socket for activity. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "server_group.h" +#include "host_guest_msg_type.h" + +/* unix socket abstract namespace address of guest agent */ +#define GUEST_CLIENT_ADDR "cgcs.server_grp" + +#define HOST_GUEST_BUFSIZE 4096 // max size of message that can be received + + +/* Server Group message is encoded in JSON format. + The message sent out to UNIX socket is a null-terminated JSON format string + without embedded newlines. + + Format: + {key:value,key:value,..., key:value} + + Key/value pairs: + "version": - version of the interface + "type": < string> - one of these types “broadcast", "notification", + "status_query", "status_response", + "status_response_done” + + "req": - sequence number for status_query/status_response/ + status_response_done messages + + "source_instance“: - source instance that send out the broadcast + "data": - message content for broadcast, notification, + status_query and status_response messages. + Must be a null-terminated string without embedded newlines. + + + Message Types: + broadcast - incoming or outgoing + notification - incoming, state change of other servers within the + server group + status_query - outgoing, query the current state of all servers + within the server group + status_response - incoming, one or more responses to the status_query + status_response_done - incoming, last response to the status query + +*/ + +// server group message type +#define GRP_BROADCAST "broadcast" // broadcast message from another server +#define GRP_NOTIFICATION "notification" // notification of server state change +#define GRP_STATUS_QUERY "status_query" // query of status of all servers in group +#define GRP_STATUS_RESP "status_response" // query response msg (could be several) +#define GRP_STATUS_RESP_DONE "status_response_done" // query response done msg (no data) +#define GRP_NACK "nack" // nack msg indicating parse or protocol error from host + + +/* Header for incoming server group messages. The exact contents will differ + * depending on message type. + * + * "len" is the overall length including header. + * + * For messages with a TYPE of GRP_BROADCAST only, the "sinstance" field + * will contain the instance name of the instance that sent the broadcast, and + * DATA will contain the message that was sent. + * + */ +static int sock; // socket for talking to guest agent +static struct sockaddr_un svaddr; // address of guest agent +static int svaddrlen; // length of guest agent address + +static unsigned int status_seqnum; // status query sequence number +static char *status_buf; // status query reassembly buffer +static unsigned long status_size; // current status buffer size +static unsigned long status_len; // currently used buffer length + +#define ERRORSIZE 400 +static char errorbuf[ERRORSIZE]; + +static sg_broadcast_msg_handler_t sg_broadcast_msg_callback; +static sg_notification_msg_handler_t sg_notification_msg_callback; +static sg_status_msg_handler_t sg_status_msg_callback; + + +/* Generic routine to send a server group message down to the host. */ +int sg_send_host_msg(const char *msg_type, int seq, const char *data) +{ + int rc; + + struct json_object *jobj_data = json_object_new_object(); + if (jobj_data == NULL) { + snprintf(errorbuf, ERRORSIZE-1, "failed to allocate json object for data"); + return -1; + } + + json_object_object_add(jobj_data, VERSION, json_object_new_int(CUR_VERSION)); + json_object_object_add(jobj_data, MSG_TYPE, json_object_new_string(msg_type)); + if (!strcmp(msg_type, GRP_STATUS_QUERY)) { + json_object_object_add(jobj_data, SEQ, json_object_new_int(seq)); + } + + if (data) { + json_object_object_add(jobj_data, DATA, json_object_new_string(data)); + } + + struct json_object *jobj_outmsg = json_object_new_object(); + if (jobj_outmsg == NULL) { + snprintf(errorbuf, ERRORSIZE-1, "failed to allocate json object for outmsg"); + json_object_put(jobj_data); + return -1; + } + + json_object_object_add(jobj_outmsg, DATA, jobj_data); + json_object_object_add(jobj_outmsg, VERSION, json_object_new_int(CUR_VERSION)); + /* This is a known address that nova-compute is listening on */ + json_object_object_add(jobj_outmsg, DEST_ADDR, json_object_new_string(GUEST_CLIENT_ADDR)); + + const char *outmsg = json_object_to_json_string_ext(jobj_outmsg, JSON_C_TO_STRING_PLAIN); + int msglen = strlen(outmsg); + + rc = sendto(sock, outmsg, msglen, 0, (struct sockaddr *) &svaddr, svaddrlen); + if (rc != msglen) { + if (rc > 0) { + snprintf(errorbuf, ERRORSIZE-1, "sendto returned %d, expected %d", + rc, msglen); + } else + snprintf(errorbuf, ERRORSIZE-1, "sendto: %m"); + json_object_put(jobj_outmsg); + return -1; + } + + json_object_put(jobj_outmsg); + return 0; +} + +/* Send a message to all other servers in our server group. */ +int sg_msg_broadcast(void *msg, unsigned int len) +{ + return sg_send_host_msg(GRP_BROADCAST, 0, (const char*)msg); +} + +/* Ensure the status buffer is at least as big as the specified size. + * Call realloc() if necessary to grow the buffer. + */ +int ensure_status_buf_size(unsigned int size) +{ + if (size > status_size) { + /* need to grow the buffer */ + void *ptr = realloc(status_buf, size); + if (!ptr) { + /* hopefully shouldn't happen */ + snprintf(errorbuf, ERRORSIZE-1, + "unable to realloc buffer to size %u", size); + + /* give up on current status query */ + status_seqnum++; + status_len = 0; + return -1; + } + status_buf = ptr; + status_size = size; + } + return 0; +} + +/* Request current status of all servers in server group. Due to limitations in + * the current implementation of host-guest comm channel agents we receive + * information on one server per response, then a final "done" message. This + * could be changed if we fix the host-guest comm channel to handle arbitrarily + * large messages. + * + * Yes, the current design is not the most robust...might want to consider adding + * an indication of how many servers we expect data for and which one we're on + * in case we lose a message or something. Better fix might be to just fix the + * host-guest comm channel to handle arbitrarily large messages. + */ +int sg_request_status() +{ + int rc; + /* If we were still receiving status updates from a previous query this + * will cause them to get dropped. + */ + status_seqnum++; + status_len = 0; + + /* Ensure we have room for an empty list otherwise give up. + * Start with a decent size buffer to minimize reallocs. + */ + if (ensure_status_buf_size(4096) != 0) + return -1; + + rc = sg_send_host_msg(GRP_STATUS_QUERY, status_seqnum, NULL); + if (rc == 0) { + /* start a list in the buffer */ + status_buf[0] = '['; + status_len = 1; + } + return rc; +} + +/* Handle a response to the status query. This should contain the current + * status of a single server. We add it to the data accumulating in the buffer. + */ +int handle_status_resp(unsigned int seqnum, const char *msg, unsigned int len) +{ + if (seqnum != status_seqnum) { + snprintf(errorbuf, ERRORSIZE-1, + "status resp seqnum %u doesn't match expected %u", + seqnum, status_seqnum); + return -1; + } + + /* Ensure we have room for new data otherwise give up. + * Add an extra byte for comma between server data. + */ + if (ensure_status_buf_size(status_len + len + 1) != 0) + return -1; + + /* comma-separate the status for each instance */ + if (status_len != 1) { + status_buf[status_len] = ','; + status_len++; + } + + /* Now copy the server status into the buffer */ + memcpy((status_buf + status_len), msg, len); + status_len += len; + return 0; +} + +/* This tells us that we've received all the server status messages + * so we can call the callback and then reset things for the next one. + */ +int handle_status_resp_done(unsigned int seqnum) +{ + if (seqnum != status_seqnum) { + snprintf(errorbuf, ERRORSIZE-1, + "status resp done seqnum %u doesn't match expected %u", + seqnum, status_seqnum); + return -1; + } + + /* Ensure we have room for list terminator otherwise give up */ + if (ensure_status_buf_size(status_len + 1) != 0) + return -1; + + if (status_buf) { + /* terminate the list */ + status_buf[status_len] = ']'; + status_len++; + + if (sg_status_msg_callback) + sg_status_msg_callback(status_buf, status_len); + } + + /* reset the buffer */ + status_len = 0; + /* bump seqnum just in case */ + status_seqnum++; + return 0; +} + + +int dispatch_sg_msg(json_object *job_msg) +{ + int rc = 0; + + struct json_object *jobj_msg_type; + if (!json_object_object_get_ex(job_msg, MSG_TYPE, &jobj_msg_type)) { + snprintf(errorbuf, ERRORSIZE-1, "failed to parse msg_type"); + return -1; + } + const char *msg_type = json_object_get_string(jobj_msg_type); + + struct json_object *jobj_data; + const char *data; + + // type GRP_STATUS_RESP_DONE message does not have a data field + if (!strcmp(msg_type, GRP_BROADCAST) || + !strcmp(msg_type, GRP_NOTIFICATION) || + !strcmp(msg_type, GRP_STATUS_RESP)) { + if (!json_object_object_get_ex(job_msg, DATA, &jobj_data)) { + snprintf(errorbuf, ERRORSIZE-1, "failed to parse data for type %s", msg_type); + return -1; + } + data = json_object_get_string(jobj_data); + } + + if (!strcmp(msg_type, GRP_BROADCAST)) { + struct json_object *jobj_source_instance; + if (!json_object_object_get_ex(job_msg, SOURCE_INSTANCE, &jobj_source_instance)) { + snprintf(errorbuf, ERRORSIZE-1, "failed to parse source_instance for type %s", msg_type); + return -1; + } + const char *source_instance = json_object_get_string(jobj_source_instance); + + if (sg_broadcast_msg_callback) + sg_broadcast_msg_callback((char *)source_instance, (void *)data, strlen(data)); + } + else if (!strcmp(msg_type, GRP_NOTIFICATION)) { + if (sg_notification_msg_callback) + sg_notification_msg_callback((void *)data, strlen(data)); + } + else if ((!strcmp(msg_type, GRP_STATUS_RESP)) || (!strcmp(msg_type, GRP_STATUS_RESP_DONE))) { + struct json_object *jobj_seq; + if (!json_object_object_get_ex(job_msg, SEQ, &jobj_seq)) { + snprintf(errorbuf, ERRORSIZE-1, "failed to parse seq for type %s", msg_type); + return -1; + } + int seq = json_object_get_int(jobj_seq); + + if (!strcmp(msg_type, GRP_STATUS_RESP)) { + rc = handle_status_resp(seq, data, strlen(data)); + } + else if (!strcmp(msg_type, GRP_STATUS_RESP_DONE)) { + rc = handle_status_resp_done(seq); + } + } + else if (!strcmp(msg_type, GRP_NACK)) { + struct json_object *jobj_log_msg; + if (!json_object_object_get_ex(job_msg, LOG_MSG, &jobj_log_msg)) { + snprintf(errorbuf, ERRORSIZE-1, "Nack: failed to parse log_msg"); + } + const char *log_msg = json_object_get_string(jobj_log_msg); + snprintf(errorbuf, ERRORSIZE-1, "Nack received, error message from host: %s", log_msg); + return -1; + } else { + snprintf(errorbuf, ERRORSIZE-1, "unknown server group message type %s", msg_type); + return -1; + } + return rc; +} + + +/* Read a message from the socket and process it. */ +int process_sg_msg() +{ + char buf[HOST_GUEST_BUFSIZE]; + int len; + int rc = -1; + + len = recv(sock, buf, sizeof(buf), 0); + if (len == -1) { + if (errno == EAGAIN) + return 0; + else { + snprintf(errorbuf, ERRORSIZE-1, "error receiving msg: %m"); + return -1; + } + } + + struct json_object *jobj_msg = json_tokener_parse(buf); + if (jobj_msg == NULL) { + snprintf(errorbuf, ERRORSIZE-1, "failed to parse msg"); + return -1; + } + + // parse version + struct json_object *jobj_version; + if (!json_object_object_get_ex(jobj_msg, VERSION, &jobj_version)) { + snprintf(errorbuf, ERRORSIZE-1, "failed to parse version"); + goto done; + } + int version = json_object_get_int(jobj_version); + + if (version != CUR_VERSION) { + snprintf(errorbuf, ERRORSIZE-1, "invalid version"); + goto done; + } + + // parse source address + struct json_object *jobj_source_addr; + if (!json_object_object_get_ex(jobj_msg, SOURCE_ADDR, &jobj_source_addr)) { + snprintf(errorbuf, ERRORSIZE-1, "failed to parse source_addr"); + goto done; + } + const char *source_addr = json_object_get_string(jobj_source_addr); + + /* check the host sender */ + if (strcmp(source_addr, GUEST_CLIENT_ADDR) != 0) { + snprintf(errorbuf, ERRORSIZE-1, "unknown sender address %s", source_addr); + goto done; + } + + // parse data. data is a json object that is nested inside the msg + struct json_object *jobj_data; + if (!json_object_object_get_ex(jobj_msg, DATA, &jobj_data)) { + snprintf(errorbuf, ERRORSIZE-1, "failed to parse data"); + goto done; + } + + rc = dispatch_sg_msg(jobj_data); + +done: + json_object_put(jobj_msg); + return rc; +} + + +/* This needs to be called first to initialize sockets, buffers, + * callback pointers, etc. + */ +int init_sg(sg_broadcast_msg_handler_t broadcast_handler, + sg_notification_msg_handler_t notification_handler, + sg_status_msg_handler_t status_handler) +{ + int flags; + int addrlen; + struct sockaddr_un cliaddr; + + // socket for talking to guest agent + sock = socket(AF_UNIX, SOCK_DGRAM, 0); + if (sock == -1) { + snprintf(errorbuf, ERRORSIZE-1, "unable to open socket: %m"); + return -1; + } + + flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, flags | O_NONBLOCK); + + /* our address */ + memset(&cliaddr, 0, sizeof(struct sockaddr_un)); + cliaddr.sun_family = AF_UNIX; + cliaddr.sun_path[0] = '\0'; + strncpy(cliaddr.sun_path+1, GUEST_CLIENT_ADDR, + sizeof(cliaddr.sun_path) - 2); + addrlen = sizeof(sa_family_t) + strlen(GUEST_CLIENT_ADDR) + 1; + + if (bind(sock, (struct sockaddr *) &cliaddr, addrlen) == -1) { + snprintf(errorbuf, ERRORSIZE-1, "unable to bind socket: %m"); + return -1; + } + + /* guest agent address */ + memset(&svaddr, 0, sizeof(struct sockaddr_un)); + svaddr.sun_family = AF_UNIX; + svaddr.sun_path[0] = '\0'; + strncpy(svaddr.sun_path+1, AGENT_ADDR, sizeof(svaddr.sun_path) - 2); + svaddrlen = sizeof(sa_family_t) + strlen(AGENT_ADDR) + 1; + + /* set up callback pointers */ + sg_broadcast_msg_callback = broadcast_handler; + sg_notification_msg_callback = notification_handler; + sg_status_msg_callback = status_handler; + + return sock; +} + +/* Provide access to the error message if the most recent call failed. */ +char *sg_get_error() +{ + return errorbuf; +} diff --git a/guest-comm/host-guest-comm-2.0/server_group.h b/guest-comm/host-guest-comm-2.0/server_group.h new file mode 100644 index 00000000..87353a82 --- /dev/null +++ b/guest-comm/host-guest-comm-2.0/server_group.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** +*/ + +#ifndef GUEST_CLIENT_H +#define GUEST_CLIENT_H + +// keys for server group messages +#define VERSION "version" +#define MSG_TYPE "msg_type" +#define SEQ "seq" +#define SOURCE_INSTANCE "source_instance" +#define DATA "data" +#define LOG_MSG "log_msg" + +/* This implements a library for server group messaging. + * + * The general idea is that everything that goes through this is multiplexed + * over a single unix socket so that the guest app only needs to monitor one + * socket for activity. + * + * This has not been tested for safe use in multithreaded applications. + */ + + +/* Function signature for the server group broadcast messaging callback function. + * source_instance is a null-terminated string of the form "instance-xxxxxxxx". + * The message contents are entirely up to the sender of the message. + */ +typedef void (*sg_broadcast_msg_handler_t)(char *source_instance, void *msg, + unsigned short msglen); + +/* Function signature for the server group notification callback function. The + * message is basically the notification as sent out by nova with some information + * removed as not relevant. The message is not null-terminated, though it is + * a JSON representation of a python dictionary. + */ +typedef void (*sg_notification_msg_handler_t)(void *msg, unsigned short msglen); + +/* Function signature for the server group status callback function. The + * message is a JSON representation of a list of dictionaries, each of which + * corresponds to a single server. The message is not null-terminated. + */ +typedef void (*sg_status_msg_handler_t)(void *msg, unsigned short msglen); + + + +/* Get error message from most recent library call that returned an error. */ +char *sg_get_error(); + +/* Allocate socket, set up callbacks, etc. This must be called once before + * any other API calls. + * + * Returns a socket that must be monitored for activity using select/poll/etc. + * A negative return value indicates an error of some kind. + */ +int init_sg(sg_broadcast_msg_handler_t broadcast_handler, + sg_notification_msg_handler_t notification_handler, + sg_status_msg_handler_t status_handler); + +/* This should be called when the socket becomes readable. This may result in + * callbacks being called. Returns 0 on success. + * A negative return value indicates an error of some kind. + */ +int process_sg_msg(); + +/* max msg length for a broadcast message */ +#define MAX_MSG_DATA_LEN 3050 + +/* Send a server group broadcast message. Returns 0 on success. + * A negative return value indicates an error of some kind. + * The message must be a null-terminated string without embedded newlines. + * len is no longer used. + */ +int sg_msg_broadcast(void *msg, unsigned int len); + +/* Request a status update for all servers in the group. + * Returns 0 if the request was successfully sent. + * A negative return value indicates an error of some kind. + * + * A successful response will cause the status_handler callback + * to be called. + * + * If a status update has been requested but the callback has not yet + * been called this may result in the previous request being cancelled. + */ +int sg_request_status(); + + +#endif + diff --git a/guest-comm/host-guest-comm-2.0/server_group_app.c b/guest-comm/host-guest-comm-2.0/server_group_app.c new file mode 100644 index 00000000..21ad3d41 --- /dev/null +++ b/guest-comm/host-guest-comm-2.0/server_group_app.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** +*/ + +#include +#include +#include +#include + +#include "server_group.h" + + +void sg_broadcast_msg_handler(char *source_instance, void *msg, + unsigned short msglen) +{ + printf("got server group broadcast msg: len: %d, sinstance: %.20s, msg: %.*s\n\n", + msglen, source_instance, msglen, (char *)msg); +} + +void sg_notification_msg_handler(void *msg, unsigned short msglen) +{ + printf("got server group notification msg: %.*s\n\n", + msglen, (char *)msg); +} + +void sg_status_msg_handler(void *msg, unsigned short msglen) +{ + printf("got server group status response msg: %.*s\n\n", + msglen, (char *)msg); +} + +int main(int argc, char **argv) +{ + int nfds; + int rc; + fd_set rfds; + // socket for guest client library + int sock = init_sg(sg_broadcast_msg_handler, + sg_notification_msg_handler, + sg_status_msg_handler); + if (sock < 0) { + printf("error initializing library: %s\n", sg_get_error()); + exit(-1); + } + nfds=sock+1; + FD_ZERO(&rfds); + FD_SET(sock, &rfds); + + if (argc == 2) { + rc = sg_msg_broadcast(argv[1], strlen(argv[1])+1); + if (rc != 0) { + printf("problem sending broadcast: %s\n", sg_get_error()); + return -1; + } + } + + rc = sg_request_status(); + if (rc != 0) { + printf("problem requesting status: %s\n", sg_get_error()); + return -1; + } + + while(1) { + int retval; + fd_set tmpfds = rfds; + retval = select(nfds, &tmpfds, NULL, NULL, NULL); + + if (retval > 0) { + rc = process_sg_msg(); + if (rc < 0) { + printf("problem processing incoming msg: %s\n", sg_get_error()); + } + } else if (retval == -1) + perror("select()"); + } + return 0; +} + + diff --git a/guest-comm/host-guest-comm-2.0/test/guest_app.c b/guest-comm/host-guest-comm-2.0/test/guest_app.c new file mode 100644 index 00000000..663394f0 --- /dev/null +++ b/guest-comm/host-guest-comm-2.0/test/guest_app.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + build: gcc -I../ -o guest_app guest_app.c -ljson-c +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "host_guest_msg_type.h" + +struct json_object *create_new_jobj_data() +{ + struct json_object *jobj_data = json_object_new_object(); + if (jobj_data == NULL) { + printf("failed to allocate json object for data\n"); + return NULL; + } + + json_object_object_add(jobj_data, VERSION, json_object_new_int(CUR_VERSION)); + json_object_object_add(jobj_data, "resource", json_object_new_string("cpu")); + json_object_object_add(jobj_data, "direction", json_object_new_string("down")); + return jobj_data; +} + +// create app to daemon msg +const char *create_outmsg(int msg_count) +{ + struct json_object *jobj_msg = json_object_new_object(); + if (jobj_msg == NULL) { + printf("failed to allocate json object for msg\n"); + return NULL; + } + + json_object_object_add(jobj_msg, VERSION, json_object_new_int(CUR_VERSION)); + json_object_object_add(jobj_msg, DEST_ADDR, json_object_new_string("h_scale_agent")); + json_object_object_add(jobj_msg, "msg_count", json_object_new_int(msg_count)); + + struct json_object *jobj_data = create_new_jobj_data(); + if (jobj_data == NULL) { + json_object_put(jobj_msg); + return NULL; + } + + json_object_object_add(jobj_msg, DATA, create_new_jobj_data()); + const char *msg = json_object_to_json_string_ext(jobj_msg, JSON_C_TO_STRING_PLAIN); + return msg; +} + +// create daemon to app msg +const char *create_inmsg() +{ + struct json_object *jobj_msg = json_object_new_object(); + if (jobj_msg == NULL) { + printf("failed to allocate json object for msg\n"); + return NULL; + } + + json_object_object_add(jobj_msg, VERSION, json_object_new_int(CUR_VERSION)); + json_object_object_add(jobj_msg, SOURCE_ADDR, json_object_new_string("h_scale_agent")); + + struct json_object *jobj_data = create_new_jobj_data(); + if (jobj_data == NULL) { + json_object_put(jobj_msg); + return NULL; + } + + json_object_object_add(jobj_msg, DATA, create_new_jobj_data()); + const char *msg = json_object_to_json_string_ext(jobj_msg, JSON_C_TO_STRING_PLAIN); + return msg; +} + +int main() +{ + char buf[4096]; + int len; + struct sockaddr_un cliaddr, srvaddr; + int rc; + + // set up socket for talking to host agent + int fd = socket(AF_UNIX, SOCK_DGRAM, 0); + if (fd == -1) { + perror("socket"); + exit(-1); + } + + memset(&cliaddr, 0, sizeof(struct sockaddr_un)); + cliaddr.sun_family = AF_UNIX; + cliaddr.sun_path[0] = '\0'; + strncpy(cliaddr.sun_path+1, "g_scale_agent", sizeof(cliaddr.sun_path) - 2); + + if (bind(fd, (struct sockaddr *) &cliaddr, sizeof(struct sockaddr_un)) == -1) { + perror("bind"); + exit(-1); + } + + /* Construct address of server */ + memset(&srvaddr, 0, sizeof(struct sockaddr_un)); + srvaddr.sun_family = AF_UNIX; + srvaddr.sun_path[0] = '\0'; + strncpy(srvaddr.sun_path+1, AGENT_ADDR, sizeof(srvaddr.sun_path) - 2); + + const char* inmsg = create_inmsg(); + const char* outmsg = create_outmsg(1); + len = strlen(outmsg); + rc = sendto(fd, outmsg, len, 0, (struct sockaddr *) &srvaddr, + sizeof(struct sockaddr_un)); + if (rc != len) { + perror("sendto"); + exit(-1); + } + outmsg = create_outmsg(2); + len = strlen(outmsg); + rc = sendto(fd, outmsg, len, 0, (struct sockaddr *) &srvaddr, + sizeof(struct sockaddr_un)); + if (rc != len) { + perror("sendto"); + exit(-1); + } + + while (1) { + len = recv(fd, buf, sizeof(buf), 0); + if (len == -1) { + perror("recvfrom"); + exit(-1); + } + printf("Msg from host: %s\n", buf); + fflush(0); + } +} + + diff --git a/guest-comm/host-guest-comm-2.0/test/host_app.c b/guest-comm/host-guest-comm-2.0/test/host_app.c new file mode 100644 index 00000000..d8b2477e --- /dev/null +++ b/guest-comm/host-guest-comm-2.0/test/host_app.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + build: gcc -I../ -o host_app host_app.c -ljson-c +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "host_guest_msg_type.h" + +struct json_object *create_new_jobj_data() +{ + struct json_object *jobj_data = json_object_new_object(); + if (jobj_data == NULL) { + printf("failed to allocate json object for data\n"); + return NULL; + } + + json_object_object_add(jobj_data, VERSION, json_object_new_int(CUR_VERSION)); + json_object_object_add(jobj_data, "resource", json_object_new_string("cpu")); + json_object_object_add(jobj_data, "direction", json_object_new_string("down")); + return jobj_data; +} + +// create app to daemon msg +const char *create_outmsg(int msg_count, const char *dest_instance) +{ + struct json_object *jobj_msg = json_object_new_object(); + if (jobj_msg == NULL) { + printf("failed to allocate json object for msg\n"); + return NULL; + } + + json_object_object_add(jobj_msg, VERSION, json_object_new_int(CUR_VERSION)); + json_object_object_add(jobj_msg, DEST_ADDR, json_object_new_string("g_scale_agent")); + json_object_object_add(jobj_msg, DEST_INSTANCE, json_object_new_string(dest_instance)); + json_object_object_add(jobj_msg, "msg_count", json_object_new_int(msg_count)); + + struct json_object *jobj_data = create_new_jobj_data(); + if (jobj_data == NULL) { + json_object_put(jobj_msg); + return NULL; + } + + json_object_object_add(jobj_msg, DATA, create_new_jobj_data()); + const char *msg = json_object_to_json_string_ext(jobj_msg, JSON_C_TO_STRING_PLAIN); + return msg; +} + +// create daemon to app msg +const char *create_inmsg() +{ + struct json_object *jobj_msg = json_object_new_object(); + if (jobj_msg == NULL) { + printf("failed to allocate json object for msg\n"); + return NULL; + } + + json_object_object_add(jobj_msg, VERSION, json_object_new_int(CUR_VERSION)); + json_object_object_add(jobj_msg, SOURCE_ADDR, json_object_new_string("h_scale_agent")); + + struct json_object *jobj_data = create_new_jobj_data(); + if (jobj_data == NULL) { + json_object_put(jobj_msg); + return NULL; + } + + json_object_object_add(jobj_msg, DATA, create_new_jobj_data()); + const char *msg = json_object_to_json_string_ext(jobj_msg, JSON_C_TO_STRING_PLAIN); + return msg; +} + +//argv[1] should be the instance name +int main(int argc, char **argv) +{ + char buf[4096]; + int len; + struct sockaddr_un cliaddr, srvaddr; + int rc; + + if (argc != 2) { + printf("call with instance name as arg\n"); + exit(1); + } + + // set up socket for talking to host agent + int fd = socket(AF_UNIX, SOCK_DGRAM, 0); + if (fd == -1) { + perror("socket"); + exit(-1); + } + + memset(&cliaddr, 0, sizeof(struct sockaddr_un)); + cliaddr.sun_family = AF_UNIX; + cliaddr.sun_path[0] = '\0'; + strncpy(cliaddr.sun_path+1, "h_scale_agent", sizeof(cliaddr.sun_path) - 2); + + if (bind(fd, (struct sockaddr *) &cliaddr, sizeof(struct sockaddr_un)) == -1) { + perror("bind"); + exit(-1); + } + + /* Construct address of server */ + memset(&srvaddr, 0, sizeof(struct sockaddr_un)); + srvaddr.sun_family = AF_UNIX; + srvaddr.sun_path[0] = '\0'; + strncpy(srvaddr.sun_path+1, AGENT_ADDR, sizeof(srvaddr.sun_path) - 2); + + const char* inmsg = create_inmsg(); + + // pass argv[1] as dest_instance + const char* outmsg = create_outmsg(1, argv[1]); + len = strlen(outmsg); + + rc = sendto(fd, outmsg, len, 0, (struct sockaddr *) &srvaddr, + sizeof(struct sockaddr_un)); + if (rc != len) { + perror("sendto"); + exit(-1); + } + + outmsg = create_outmsg(2, argv[1]); + len = strlen(outmsg); + rc = sendto(fd, outmsg, len, 0, (struct sockaddr *) &srvaddr, + sizeof(struct sockaddr_un)); + if (rc != len) { + perror("sendto"); + exit(-1); + } + + while (1) { + len = recv(fd, buf, sizeof(buf), 0); + if (len == -1) { + perror("recvfrom"); + exit(-1); + } + printf("Msg from instance %s\n", buf); + fflush(0); + } +} + + diff --git a/guest-comm/host-guest-comm-2.0/test/test_host_guest_msg.c b/guest-comm/host-guest-comm-2.0/test/test_host_guest_msg.c new file mode 100644 index 00000000..2626c98c --- /dev/null +++ b/guest-comm/host-guest-comm-2.0/test/test_host_guest_msg.c @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2013-2016, Wind River Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3) Neither the name of Wind River Systems nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + This file is used to test functions in file host_guest_msg.c. + Functions under test are directly copied to here to simplify compile. + Once tested and refactored, the functions can be copied back to their + original location with appropriate debug traces. + + build: gcc -I. -o test_host_guest_msg test_host_guest_msg.c -ljson-c + + usage: binary can be executed directly on a linux desktop. + ./test_host_guest_msg - run without parameters to check TCs PASS or FAIL + ./test_host_guest_msg 1 - show error logs + ./test_host_guest_msg 2 - show error and debug logs +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//number of guest +#define NUM_GUEST 3 + +//number of reads +#define NUM_READS 3 + +int debug = 0; +#define PRINT_ERR(format, ...) if(debug >= 1) printf(format, ##__VA_ARGS__) +#define PRINT_DEBUG(format, ...) if(debug >= 2) printf(format, ##__VA_ARGS__) + +// One tokener for each instance connection serve as reassembly buffer +struct json_tokener* tok[NUM_GUEST]; +void process_msg(json_object *jobj_msg, int fd); + +//---------------------------------------------------------- +// Functions Under Test +//---------------------------------------------------------- +void parser(void *buf, ssize_t len, int fd, json_tokener* tok, int newline_found) +{ + PRINT_DEBUG("parser: len=%lu, buf=%s\n", len, (char *)buf); + + json_object *jobj = json_tokener_parse_ex(tok, buf, len); + enum json_tokener_error jerr = json_tokener_get_error(tok); + + if (jerr == json_tokener_success) { + process_msg(jobj, fd); + json_object_put(jobj); + return; + } + + else if (jerr == json_tokener_continue) { + // partial JSON is parsed , continue to read from socket + PRINT_DEBUG("partial message parsed, continue read socket\n"); + PRINT_DEBUG("processed so far buf=%s\n", (char *)buf); + + // if newline was found in the middle of the buffer, the message should + // be completed at this point. Throw out incomplete message by resetting + // tokener. + if (newline_found) { + PRINT_DEBUG("newline_found. throw out the partial message\n"); + json_tokener_reset(tok); + } + } + else + { + PRINT_ERR("JSON Parsing Error: %s\n", json_tokener_error_desc(jerr)); + json_tokener_reset(tok); + } +} + + +//---------------------------------------------------------- +// Functions Under Test +//---------------------------------------------------------- +void handle_virtio_serial_msg(void *buf, ssize_t len, int fd, json_tokener* tok) +{ + void *origbuf = buf; + void *newline; + + ssize_t len_head; + +next: + if (len == 0) + return; + + PRINT_DEBUG("analyzing buffer at offset %lu, len %zd\n", buf-origbuf, len); + + // search for newline as delimiter + newline = memchr(buf, '\n', len); + + if (newline) { + PRINT_DEBUG("found newline start at offset %lu\n", newline - origbuf); + + // split buffer to head and tail at the location of newline. + // feed the head to the parser and recursively process the tail. + len_head = newline-buf; + + // parse head + if (len_head > 0) + parser(buf, len_head, fd, tok, 1); + + // start of the tail: skip newline + buf += len_head+1; + // length of the tail: deduct 1 for the newline character + len -= len_head+1; + // continue to process the tail. + goto next; + } + else { + parser(buf, len, fd, tok, 0); + } +} + + +// buffer to simulate socket to read, one socket per guest +void *socket[NUM_GUEST][NUM_READS]; + +// resulting parsed socket +char socket_processed[NUM_GUEST][NUM_READS][500]; + +// expected result +char *socket_expected[NUM_GUEST][NUM_READS]; + +// track current buffer being processed for particular guest +int current_processed[NUM_GUEST]; + +void free_tok() +{ + int fd; + for (fd=0; fd 1) + debug = atoi(argv[1]); + + setup_test(); + + printf("\n===== TC1 one valid message per read: "); + init_socket_tc1(); + for (read = 0; read > /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 + + + + + diff --git a/nfv/centos/build_srpm.data b/nfv/centos/build_srpm.data new file mode 100755 index 00000000..5651d70f --- /dev/null +++ b/nfv/centos/build_srpm.data @@ -0,0 +1 @@ +TIS_PATCH_VER=67 diff --git a/nfv/centos/nfv.spec b/nfv/centos/nfv.spec new file mode 100755 index 00000000..09b16146 --- /dev/null +++ b/nfv/centos/nfv.spec @@ -0,0 +1,187 @@ +Summary: Network Function Virtualization +Name: nfv +Version: 1.0 +Release: %{tis_patch_ver}%{?_tis_dist} +License: Apache-2.0 +Group: base +Packager: Wind River +URL: unknown +Source0: %{name}-%{version}.tar.gz + +%define debug_package %{nil} + +BuildRequires: python-setuptools + +%description +Titanium Cloud Config Info + +%define local_bindir /usr/bin/ +%define pythonroot /usr/lib64/python2.7/site-packages + +%define build_python() ( \ + pushd %1; \ + %{__python} setup.py build; \ + popd) + +%define install_python() ( \ + pushd %1; \ + %{__python} setup.py install \\\ + --root=$RPM_BUILD_ROOT \\\ + --install-lib=%{pythonroot} \\\ + --prefix=/usr \\\ + --install-data=/usr/share \\\ + --single-version-externally-managed; \ + popd) + +# TODO: nfv-docs + +%package -n nfv-common +Requires: librt.so.1()(64bit) +Summary: Network Function Virtualization Common +Group: base + +%description -n nfv-common +Network Function Virtualization Common + +%package -n nfv-plugins +Summary: Network Function Virtualization Plugins +Group: base + +%description -n nfv-plugins +Network Function Virtualization Plugins + +%package -n nfv-tools +Summary: Network Function Virtualization Tools +Group: base + +%description -n nfv-tools +Network Function Virtualization Tools + +%package -n nfv-vim +Summary: Virtual Infrastructure Manager +Group: base + +%description -n nfv-vim +Virtual Infrastructure Manager + +%package -n nfv-client +Summary: Network Function Virtualization Client +Group: base + +%description -n nfv-client +Network Function Virtualization Client + +%prep +%setup + +%build +%build_python nfv-common +%build_python nfv-plugins +%build_python nfv-tools +%build_python nfv-vim +%build_python nfv-client + +%install +%install_python nfv-common +%install_python nfv-plugins +%install_python nfv-tools +%install_python nfv-vim +%install_python nfv-client + +# nfv-client +install -d -m 755 %{buildroot}%{_sysconfdir}/bash_completion.d +install -m 444 nfv-client/scripts/sw-manager.completion %{buildroot}%{_sysconfdir}/bash_completion.d/sw-manager + +# nfv-plugins +install -d -m 755 %{buildroot}/etc/nfv/ +install -d -m 755 %{buildroot}/etc/nfv/nfv_plugins/ +install -d -m 755 %{buildroot}/etc/nfv/nfv_plugins/alarm_handlers/ +install -p -D -m 600 nfv-plugins/nfv_plugins/alarm_handlers/config.ini %{buildroot}/etc/nfv/nfv_plugins/alarm_handlers/config.ini +install -d -m 755 %{buildroot}/etc/nfv/nfv_plugins/event_log_handlers/ +install -p -D -m 600 nfv-plugins/nfv_plugins/event_log_handlers/config.ini %{buildroot}/etc/nfv/nfv_plugins/event_log_handlers/config.ini +install -d -m 755 %{buildroot}/etc/nfv/nfv_plugins/nfvi_plugins/ +install -p -D -m 600 nfv-plugins/nfv_plugins/nfvi_plugins/config.ini %{buildroot}/etc/nfv/nfv_plugins/nfvi_plugins/config.ini +install -d -m 755 %{buildroot}/ +install -p -D -m 755 nfv-plugins/scripts/nfvi_plugins.logrotate %{buildroot}/etc/logrotate.d/nfvi_plugins.logrotate + +# nfv-vim +install -d -m 755 %{buildroot}/usr/lib/ocf/resource.d/nfv +install -p -D -m 755 nfv-vim/scripts/vim %{buildroot}/usr/lib/ocf/resource.d/nfv/vim +install -p -D -m 755 nfv-vim/scripts/vim-api %{buildroot}/usr/lib/ocf/resource.d/nfv/vim-api +install -p -D -m 755 nfv-vim/scripts/vim-webserver %{buildroot}/usr/lib/ocf/resource.d/nfv/vim-webserver +install -d -m 755 %{buildroot}/etc/nfv/ +install -d -m 755 %{buildroot}/etc/nfv/vim/ +install -p -D -m 600 nfv-vim/nfv_vim/config.ini %{buildroot}/etc/nfv/vim/config.ini +install -p -D -m 600 nfv-vim/nfv_vim/debug.ini %{buildroot}/etc/nfv/vim/debug.ini + +%post -n nfv-common + +%post -n nfv-plugins + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-,root,root,-) + +%files -n nfv-common +%defattr(-,root,root,-) +%doc nfv-common/LICENSE +%dir %{pythonroot}/nfv_common/ +%{pythonroot}/nfv_common/* +%dir %{pythonroot}/windriver_nfv_common_plugins-%{version}.0-py2.7.egg-info +%{pythonroot}/windriver_nfv_common_plugins-%{version}.0-py2.7.egg-info/* + +%files -n nfv-plugins +%defattr(-,root,root,-) +%doc nfv-plugins/LICENSE +%dir /etc/logrotate.d/ +/etc/logrotate.d/nfvi_plugins.logrotate +%dir /etc/nfv/nfv_plugins/ +%config(noreplace)/etc/nfv/nfv_plugins/alarm_handlers/config.ini +%config(noreplace)/etc/nfv/nfv_plugins/event_log_handlers/config.ini +%config(noreplace)/etc/nfv/nfv_plugins/nfvi_plugins/config.ini +/etc/nfv/nfv_plugins/* +%dir %{pythonroot}/nfv_plugins/ +%{pythonroot}/nfv_plugins/* +%dir %{pythonroot}/windriver_nfv_plugins-%{version}.0-py2.7.egg-info +%{pythonroot}/windriver_nfv_plugins-%{version}.0-py2.7.egg-info/* + +%files -n nfv-tools +%defattr(-,root,root,-) +%doc nfv-tools/LICENSE +%{local_bindir}/nfv-forensic +%{local_bindir}/nfv-notify +%dir %{pythonroot}/nfv_tools/ +%{pythonroot}/nfv_tools/* +%dir %{pythonroot}/nfv_tools-%{version}.0-py2.7.egg-info +%{pythonroot}/nfv_tools-%{version}.0-py2.7.egg-info/* + +%files -n nfv-vim +%defattr(-,root,root,-) +%doc nfv-vim/LICENSE +%{local_bindir}/nfv-vim +%{local_bindir}/nfv-vim-api +%{local_bindir}/nfv-vim-manage +%{local_bindir}/nfv-vim-webserver +%dir /etc/nfv/vim/ +%config(noreplace)/etc/nfv/vim/config.ini +%config(noreplace)/etc/nfv/vim/debug.ini +%dir /usr/lib/ocf/resource.d/nfv/ +/usr/lib/ocf/resource.d/nfv/vim +/usr/lib/ocf/resource.d/nfv/vim-api +/usr/lib/ocf/resource.d/nfv/vim-webserver +%dir %{pythonroot}/nfv_vim/ +%{pythonroot}/nfv_vim/* +%dir %{pythonroot}/nfv_vim-%{version}.0-py2.7.egg-info +%{pythonroot}/nfv_vim-%{version}.0-py2.7.egg-info/* + +%files -n nfv-client +%defattr(-,root,root,-) +%doc nfv-client/LICENSE +%{local_bindir}/sw-manager +%{_sysconfdir}/bash_completion.d/sw-manager +%dir %{pythonroot}/nfv_client/ +%{pythonroot}/nfv_client/* +%dir %{pythonroot}/nfv_client-%{version}.0-py2.7.egg-info +%{pythonroot}/nfv_client-%{version}.0-py2.7.egg-info/* diff --git a/nfv/nfv-client/LICENSE b/nfv/nfv-client/LICENSE new file mode 100755 index 00000000..d6456956 --- /dev/null +++ b/nfv/nfv-client/LICENSE @@ -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. diff --git a/nfv/nfv-client/nfv_client/__init__.py b/nfv/nfv-client/nfv_client/__init__.py new file mode 100755 index 00000000..e302d78a --- /dev/null +++ b/nfv/nfv-client/nfv_client/__init__.py @@ -0,0 +1,4 @@ +# Copyright (c) 2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# diff --git a/nfv/nfv-client/nfv_client/openstack/__init__.py b/nfv/nfv-client/nfv_client/openstack/__init__.py new file mode 100755 index 00000000..e302d78a --- /dev/null +++ b/nfv/nfv-client/nfv_client/openstack/__init__.py @@ -0,0 +1,4 @@ +# Copyright (c) 2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# diff --git a/nfv/nfv-client/nfv_client/openstack/objects.py b/nfv/nfv-client/nfv_client/openstack/objects.py new file mode 100755 index 00000000..8a51d620 --- /dev/null +++ b/nfv/nfv-client/nfv_client/openstack/objects.py @@ -0,0 +1,77 @@ +# +# Copyright (c) 2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import re +import iso8601 +import datetime + + +class Token(object): + """ + Token + """ + def __init__(self, token_data, token_id): + self._expired = False + self._data = token_data + self._token_id = token_id + + def set_expired(self): + self._expired = True + + def is_expired(self, within_seconds=300): + if not self._expired: + end = iso8601.parse_date(self._data['token']['expires_at']) + now = iso8601.parse_date(datetime.datetime.utcnow().isoformat()) + if end <= now: + return True + delta = abs(end - now).seconds + return delta <= within_seconds + return True + + def get_id(self): + """ + Get the identifier of the token. + """ + return self._token_id + + def get_tenant_id(self): + """ + Get the tenant identifier of the token. + """ + return self._data['token']['project']['id'] + + @staticmethod + def _url_strip_version(url): + """ + Strip the version information from the url + """ + # Get rid of the trailing '/' if present and remove the version + # information from the URL. + url = url.rstrip('/') + url_bits = url.split('/') + # Regular-Expression to match 'v1' or 'v2.0' etc + # prefix regular expression with r to treat as raw string to + # suppress lint 'anomalous backslash' warnings + if re.match(r'v\d+\.?\d*', url_bits[-1]): + url = '/'.join(url_bits[:-1]) + + elif re.match(r'v\d+\.?\d*', url_bits[-2]): + url = '/'.join(url_bits[:-2]) + + return url + + def get_service_url(self, region_name, service_name, service_type, interface): + """ + Search the catalog of a service in a region for the url + """ + for catalog in self._data['token']['catalog']: + if catalog['type'] == service_type: + if catalog['name'] == service_name: + if 0 != len(catalog['endpoints']): + for endpoint in catalog['endpoints']: + if (endpoint['region'] == region_name and + endpoint['interface'] == interface): + return endpoint['url'] + return None diff --git a/nfv/nfv-client/nfv_client/openstack/openstack.py b/nfv/nfv-client/nfv_client/openstack/openstack.py new file mode 100755 index 00000000..4f378a70 --- /dev/null +++ b/nfv/nfv-client/nfv_client/openstack/openstack.py @@ -0,0 +1,78 @@ +# +# Copyright (c) 2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import json +import urllib2 + +from objects import Token + + +class OpenStackServices(object): + """ + OpenStack Services Constants + """ + VIM = 'vim' + + +class OpenStackServiceTypes(object): + """ + OpenStack Service Types Constants + """ + NFV = 'nfv' + + +SERVICE = OpenStackServices() +SERVICE_TYPE = OpenStackServiceTypes() + + +def get_token(auth_uri, project_name, project_domain_name, username, password, + user_domain_name): + """ + Ask OpenStack for a token + """ + try: + url = auth_uri + "/auth/tokens" + + request_info = urllib2.Request(url) + request_info.add_header("Content-Type", "application/json") + request_info.add_header("Accept", "application/json") + + payload = json.dumps( + {"auth": { + "identity": { + "methods": [ + "password" + ], + "password": { + "user": { + "name": username, + "password": password, + "domain": {"name": user_domain_name} + } + } + }, + "scope": { + "project": { + "name": project_name, + "domain": {"name": project_domain_name} + }}}}) + + request_info.add_data(payload) + + request = urllib2.urlopen(request_info) + # Identity API v3 returns token id in X-Subject-Token + # response header. + token_id = request.info().getheader('X-Subject-Token') + response = json.loads(request.read()) + request.close() + return Token(response, token_id) + + except urllib2.HTTPError as e: + print(e) + return None + + except urllib2.URLError as e: + print(e) + return None diff --git a/nfv/nfv-client/nfv_client/openstack/rest_api.py b/nfv/nfv-client/nfv_client/openstack/rest_api.py new file mode 100755 index 00000000..f3205171 --- /dev/null +++ b/nfv/nfv-client/nfv_client/openstack/rest_api.py @@ -0,0 +1,102 @@ +# +# Copyright (c) 2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import json +import urllib2 +import httplib + + +def request(token_id, method, api_cmd, api_cmd_headers=None, api_cmd_payload=None): + """ + Make a rest-api request + """ + headers_per_hop = ['connection', 'keep-alive', 'proxy-authenticate', + 'proxy-authorization', 'te', 'trailers', + 'transfer-encoding', 'upgrade'] + + try: + request_info = urllib2.Request(api_cmd) + request_info.get_method = lambda: method + if token_id is not None: + request_info.add_header("X-Auth-Token", token_id) + request_info.add_header("Accept", "application/json") + + if api_cmd_headers is not None: + for header_type, header_value in api_cmd_headers.items(): + request_info.add_header(header_type, header_value) + + if api_cmd_payload is not None: + request_info.add_data(api_cmd_payload) + + url_request = urllib2.urlopen(request_info) + + headers = list() # list of tuples + for key, value in url_request.info().items(): + if key not in headers_per_hop: + cap_key = '-'.join((ck.capitalize() for ck in key.split('-'))) + headers.append((cap_key, value)) + + response_raw = url_request.read() + + if response_raw == "": + response = dict() + else: + response = json.loads(response_raw) + + url_request.close() + + return response + + except urllib2.HTTPError as e: + headers = list() + response_raw = dict() + + if e.fp is not None: + headers = list() # list of tuples + for key, value in e.fp.info().items(): + if key not in headers_per_hop: + cap_key = '-'.join((ck.capitalize() + for ck in key.split('-'))) + headers.append((cap_key, value)) + + response_raw = e.fp.read() + + if httplib.FOUND == e.code: + return response_raw + + elif httplib.NOT_FOUND == e.code: + return None + + elif httplib.CONFLICT == e.code: + raise Exception("Operation failed: conflict detected") + + elif httplib.FORBIDDEN == e.code: + raise Exception("Authorization failed") + + # Attempt to get the reason for the http error from the response + reason = '' + for header, value in headers: + if 'Content-Type' == header: + if 'application/json' == value.split(';')[0]: + try: + response = json.loads(response_raw) + message = response.get('faultstring', None) + if message is not None: + reason = str(message.lower().rstrip('.')) + print "Operation failed: %s" % reason + return + + except ValueError: + pass + + print("Rest-API status=%s, %s, %s, headers=%s, payload=%s, response=%s" + % (e.code, method, api_cmd, api_cmd_headers, api_cmd_payload, + response_raw)) + raise + + except urllib2.URLError as e: + print("Rest-API status=ERR, %s, %s, headers=%s, payload=%s" + % (method, api_cmd, api_cmd_headers, api_cmd_payload,)) + raise diff --git a/nfv/nfv-client/nfv_client/openstack/sw_update.py b/nfv/nfv-client/nfv_client/openstack/sw_update.py new file mode 100755 index 00000000..4bedf551 --- /dev/null +++ b/nfv/nfv-client/nfv_client/openstack/sw_update.py @@ -0,0 +1,319 @@ +# +# Copyright (c) 2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import json +import rest_api + + +class StrategyStep(object): + step_id = None + step_name = None + entity_type = None + entity_names = [] + entity_uuids = [] + timeout = None + start_date_time = None + end_date_time = None + result = None + reason = None + + def __repr__(self): + return "%s" % str(self.__dict__) + + +class StrategyStage(object): + stage_id = None + stage_name = None + steps = [] + total_steps = None + current_step = None + timeout = None + start_date_time = None + end_date_time = None + inprogress = None + result = None + reason = None + + def __repr__(self): + return "%s" % str(self.__dict__) + + +class StrategyPhase(object): + phase_name = None + stages = [] + total_stages = None + current_stage = None + stop_at_stage = None + timeout = None + start_date_time = None + end_date_time = None + inprogress = None + completion_percentage = None + result = None + reason = None + + def __repr__(self): + return "%s" % str(self.__dict__) + + +class Strategy(object): + uuid = None + name = None + controller_apply_type = None + storage_apply_type = None + swift_apply_type = None + compute_apply_type = None + max_parallel_compute_hosts = None + default_instance_action = None + alarm_restrictions = None + current_phase = None + current_phase_completion_percentage = None + state = None + build_phase = None + apply_phase = None + abort_phase = None + + def __repr__(self): + return "%s" % str(self.__dict__) + + +def _get_strategy_step_object_from_response(response): + """ + Convert the Rest-API response into a strategy step object + """ + step = StrategyStep() + step.step_id = response['step-id'] + step.step_name = response['step-name'] + step.entity_type = response['entity-type'] + step.entity_names = response['entity-names'] + step.entity_uuids = response['entity-uuids'] + step.timeout = response['timeout'] + step.start_date_time = response['start-date-time'] + step.end_date_time = response['end-date-time'] + step.result = response['result'] + step.reason = response['reason'] + return step + + +def _get_strategy_stage_object_from_response(response): + """ + Convert the Rest-API response into a strategy stage object + """ + stage = StrategyStage() + stage.stage_id = response['stage-id'] + stage.stage_name = response['stage-name'] + stage.total_steps = response['total-steps'] + stage.current_step = response['current-step'] + stage.timeout = response['timeout'] + stage.start_date_time = response['start-date-time'] + stage.end_date_time = response['end-date-time'] + stage.inprogress = response['inprogress'] + stage.result = response['result'] + stage.reason = response['reason'] + + stage.steps = [] + for step in response['steps']: + stage.steps.append(_get_strategy_step_object_from_response(step)) + + return stage + + +def _get_strategy_phase_object_from_response(response): + """ + Convert the Rest-API response into a strategy phase object + """ + phase = StrategyPhase() + phase.phase_name = response['phase-name'] + phase.total_stages = response['total-stages'] + phase.current_stage = response['current-stage'] + phase.stop_at_stage = response['stop-at-stage'] + phase.timeout = response['timeout'] + phase.start_date_time = response['start-date-time'] + phase.end_date_time = response['end-date-time'] + phase.inprogress = response['inprogress'] + phase.completion_percentage = response['completion-percentage'] + phase.result = response['result'] + phase.reason = response['reason'] + + phase.stages = [] + for stage in response['stages']: + phase.stages.append(_get_strategy_stage_object_from_response(stage)) + + return phase + + +def _get_strategy_object_from_response(response): + """ + Convert the Rest-API response into a strategy object + """ + strategy_data = response.get('strategy', None) + if strategy_data is None: + return None + strategy = Strategy() + strategy.uuid = strategy_data['uuid'] + strategy.name = strategy_data['name'] + strategy.controller_apply_type = strategy_data['controller-apply-type'] + strategy.storage_apply_type = strategy_data['storage-apply-type'] + strategy.swift_apply_type = strategy_data['swift-apply-type'] + strategy.compute_apply_type = strategy_data['compute-apply-type'] + strategy.max_parallel_compute_hosts = \ + strategy_data['max-parallel-compute-hosts'] + strategy.default_instance_action = strategy_data['default-instance-action'] + strategy.alarm_restrictions = strategy_data['alarm-restrictions'] + strategy.current_phase = strategy_data['current-phase'] + strategy.current_phase_completion_percentage \ + = strategy_data['current-phase-completion-percentage'] + strategy.state = strategy_data['state'] + + strategy.build_phase \ + = _get_strategy_phase_object_from_response(strategy_data['build-phase']) + strategy.apply_phase \ + = _get_strategy_phase_object_from_response(strategy_data['apply-phase']) + strategy.abort_phase \ + = _get_strategy_phase_object_from_response(strategy_data['abort-phase']) + + return strategy + + +def get_strategies(token_id, url, strategy_name): + """ + Software Update - Get Strategies + """ + api_cmd = url + "/api/orchestration/%s/strategy" % strategy_name + + api_cmd_headers = dict() + api_cmd_headers['X-Auth-Token'] = token_id + + response = rest_api.request(token_id, "GET", api_cmd, api_cmd_headers) + if not response: + return None + + return _get_strategy_object_from_response(response) + + +def get_strategy(token_id, url, strategy_name, strategy_uuid): + """ + Software Update - Get Strategy + """ + api_cmd = url + "/api/orchestration/%s/strategy/%s" % (strategy_name, + strategy_uuid) + + api_cmd_headers = dict() + api_cmd_headers['X-Auth-Token'] = token_id + + response = rest_api.request(token_id, "GET", api_cmd, api_cmd_headers) + if not response: + return None + + return _get_strategy_object_from_response(response) + + +def create_strategy(token_id, url, strategy_name, controller_apply_type, + storage_apply_type, swift_apply_type, compute_apply_type, + max_parallel_compute_hosts, + default_instance_action, alarm_restrictions, **kwargs): + """ + Software Update - Create Strategy + """ + api_cmd = url + "/api/orchestration/%s/strategy" % strategy_name + + api_cmd_headers = dict() + api_cmd_headers['Content-Type'] = "application/json" + api_cmd_headers['X-Auth-Token'] = token_id + + api_cmd_payload = dict() + if 'sw-patch' == strategy_name: + api_cmd_payload['controller-apply-type'] = controller_apply_type + api_cmd_payload['swift-apply-type'] = swift_apply_type + api_cmd_payload['default-instance-action'] = default_instance_action + elif 'sw-upgrade' == strategy_name: + if 'start_upgrade' in kwargs and kwargs['start_upgrade']: + api_cmd_payload['start-upgrade'] = True + if 'complete_upgrade' in kwargs and kwargs['complete_upgrade']: + api_cmd_payload['complete-upgrade'] = True + api_cmd_payload['storage-apply-type'] = storage_apply_type + api_cmd_payload['compute-apply-type'] = compute_apply_type + if max_parallel_compute_hosts is not None: + api_cmd_payload['max-parallel-compute-hosts'] = \ + max_parallel_compute_hosts + api_cmd_payload['alarm-restrictions'] = alarm_restrictions + + response = rest_api.request(token_id, "POST", api_cmd, api_cmd_headers, + json.dumps(api_cmd_payload)) + if not response: + return None + + return _get_strategy_object_from_response(response) + + +def delete_strategy(token_id, url, strategy_name, force=False): + """ + Software Update - Delete Strategy + """ + api_cmd = url + "/api/orchestration/%s/strategy" % strategy_name + + api_cmd_headers = dict() + api_cmd_headers['Content-Type'] = "application/json" + api_cmd_headers['X-Auth-Token'] = token_id + + api_cmd_payload = dict() + api_cmd_payload['force'] = force + + response = rest_api.request(token_id, "DELETE", api_cmd, api_cmd_headers, + json.dumps(api_cmd_payload)) + # We expect an empty response body for this request (204 NO CONTENT). If + # there is no response body it is a 404 NOT FOUND which means there was + # no strategy to delete. + if response is None: + return False + + return True + + +def apply_strategy(token_id, url, strategy_name, stage_id=None): + """ + Software Update - Apply Strategy + """ + api_cmd = url + ("/api/orchestration/%s/strategy/actions" % strategy_name) + + api_cmd_headers = dict() + api_cmd_headers['Content-Type'] = "application/json" + api_cmd_headers['X-Auth-Token'] = token_id + + api_cmd_payload = dict() + if stage_id is None: + api_cmd_payload['action'] = 'apply-all' + else: + api_cmd_payload['action'] = 'apply-stage' + api_cmd_payload['stage-id'] = stage_id + + response = rest_api.request(token_id, "POST", api_cmd, api_cmd_headers, + json.dumps(api_cmd_payload)) + if not response: + return None + + return _get_strategy_object_from_response(response) + + +def abort_strategy(token_id, url, strategy_name, stage_id): + """ + Software Update - Abort Strategy + """ + api_cmd = url + ("/api/orchestration/%s/strategy/actions" % strategy_name) + + api_cmd_headers = dict() + api_cmd_headers['Content-Type'] = "application/json" + api_cmd_headers['X-Auth-Token'] = token_id + + api_cmd_payload = dict() + api_cmd_payload['action'] = 'abort-stage' + api_cmd_payload['stage-id'] = stage_id + + response = rest_api.request(token_id, "POST", api_cmd, api_cmd_headers, + json.dumps(api_cmd_payload)) + if not response: + return None + + return _get_strategy_object_from_response(response) diff --git a/nfv/nfv-client/nfv_client/shell.py b/nfv/nfv-client/nfv_client/shell.py new file mode 100755 index 00000000..1724f5e1 --- /dev/null +++ b/nfv/nfv-client/nfv_client/shell.py @@ -0,0 +1,342 @@ +# +# Copyright (c) 2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import os +import sys +import argparse +import urllib2 + +from nfv_client import sw_update + + +def process_main(argv=sys.argv[1:]): # pylint: disable=dangerous-default-value + """ + Client - Main + """ + try: + parser = argparse.ArgumentParser() + parser.add_argument('--debug', action='store_true') + parser.add_argument('--os-auth-url', default=None) + parser.add_argument('--os-project-name', default=None) + parser.add_argument('--os-project-domain-name', default=None) + parser.add_argument('--os-username', default=None) + parser.add_argument('--os-password', default=None) + parser.add_argument('--os-user-domain-name', default=None) + parser.add_argument('--os-region-name', default=None) + parser.add_argument('--os-interface', default=None) + + commands = parser.add_subparsers(title='Commands', metavar='') + + # Software Patch Commands + sw_patch_parser = commands.add_parser('patch-strategy', + help='Patch Strategy') + sw_patch_parser.set_defaults(cmd_area='patch-strategy') + + sw_patch_cmds = sw_patch_parser.add_subparsers( + title='Software Patch Commands', metavar='') + + sw_patch_create_strategy_cmd \ + = sw_patch_cmds.add_parser('create', help='Create a strategy') + sw_patch_create_strategy_cmd.set_defaults(cmd='create') + sw_patch_create_strategy_cmd.add_argument( + '--controller-apply-type', default=sw_update.APPLY_TYPE_SERIAL, + choices=[sw_update.APPLY_TYPE_SERIAL, sw_update.APPLY_TYPE_IGNORE], + help='defaults to serial') + sw_patch_create_strategy_cmd.add_argument( + '--storage-apply-type', default=sw_update.APPLY_TYPE_SERIAL, + choices=[sw_update.APPLY_TYPE_SERIAL, sw_update.APPLY_TYPE_PARALLEL, + sw_update.APPLY_TYPE_IGNORE], + help='defaults to serial') + sw_patch_create_strategy_cmd.add_argument( + '--compute-apply-type', default=sw_update.APPLY_TYPE_SERIAL, + choices=[sw_update.APPLY_TYPE_SERIAL, sw_update.APPLY_TYPE_PARALLEL, + sw_update.APPLY_TYPE_IGNORE], + help='defaults to serial') + sw_patch_create_strategy_cmd.add_argument( + '--max-parallel-compute-hosts', type=int, choices=range(2, 101), + help='maximum compute hosts to patch in parallel') + sw_patch_create_strategy_cmd.add_argument( + '--instance-action', default=sw_update.INSTANCE_ACTION_STOP_START, + choices=[sw_update.INSTANCE_ACTION_MIGRATE, + sw_update.INSTANCE_ACTION_STOP_START], + help='defaults to stop-start') + sw_patch_create_strategy_cmd.add_argument( + '--alarm-restrictions', default=sw_update.ALARM_RESTRICTIONS_STRICT, + choices=[sw_update.ALARM_RESTRICTIONS_STRICT, + sw_update.ALARM_RESTRICTIONS_RELAXED], + help='defaults to strict') + + sw_patch_delete_strategy_cmd \ + = sw_patch_cmds.add_parser('delete', help='Delete a strategy') + sw_patch_delete_strategy_cmd.set_defaults(cmd='delete') + sw_patch_delete_strategy_cmd.add_argument( + '--force', action='store_true', help=argparse.SUPPRESS) + + sw_patch_apply_strategy_cmd \ + = sw_patch_cmds.add_parser('apply', help='Apply a strategy') + sw_patch_apply_strategy_cmd.set_defaults(cmd='apply') + sw_patch_apply_strategy_cmd.add_argument( + '--stage-id', default=None, help='stage identifier to apply') + + sw_patch_abort_strategy_cmd \ + = sw_patch_cmds.add_parser('abort', help='Abort a strategy') + sw_patch_abort_strategy_cmd.set_defaults(cmd='abort') + sw_patch_abort_strategy_cmd.add_argument( + '--stage-id', help='stage identifier to abort') + + sw_patch_show_strategy_cmd \ + = sw_patch_cmds.add_parser('show', help='Show a strategy') + sw_patch_show_strategy_cmd.set_defaults(cmd='show') + sw_patch_show_strategy_cmd.add_argument( + '--details', action='store_true', help='show strategy details') + + # Software Upgrade Commands + sw_upgrade_parser = commands.add_parser('upgrade-strategy', + help='Upgrade Strategy') + sw_upgrade_parser.set_defaults(cmd_area='upgrade-strategy') + + sw_upgrade_cmds = sw_upgrade_parser.add_subparsers( + title='Software Upgrade Commands', metavar='') + + sw_upgrade_create_strategy_cmd \ + = sw_upgrade_cmds.add_parser('create', help='Create a strategy') + sw_upgrade_create_strategy_cmd.set_defaults(cmd='create') + sw_upgrade_create_strategy_cmd.add_argument( + '--storage-apply-type', default=sw_update.APPLY_TYPE_SERIAL, + choices=[sw_update.APPLY_TYPE_SERIAL, sw_update.APPLY_TYPE_PARALLEL, + sw_update.APPLY_TYPE_IGNORE], + help='defaults to serial') + sw_upgrade_create_strategy_cmd.add_argument( + '--compute-apply-type', default=sw_update.APPLY_TYPE_SERIAL, + choices=[sw_update.APPLY_TYPE_SERIAL, sw_update.APPLY_TYPE_PARALLEL, + sw_update.APPLY_TYPE_IGNORE], + help='defaults to serial') + sw_upgrade_create_strategy_cmd.add_argument( + '--max-parallel-compute-hosts', type=int, choices=range(2, 11), + help='maximum compute hosts to upgrade in parallel') + # Disable support for --start-upgrade as it was not completed + # sw_upgrade_create_strategy_cmd.add_argument( + # '--start-upgrade', action='store_true', + # help=argparse.SUPPRESS) + sw_upgrade_create_strategy_cmd.add_argument( + '--complete-upgrade', action='store_true', help=argparse.SUPPRESS) + sw_upgrade_create_strategy_cmd.add_argument( + '--alarm-restrictions', default=sw_update.ALARM_RESTRICTIONS_STRICT, + choices=[sw_update.ALARM_RESTRICTIONS_STRICT, + sw_update.ALARM_RESTRICTIONS_RELAXED], + help='defaults to strict') + + sw_upgrade_delete_strategy_cmd \ + = sw_upgrade_cmds.add_parser('delete', help='Delete a strategy') + sw_upgrade_delete_strategy_cmd.set_defaults(cmd='delete') + sw_upgrade_delete_strategy_cmd.add_argument( + '--force', action='store_true', help=argparse.SUPPRESS) + + sw_upgrade_apply_strategy_cmd \ + = sw_upgrade_cmds.add_parser('apply', help='Apply a strategy') + sw_upgrade_apply_strategy_cmd.set_defaults(cmd='apply') + sw_upgrade_apply_strategy_cmd.add_argument( + '--stage-id', default=None, help='stage identifier to apply') + + sw_upgrade_abort_strategy_cmd \ + = sw_upgrade_cmds.add_parser('abort', help='Abort a strategy') + sw_upgrade_abort_strategy_cmd.set_defaults(cmd='abort') + sw_upgrade_abort_strategy_cmd.add_argument( + '--stage-id', help='stage identifier to abort') + + sw_upgrade_show_strategy_cmd \ + = sw_upgrade_cmds.add_parser('show', help='Show a strategy') + sw_upgrade_show_strategy_cmd.set_defaults(cmd='show') + sw_upgrade_show_strategy_cmd.add_argument( + '--details', action='store_true', help='show strategy details') + + args = parser.parse_args(argv) + + if args.debug: + # Enable Debug + handler = urllib2.HTTPHandler(debuglevel=1) + opener = urllib2.build_opener(handler) + urllib2.install_opener(opener) + + if args.os_auth_url is None: + args.os_auth_url = os.environ.get('OS_AUTH_URL', None) + + if args.os_project_name is None: + args.os_project_name = os.environ.get('OS_PROJECT_NAME', None) + + if args.os_project_domain_name is None: + args.os_project_domain_name \ + = os.environ.get('OS_PROJECT_DOMAIN_NAME', None) + + if args.os_username is None: + args.os_username = os.environ.get('OS_USERNAME', None) + + if args.os_password is None: + args.os_password = os.environ.get('OS_PASSWORD', None) + + if args.os_user_domain_name is None: + args.os_user_domain_name = os.environ.get('OS_USER_DOMAIN_NAME', None) + + if args.os_region_name is None: + args.os_region_name = os.environ.get('OS_REGION_NAME', None) + + if args.os_interface is None: + args.os_interface = os.environ.get('OS_INTERFACE', None) + + if args.os_auth_url is None: + print("Authentication URI not given") + return + + if args.os_project_name is None: + print("Project name not given") + return + + if args.os_project_domain_name is None: + print("Project domain name not given") + return + + if args.os_username is None: + print("Username not given") + return + + if args.os_password is None: + print("User password not given") + return + + if args.os_user_domain_name is None: + print("User domain name not given") + return + + if args.os_region_name is None: + print("Openstack region name not given") + return + + if args.os_interface is None: + print("Openstack interface not given") + return + + if 'patch-strategy' == args.cmd_area: + if 'create' == args.cmd: + sw_update.create_strategy( + args.os_auth_url, args.os_project_name, + args.os_project_domain_name, args.os_username, args.os_password, + args.os_user_domain_name, args.os_region_name, + args.os_interface, + sw_update.STRATEGY_NAME_SW_PATCH, + args.controller_apply_type, + args.storage_apply_type, sw_update.APPLY_TYPE_IGNORE, + args.compute_apply_type, + args.max_parallel_compute_hosts, + args.instance_action, + args.alarm_restrictions) + + elif 'delete' == args.cmd: + sw_update.delete_strategy(args.os_auth_url, args.os_project_name, + args.os_project_domain_name, + args.os_username, args.os_password, + args.os_user_domain_name, + args.os_region_name, args.os_interface, + sw_update.STRATEGY_NAME_SW_PATCH, + args.force) + + elif 'apply' == args.cmd: + sw_update.apply_strategy(args.os_auth_url, args.os_project_name, + args.os_project_domain_name, + args.os_username, args.os_password, + args.os_user_domain_name, + args.os_region_name, args.os_interface, + sw_update.STRATEGY_NAME_SW_PATCH, + args.stage_id) + + elif 'abort' == args.cmd: + sw_update.abort_strategy(args.os_auth_url, args.os_project_name, + args.os_project_domain_name, + args.os_username, args.os_password, + args.os_user_domain_name, + args.os_region_name, args.os_interface, + sw_update.STRATEGY_NAME_SW_PATCH, + args.stage_id) + + elif 'show' == args.cmd: + sw_update.show_strategy(args.os_auth_url, args.os_project_name, + args.os_project_domain_name, + args.os_username, args.os_password, + args.os_user_domain_name, + args.os_region_name, args.os_interface, + sw_update.STRATEGY_NAME_SW_PATCH, + args.details) + + else: + raise ValueError("Unknown command, %s, given for patch-strategy" + % args.cmd) + elif 'upgrade-strategy' == args.cmd_area: + if 'create' == args.cmd: + sw_update.create_strategy( + args.os_auth_url, args.os_project_name, + args.os_project_domain_name, args.os_username, args.os_password, + args.os_user_domain_name, args.os_region_name, + args.os_interface, + sw_update.STRATEGY_NAME_SW_UPGRADE, + sw_update.APPLY_TYPE_IGNORE, + args.storage_apply_type, sw_update.APPLY_TYPE_IGNORE, + args.compute_apply_type, + args.max_parallel_compute_hosts, + None, args.alarm_restrictions, + # start_upgrade=args.start_upgrade, + complete_upgrade=args.complete_upgrade + ) + + elif 'delete' == args.cmd: + sw_update.delete_strategy(args.os_auth_url, args.os_project_name, + args.os_project_domain_name, + args.os_username, args.os_password, + args.os_user_domain_name, + args.os_region_name, args.os_interface, + sw_update.STRATEGY_NAME_SW_UPGRADE, + args.force) + + elif 'apply' == args.cmd: + sw_update.apply_strategy(args.os_auth_url, args.os_project_name, + args.os_project_domain_name, + args.os_username, args.os_password, + args.os_user_domain_name, + args.os_region_name, args.os_interface, + sw_update.STRATEGY_NAME_SW_UPGRADE, + args.stage_id) + + elif 'abort' == args.cmd: + sw_update.abort_strategy(args.os_auth_url, args.os_project_name, + args.os_project_domain_name, + args.os_username, args.os_password, + args.os_user_domain_name, + args.os_region_name, args.os_interface, + sw_update.STRATEGY_NAME_SW_UPGRADE, + args.stage_id) + + elif 'show' == args.cmd: + sw_update.show_strategy(args.os_auth_url, args.os_project_name, + args.os_project_domain_name, + args.os_username, args.os_password, + args.os_user_domain_name, + args.os_region_name, args.os_interface, + sw_update.STRATEGY_NAME_SW_UPGRADE, + args.details) + + else: + raise ValueError("Unknown command, %s, given for upgrade-strategy" + % args.cmd) + else: + raise ValueError("Unknown command area, %s, given" % args.cmd_area) + + except KeyboardInterrupt: + print("Keyboard Interrupt received.") + + except Exception as e: # pylint: disable=broad-except + print(e) + sys.exit(1) + + +if __name__ == "__main__": + process_main(sys.argv[1:]) diff --git a/nfv/nfv-client/nfv_client/sw_update/__init__.py b/nfv/nfv-client/nfv_client/sw_update/__init__.py new file mode 100755 index 00000000..954f8dc3 --- /dev/null +++ b/nfv/nfv-client/nfv_client/sw_update/__init__.py @@ -0,0 +1,20 @@ +# Copyright (c) 2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# flake8: noqa +# +from _sw_update import STRATEGY_NAME_SW_PATCH +from _sw_update import STRATEGY_NAME_SW_UPGRADE +from _sw_update import APPLY_TYPE_SERIAL +from _sw_update import APPLY_TYPE_PARALLEL +from _sw_update import APPLY_TYPE_IGNORE +from _sw_update import INSTANCE_ACTION_MIGRATE +from _sw_update import INSTANCE_ACTION_STOP_START +from _sw_update import ALARM_RESTRICTIONS_STRICT +from _sw_update import ALARM_RESTRICTIONS_RELAXED +from _sw_update import create_strategy +from _sw_update import delete_strategy +from _sw_update import apply_strategy +from _sw_update import abort_strategy +from _sw_update import show_strategy diff --git a/nfv/nfv-client/nfv_client/sw_update/_sw_update.py b/nfv/nfv-client/nfv_client/sw_update/_sw_update.py new file mode 100755 index 00000000..25b65be2 --- /dev/null +++ b/nfv/nfv-client/nfv_client/sw_update/_sw_update.py @@ -0,0 +1,292 @@ +# +# Copyright (c) 2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +from nfv_client.openstack import openstack +from nfv_client.openstack import sw_update + +STRATEGY_NAME_SW_PATCH = 'sw-patch' +STRATEGY_NAME_SW_UPGRADE = 'sw-upgrade' + +APPLY_TYPE_SERIAL = 'serial' +APPLY_TYPE_PARALLEL = 'parallel' +APPLY_TYPE_IGNORE = 'ignore' + +INSTANCE_ACTION_MIGRATE = 'migrate' +INSTANCE_ACTION_STOP_START = 'stop-start' + +ALARM_RESTRICTIONS_STRICT = 'strict' +ALARM_RESTRICTIONS_RELAXED = 'relaxed' + + +def _print(indent_by, field, value, remains=''): + print("%s%s%s%s %s" % (' ' * indent_by, field + ':', + ' ' * (42 - indent_by - len('%s' % field) - 1), value, + remains)) + + +def _display_strategy_step(strategy_step): + """ + Software Update - Display Strategy Step Information + """ + _print(12, "step-id", strategy_step.step_id) + _print(12, "step-name", strategy_step.step_name) + if 0 < len(strategy_step.entity_type): + _print(12, "entity-type", strategy_step.entity_type) + if 0 < len(strategy_step.entity_names): + _print(12, "entity-names", strategy_step.entity_names) + if 0 < len(strategy_step.entity_uuids): + _print(12, "entity-uuids", strategy_step.entity_uuids) + _print(12, "timeout", strategy_step.timeout, 'seconds') + if 0 < len(strategy_step.start_date_time): + _print(12, "start-date-time", strategy_step.start_date_time) + if 0 < len(strategy_step.end_date_time): + _print(12, "end-date-time", strategy_step.end_date_time) + _print(12, "result", strategy_step.result) + _print(12, "reason", strategy_step.reason) + + +def _display_strategy_stage(strategy_stage, details=False): + """ + Software Update - Display Strategy Stage Information + """ + _print(8, "stage-id", strategy_stage.stage_id) + _print(8, "stage-name", strategy_stage.stage_name) + _print(8, "total-steps", strategy_stage.total_steps) + _print(8, "current-step", strategy_stage.current_step) + _print(8, "timeout", strategy_stage.timeout, 'seconds') + _print(8, "start-date-time", strategy_stage.start_date_time) + if strategy_stage.inprogress: + _print(8, "inprogress", "true") + else: + _print(8, "end-date-time", strategy_stage.end_date_time) + _print(8, "result", strategy_stage.result) + _print(8, "reason", strategy_stage.reason) + + if details: + print(" steps:") + for step in strategy_stage.steps: + _display_strategy_step(step) + print("") + + +def _display_strategy_phase(strategy_phase, details=False): + """ + Software Update - Display Strategy Phase Information + """ + print(" %s-phase:" % strategy_phase.phase_name) + _print(4, "total-stages", strategy_phase.total_stages) + _print(4, "current-stage", strategy_phase.current_stage) + _print(4, "stop-at-stage", strategy_phase.stop_at_stage) + _print(4, "timeout", strategy_phase.timeout, 'seconds') + _print(4, "completion-percentage", + ("%s%%" % strategy_phase.completion_percentage)) + _print(4, "start-date-time", strategy_phase.start_date_time) + if strategy_phase.inprogress: + _print(4, "inprogress", "true") + else: + _print(4, "end-date-time", strategy_phase.end_date_time) + _print(4, "result", strategy_phase.result) + _print(4, "reason", strategy_phase.reason) + + if details: + print(" stages:") + for stage in strategy_phase.stages: + _display_strategy_stage(stage, details) + print("") + + +def _display_strategy(strategy, details=False): + """ + Software Update - Display Strategy Information + """ + if strategy.name == STRATEGY_NAME_SW_PATCH: + print("Strategy Patch Strategy:") + elif strategy.name == STRATEGY_NAME_SW_UPGRADE: + print("Strategy Upgrade Strategy:") + else: + print("Strategy Unknown Strategy:") + + _print(2, "strategy-uuid", strategy.uuid) + _print(2, "controller-apply-type", strategy.controller_apply_type) + _print(2, "storage-apply-type", strategy.storage_apply_type) + _print(2, "compute-apply-type", strategy.compute_apply_type) + if APPLY_TYPE_PARALLEL == strategy.compute_apply_type: + _print(2, "max-parallel-compute-hosts", + strategy.max_parallel_compute_hosts) + _print(2, "default-instance-action", strategy.default_instance_action) + _print(2, "alarm-restrictions", strategy.alarm_restrictions) + _print(2, "current-phase", strategy.current_phase) + _print(2, "current-phase-completion", + ("%s%%" % strategy.current_phase_completion_percentage)) + _print(2, "state", strategy.state) + + if details: + if 0 < strategy.build_phase.total_stages: + _display_strategy_phase(strategy.build_phase, details) + + if 0 < strategy.apply_phase.total_stages: + _display_strategy_phase(strategy.apply_phase, details) + + if 0 < strategy.abort_phase.total_stages: + _display_strategy_phase(strategy.abort_phase, details) + else: + if strategy.current_phase == strategy.build_phase.phase_name: + if strategy.build_phase.inprogress: + _print(2, "inprogress", "true") + else: + _print(2, "build-result", strategy.build_phase.result) + _print(2, "build-reason", strategy.build_phase.reason) + elif strategy.current_phase == strategy.apply_phase.phase_name: + if strategy.apply_phase.inprogress: + _print(2, "inprogress", "true") + else: + _print(2, "apply-result", strategy.apply_phase.result) + _print(2, "apply-reason", strategy.apply_phase.reason) + elif strategy.current_phase == strategy.abort_phase.phase_name: + if strategy.abort_phase.inprogress: + _print(2, "inprogress", "true") + _print(2, "apply-result", strategy.apply_phase.result) + _print(2, "apply-reason", strategy.apply_phase.reason) + _print(2, "abort-result", "") + _print(2, "abort-reason", "") + else: + _print(2, "apply-result", strategy.apply_phase.result) + _print(2, "apply-reason", strategy.apply_phase.reason) + _print(2, "abort-result", strategy.abort_phase.result) + _print(2, "abort-reason", strategy.abort_phase.reason) + + +def create_strategy(os_auth_uri, os_project_name, os_project_domain_name, + os_username, os_password, os_user_domain_name, + os_region_name, os_interface, + strategy_name, controller_apply_type, + storage_apply_type, swift_apply_type, compute_apply_type, + max_parallel_compute_hosts, + default_instance_action, alarm_restrictions, **kwargs): + """ + Software Update - Create Strategy + """ + token = openstack.get_token(os_auth_uri, os_project_name, + os_project_domain_name, os_username, os_password, + os_user_domain_name) + + url = token.get_service_url(os_region_name, openstack.SERVICE.VIM, + openstack.SERVICE_TYPE.NFV, os_interface) + if url is None: + raise ValueError("NFV-VIM URL is invalid") + + strategy = sw_update.create_strategy(token.get_id(), url, + strategy_name, + controller_apply_type, + storage_apply_type, swift_apply_type, + compute_apply_type, + max_parallel_compute_hosts, + default_instance_action, + alarm_restrictions, + **kwargs) + if not strategy: + raise Exception("Strategy creation failed") + + _display_strategy(strategy) + + +def delete_strategy(os_auth_uri, os_project_name, os_project_domain_name, + os_username, os_password, os_user_domain_name, os_region_name, + os_interface, strategy_name, force=False): + """ + Software Update - Delete Strategy + """ + token = openstack.get_token(os_auth_uri, os_project_name, + os_project_domain_name, os_username, os_password, + os_user_domain_name) + + url = token.get_service_url(os_region_name, openstack.SERVICE.VIM, + openstack.SERVICE_TYPE.NFV, os_interface) + if url is None: + raise ValueError("NFV-VIM URL is invalid") + + success = sw_update.delete_strategy(token.get_id(), url, + strategy_name, force) + if success: + print("Strategy deleted") + return + else: + raise Exception("Strategy delete failed") + + +def apply_strategy(os_auth_uri, os_project_name, os_project_domain_name, + os_username, os_password, os_user_domain_name, os_region_name, + os_interface, strategy_name, stage_id=None): + """ + Software Update - Apply Strategy + """ + token = openstack.get_token(os_auth_uri, os_project_name, + os_project_domain_name, os_username, os_password, + os_user_domain_name) + + url = token.get_service_url(os_region_name, openstack.SERVICE.VIM, + openstack.SERVICE_TYPE.NFV, os_interface) + if url is None: + raise ValueError("NFV-VIM URL is invalid") + + strategy = sw_update.apply_strategy(token.get_id(), url, + strategy_name, stage_id) + if not strategy: + if stage_id is None: + raise Exception("Strategy apply failed") + else: + raise Exception("Strategy stage %s apply failed" % stage_id) + + _display_strategy(strategy) + + +def abort_strategy(os_auth_uri, os_project_name, os_project_domain_name, + os_username, os_password, os_user_domain_name, os_region_name, + os_interface, strategy_name, stage_id=None): + """ + Software Update - Abort Strategy + """ + token = openstack.get_token(os_auth_uri, os_project_name, + os_project_domain_name, os_username, os_password, + os_user_domain_name) + + url = token.get_service_url(os_region_name, openstack.SERVICE.VIM, + openstack.SERVICE_TYPE.NFV, os_interface) + if url is None: + raise ValueError("NFV-VIM URL is invalid") + + strategy = sw_update.abort_strategy(token.get_id(), url, + strategy_name, stage_id) + if not strategy: + if stage_id is None: + raise Exception("Strategy abort failed") + else: + raise Exception("Strategy stage %s abort failed" % stage_id) + + _display_strategy(strategy) + + +def show_strategy(os_auth_uri, os_project_name, os_project_domain_name, + os_username, os_password, os_user_domain_name, os_region_name, + os_interface, strategy_name, details=False): + """ + Software Update - Show Strategy + """ + token = openstack.get_token(os_auth_uri, os_project_name, + os_project_domain_name, os_username, os_password, + os_user_domain_name) + + url = token.get_service_url(os_region_name, openstack.SERVICE.VIM, + openstack.SERVICE_TYPE.NFV, os_interface) + if url is None: + raise ValueError("NFV-VIM URL is invalid") + + strategy = sw_update.get_strategies(token.get_id(), url, + strategy_name) + if not strategy: + print("No strategy available") + return + + _display_strategy(strategy, details) diff --git a/nfv/nfv-client/scripts/sw-manager.completion b/nfv/nfv-client/scripts/sw-manager.completion new file mode 100755 index 00000000..e7c00e04 --- /dev/null +++ b/nfv/nfv-client/scripts/sw-manager.completion @@ -0,0 +1,190 @@ +# +# Copyright (c) 2016-2017 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +# +# This file provides bash-completion functionality for the sw-manager CLI +# + +function _swmanager() +{ + COMPREPLY=() + local cur="${COMP_WORDS[COMP_CWORD]}" + local prev="${COMP_WORDS[COMP_CWORD-1]}" + local subcommand=${COMP_WORDS[1]} + + # + # The available sw-manager subcommands + # + local subcommands=" + patch-strategy + upgrade-strategy + " + + if [ $COMP_CWORD -gt 1 ]; then + # + # Complete the arguments to the subcommands + # + case "$subcommand" in + patch-strategy) + local actions=" + create + delete + apply + abort + show + " + if [ $COMP_CWORD -gt 2 ]; then + local action=${COMP_WORDS[2]} + # + # Complete the arguments for each action + # + case "$action" in + create) + local createopts=" + --controller-apply-type + --storage-apply-type + --compute-apply-type + --max-parallel-compute-hosts + --instance-action + --alarm-restrictions + " + local createopt=${prev} + case "$createopt" in + --controller-apply-type) + COMPREPLY=($(compgen -W "serial ignore" -- ${cur})) + return 0 + ;; + --storage-apply-type|--compute-apply-type) + COMPREPLY=($(compgen -W "serial parallel ignore" -- ${cur})) + return 0 + ;; + --max-parallel-compute-hosts) + COMPREPLY=( $(compgen -- ${cur})) + return 0 + ;; + --instance-action) + COMPREPLY=($(compgen -W "migrate stop-start" -- ${cur})) + return 0 + ;; + --alarm-restrictions) + COMPREPLY=($(compgen -W "strict relaxed" -- ${cur})) + return 0 + ;; + *) + ;; + esac + COMPREPLY=($(compgen -W "${createopts}" -- ${cur})) + return 0 + ;; + apply|abort) + if [ "${prev}" = "${action}" ]; then + COMPREPLY=($(compgen -W "--stage-id" -- ${cur})) + fi + return 0 + ;; + show) + if [ "${prev}" = "${action}" ]; then + COMPREPLY=($(compgen -W "--details" -- ${cur})) + fi + return 0 + ;; + delete) + # These subcommands have no options/arguments + COMPREPLY=( $(compgen -- ${cur}) ) + return 0 + ;; + *) + ;; + esac + fi + + # Provide actions for completion + COMPREPLY=($(compgen -W "${actions}" -- ${cur})) + return 0 + ;; + upgrade-strategy) + local actions=" + create + delete + apply + abort + show + " + if [ $COMP_CWORD -gt 2 ]; then + local action=${COMP_WORDS[2]} + # + # Complete the arguments for each action + # + case "$action" in + create) + local createopts=" + --storage-apply-type + --compute-apply-type + --max-parallel-compute-hosts + --alarm-restrictions + " + local createopt=${prev} + case "$createopt" in + --storage-apply-type) + COMPREPLY=($(compgen -W "serial parallel ignore" -- ${cur})) + return 0 + ;; + --compute-apply-type) + COMPREPLY=($(compgen -W "serial parallel ignore" -- ${cur})) + return 0 + ;; + --max-parallel-compute-hosts) + COMPREPLY=( $(compgen -- ${cur})) + return 0 + ;; + --alarm-restrictions) + COMPREPLY=($(compgen -W "strict relaxed" -- ${cur})) + return 0 + ;; + *) + ;; + esac + COMPREPLY=($(compgen -W "${createopts}" -- ${cur})) + return 0 + ;; + apply|abort) + if [ "${prev}" = "${action}" ]; then + COMPREPLY=($(compgen -W "--stage-id" -- ${cur})) + fi + return 0 + ;; + show) + if [ "${prev}" = "${action}" ]; then + COMPREPLY=($(compgen -W "--details" -- ${cur})) + fi + return 0 + ;; + delete) + # These subcommands have no options/arguments + COMPREPLY=( $(compgen -- ${cur}) ) + return 0 + ;; + *) + ;; + esac + fi + + # Provide actions for completion + COMPREPLY=($(compgen -W "${actions}" -- ${cur})) + return 0 + ;; + *) + ;; + esac + fi + + # Provide subcommands for completion + COMPREPLY=($(compgen -W "${subcommands}" -- ${cur})) + return 0 +} + +# Bind the above function to the sw-manager CLI +complete -F _swmanager -o filenames sw-manager diff --git a/nfv/nfv-client/setup.py b/nfv/nfv-client/setup.py new file mode 100755 index 00000000..0a9f94e2 --- /dev/null +++ b/nfv/nfv-client/setup.py @@ -0,0 +1,21 @@ +# +# Copyright (c) 2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +from setuptools import setup, find_packages + +setup( + name='nfv_client', + description='NFV Client', + version='1.0.0', + license='Apache-2.0', + platforms=['any'], + provides='nfv_client', + packages=find_packages(), + entry_points={ + 'console_scripts': [ + 'sw-manager = nfv_client.shell:process_main', + ], + } +) diff --git a/nfv/nfv-common/LICENSE b/nfv/nfv-common/LICENSE new file mode 100755 index 00000000..d6456956 --- /dev/null +++ b/nfv/nfv-common/LICENSE @@ -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. diff --git a/nfv/nfv-common/nfv_common/__init__.py b/nfv/nfv-common/nfv_common/__init__.py new file mode 100755 index 00000000..02083bd0 --- /dev/null +++ b/nfv/nfv-common/nfv_common/__init__.py @@ -0,0 +1,5 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# diff --git a/nfv/nfv-common/nfv_common/alarm/__init__.py b/nfv/nfv-common/nfv_common/alarm/__init__.py new file mode 100755 index 00000000..a5a3f12b --- /dev/null +++ b/nfv/nfv-common/nfv_common/alarm/__init__.py @@ -0,0 +1,19 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# flake8: noqa +# +from objects.v1 import ALARM_CONTEXT +from objects.v1 import ALARM_TYPE +from objects.v1 import ALARM_EVENT_TYPE +from objects.v1 import ALARM_PROBABLE_CAUSE +from objects.v1 import ALARM_SEVERITY +from objects.v1 import ALARM_TREND_INDICATION +from objects.v1 import AlarmStateData +from objects.v1 import AlarmThresholdData +from objects.v1 import AlarmData + +from _alarm_module import alarm_raise, alarm_clear +from _alarm_module import alarm_subsystem_sane, alarm_initialize, alarm_finalize diff --git a/nfv/nfv-common/nfv_common/alarm/_alarm_handlers.py b/nfv/nfv-common/nfv_common/alarm/_alarm_handlers.py new file mode 100755 index 00000000..21a453c2 --- /dev/null +++ b/nfv/nfv-common/nfv_common/alarm/_alarm_handlers.py @@ -0,0 +1,87 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import six +import stevedore + +from nfv_common import debug +from nfv_common.helpers import Singleton + +DLOG = debug.debug_get_logger('nfv_common.alarm.alarm_handlers') + + +@six.add_metaclass(Singleton) +class AlarmHandlers(stevedore.enabled.EnabledExtensionManager): + """ + Alarm Handlers + """ + _version = '1.0.0' + _signature = 'e33d7cf6-f270-4256-893e-16266ee4dd2e' + + def __init__(self, namespace, handler_names): + super(AlarmHandlers, self).__init__(namespace, + AlarmHandlers.valid_handler, + invoke_on_load=True, + invoke_args=(), invoke_kwds={}) + + handler_list = [handler.strip() for handler in handler_names.split(',')] + + self._handlers = {} + for handler in self: + if handler.obj.name in handler_list: + handler_id = handler.obj.provider + ':' + handler.obj.name + if handler_id not in self._handlers: + self._handlers[handler_id] = handler + DLOG.info("Loaded handler %s version %s provided by %s." + % (handler.obj.name, handler.obj.version, + handler.obj.provider)) + + @staticmethod + def valid_handler(handler): + """ + Verify signature of the handler is valid + """ + if AlarmHandlers._signature == handler.obj.signature: + return True + else: + DLOG.info("Handler %s version %s from provider %s has an invalid " + "signature." % (handler.obj.name, handler.obj.version, + handler.obj.provider)) + return False + + def raise_alarm(self, alarm_uuid, alarm_data): + """ + Raise an alarm using the handlers + """ + for handler_type, handler in self._handlers.iteritems(): + handler.obj.raise_alarm(alarm_uuid, alarm_data) + + def clear_alarm(self, alarm_uuid): + """ + Clear an alarm using the handlers + """ + for handler_type, handler in self._handlers.iteritems(): + handler.obj.clear_alarm(alarm_uuid) + + def audit_alarms(self): + """ + Audit alarms using the handlers + """ + for handler_type, handler in self._handlers.iteritems(): + handler.obj.audit_alarms() + + def initialize(self, config_file): + """ + Initialize handlers + """ + for handler_id, handler in self._handlers.iteritems(): + handler.obj.initialize(config_file) + + def finalize(self): + """ + Finalize handlers + """ + for handler_id, handler in self._handlers.iteritems(): + handler.obj.finalize() diff --git a/nfv/nfv-common/nfv_common/alarm/_alarm_module.py b/nfv/nfv-common/nfv_common/alarm/_alarm_module.py new file mode 100755 index 00000000..9692f501 --- /dev/null +++ b/nfv/nfv-common/nfv_common/alarm/_alarm_module.py @@ -0,0 +1,48 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +from nfv_common import debug + +DLOG = debug.debug_get_logger('nfv_common.alarm.alarm_module') + + +def alarm_raise(alarm_uuid, alarm_data): + """ + Raise an alarm + """ + from _alarm_thread import AlarmThread + AlarmThread().alarm_raise(alarm_uuid, alarm_data) + + +def alarm_clear(alarm_uuid): + """ + Clear an alarm + """ + from _alarm_thread import AlarmThread + AlarmThread().alarm_clear(alarm_uuid) + + +def alarm_subsystem_sane(): + """ + Returns true if the alarm subsystem is healthy + """ + from _alarm_thread import AlarmThread + return 600 >= AlarmThread().stall_elapsed_secs + + +def alarm_initialize(config): + """ + Initialize the alarm subsystem + """ + from _alarm_thread import AlarmThread + AlarmThread(config).start() + + +def alarm_finalize(): + """ + Finalize the alarm subsystem + """ + from _alarm_thread import AlarmThread + AlarmThread().stop(max_wait_in_seconds=5) diff --git a/nfv/nfv-common/nfv_common/alarm/_alarm_thread.py b/nfv/nfv-common/nfv_common/alarm/_alarm_thread.py new file mode 100755 index 00000000..42252ded --- /dev/null +++ b/nfv/nfv-common/nfv_common/alarm/_alarm_thread.py @@ -0,0 +1,107 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import six +import datetime + +from nfv_common import debug +from nfv_common import thread +from nfv_common import timers +from nfv_common.helpers import Singleton, coroutine + +from _alarm_handlers import AlarmHandlers + +DLOG = debug.debug_get_logger('nfv_common.alarm.alarm_thread') + + +@six.add_metaclass(Singleton) +class AlarmWorker(thread.ThreadWorker): + """ + Alarm Worker + """ + def __init__(self, name, config): + super(AlarmWorker, self).__init__(name) + self._config = config + self._handlers = None + self._alarm_audit_timer_id = None + + @coroutine + def _alarm_audit(self): + """ + Called periodically to audit alarms + """ + while True: + (yield) + self._handlers.audit_alarms() + + def initialize(self): + """ + Initialize the Alarm Worker + """ + self._handlers = AlarmHandlers(self._config['namespace'], + self._config['handlers']) + self._handlers.initialize(self._config['config_file']) + + self._alarm_audit_timer_id = timers.timers_create_timer( + 'alarm_audit', int(self._config['audit_interval']), + int(self._config['audit_interval']), self._alarm_audit) + + def finalize(self): + """ + Finalize the Alarm Worker + """ + if self._handlers is not None: + self._handlers.finalize() + + if self._alarm_audit_timer_id is not None: + timers.timers_delete_timer(self._alarm_audit_timer_id) + + def do_work(self, action, work): + """ + Do work given to the Alarm Worker + """ + if AlarmThread.ACTION_RAISE_ALARM == action: + DLOG.verbose("Raise alarm with uuid=%s" % work['alarm-uuid']) + self._handlers.raise_alarm(work['alarm-uuid'], work['alarm-data']) + + elif AlarmThread.ACTION_CLEAR_ALARM == action: + DLOG.verbose("Clear alarm with uuid=%s" % work['alarm-uuid']) + self._handlers.clear_alarm(work['alarm-uuid']) + + else: + DLOG.debug("Unknown action %s given." % action) + + +@six.add_metaclass(Singleton) +class AlarmThread(thread.Thread): + """ + Alarm Thread + """ + ACTION_RAISE_ALARM = "thread-raise-alarm" + ACTION_CLEAR_ALARM = "thread-clear-alarm" + + def __init__(self, config=None): + self._worker = AlarmWorker('Alarm', config) + super(AlarmThread, self).__init__('Alarm', self._worker) + + def alarm_raise(self, alarm_uuid, alarm_data): + """ + Send raise alarm to the Alarm Thread Worker + """ + work = dict() + work['alarm-uuid'] = alarm_uuid + work['alarm-data'] = alarm_data + work['alarm-change-date'] = datetime.datetime.utcnow() + self.send_work(AlarmThread.ACTION_RAISE_ALARM, work) + + def alarm_clear(self, alarm_uuid): + """ + Send clear alarm to the Alarm Thread Worker + """ + work = dict() + work['alarm-uuid'] = alarm_uuid + work['alarm-data'] = None + work['alarm-change-date'] = datetime.datetime.utcnow() + self.send_work(AlarmThread.ACTION_CLEAR_ALARM, work) diff --git a/nfv/nfv-common/nfv_common/alarm/handlers/__init__.py b/nfv/nfv-common/nfv_common/alarm/handlers/__init__.py new file mode 100755 index 00000000..02083bd0 --- /dev/null +++ b/nfv/nfv-common/nfv_common/alarm/handlers/__init__.py @@ -0,0 +1,5 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# diff --git a/nfv/nfv-common/nfv_common/alarm/handlers/v1/__init__.py b/nfv/nfv-common/nfv_common/alarm/handlers/v1/__init__.py new file mode 100755 index 00000000..f3fb86b2 --- /dev/null +++ b/nfv/nfv-common/nfv_common/alarm/handlers/v1/__init__.py @@ -0,0 +1,8 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# flake8: noqa +# +from _alarm_handler import AlarmHandler diff --git a/nfv/nfv-common/nfv_common/alarm/handlers/v1/_alarm_handler.py b/nfv/nfv-common/nfv_common/alarm/handlers/v1/_alarm_handler.py new file mode 100755 index 00000000..bd4336c8 --- /dev/null +++ b/nfv/nfv-common/nfv_common/alarm/handlers/v1/_alarm_handler.py @@ -0,0 +1,58 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import abc +import six + + +@six.add_metaclass(abc.ABCMeta) +class AlarmHandler(object): + """ + Abstract Alarm Handler Class Definition + """ + @abc.abstractproperty + def name(self): + """ The name of handler """ + pass + + @abc.abstractproperty + def version(self): + """ The versions of the handler """ + pass + + @abc.abstractproperty + def provider(self): + """ Who created the handler """ + pass + + @abc.abstractproperty + def signature(self): + """ Signature of the handler """ + pass + + @abc.abstractmethod + def raise_alarm(self, alarm_uuid, alarm_data): + """ Raise an alarm via the handler """ + pass + + @abc.abstractmethod + def clear_alarm(self, alarm_uuid): + """ Clear an alarm via the handler """ + pass + + @abc.abstractmethod + def audit_alarms(self): + """ Audit alarms via the handler """ + pass + + @abc.abstractmethod + def initialize(self, config_file): + """ Initialize the handler """ + pass + + @abc.abstractmethod + def finalize(self): + """ Finalize the handler """ + pass diff --git a/nfv/nfv-common/nfv_common/alarm/objects/__init__.py b/nfv/nfv-common/nfv_common/alarm/objects/__init__.py new file mode 100755 index 00000000..90b33192 --- /dev/null +++ b/nfv/nfv-common/nfv_common/alarm/objects/__init__.py @@ -0,0 +1,8 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# flake8: noqa +# +import v1 diff --git a/nfv/nfv-common/nfv_common/alarm/objects/v1/__init__.py b/nfv/nfv-common/nfv_common/alarm/objects/v1/__init__.py new file mode 100755 index 00000000..25dc7c7c --- /dev/null +++ b/nfv/nfv-common/nfv_common/alarm/objects/v1/__init__.py @@ -0,0 +1,10 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# flake8: noqa +# +from _alarm_defs import ALARM_CONTEXT, ALARM_TYPE, ALARM_EVENT_TYPE +from _alarm_defs import ALARM_PROBABLE_CAUSE, ALARM_SEVERITY, ALARM_TREND_INDICATION +from _alarm_data import AlarmData, AlarmStateData, AlarmThresholdData diff --git a/nfv/nfv-common/nfv_common/alarm/objects/v1/_alarm_data.py b/nfv/nfv-common/nfv_common/alarm/objects/v1/_alarm_data.py new file mode 100755 index 00000000..c5fd1046 --- /dev/null +++ b/nfv/nfv-common/nfv_common/alarm/objects/v1/_alarm_data.py @@ -0,0 +1,66 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +from datetime import datetime + + +class AlarmStateData(object): + """ + Alarm State Data + """ + def __init__(self, state): + self.state = state + + def as_dict(self): + return dict.copy(self.__dict__) + + +class AlarmThresholdData(object): + """ + Alarm Threshold Data + """ + def __init__(self, threshold_value, observed_value): + self.threshold_value = threshold_value + self.observed_value = observed_value + + def as_dict(self): + return dict.copy(self.__dict__) + + +class AlarmData(object): + """ + Alarm Data + """ + + def __init__(self, alarm_uuid, alarm_type, alarm_context, entity_type, + entity, event_type, probable_cause, perceived_severity, + trend_indication, specific_problem_text, proposed_repair_action, + additional_text="", state_data=None, threshold_data=None, + service_affecting=True, suppression_allowed=True, + raised_timestamp=None): + + self.alarm_uuid = alarm_uuid + self.alarm_type = alarm_type + self.alarm_context = alarm_context + self.entity_type = entity_type + self.entity = entity + self.event_type = event_type + self.probable_cause = probable_cause + self.perceived_severity = perceived_severity + self.trend_indication = trend_indication + self.specific_problem_text = specific_problem_text + self.proposed_repair_action = proposed_repair_action + self.additional_text = additional_text + self.state_data = state_data + self.threshold_data = threshold_data + self.service_affecting = service_affecting + self.suppression_allowed = suppression_allowed + self.created_timestamp = datetime.utcnow() + self.raised_timestamp = raised_timestamp + self.changed_timestamp = None + self.cleared_timestamp = None + + def as_dict(self): + return dict.copy(self.__dict__) diff --git a/nfv/nfv-common/nfv_common/alarm/objects/v1/_alarm_defs.py b/nfv/nfv-common/nfv_common/alarm/objects/v1/_alarm_defs.py new file mode 100755 index 00000000..cc816753 --- /dev/null +++ b/nfv/nfv-common/nfv_common/alarm/objects/v1/_alarm_defs.py @@ -0,0 +1,116 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import six + +from nfv_common.helpers import Constants, Constant, Singleton + + +@six.add_metaclass(Singleton) +class _AlarmType(Constants): + """ + Alarm Type Constants + """ + UNKNOWN = Constant('unknown') + MULTI_NODE_RECOVERY_MODE = Constant('multi-node-recovery-mode') + HOST_SERVICES_FAILED = Constant('host-services-failed') + INSTANCE_FAILED = Constant('instance-failed') + INSTANCE_SCHEDULING_FAILED = Constant('instance-scheduling-failed') + INSTANCE_STOPPED = Constant('instance-stopped') + INSTANCE_REBOOTING = Constant('instance-rebooting') + INSTANCE_PAUSED = Constant('instance-paused') + INSTANCE_SUSPENDED = Constant('instance-suspended') + INSTANCE_EVACUATING = Constant('instance-evacuating') + INSTANCE_REBUILDING = Constant('instance-rebuilding') + INSTANCE_LIVE_MIGRATING = Constant('instance-live-migrating') + INSTANCE_COLD_MIGRATING = Constant('instance-cold-migrating') + INSTANCE_COLD_MIGRATED = Constant('instance-cold-migrated') + INSTANCE_COLD_MIGRATE_REVERTING = Constant('instance-cold-migrate-reverting') + INSTANCE_RESIZING = Constant('instance-resizing') + INSTANCE_RESIZED = Constant('instance-resized') + INSTANCE_RESIZE_REVERTING = Constant('instance-resize-reverting') + INSTANCE_GUEST_HEARTBEAT = Constant('instance-guest-heartbeat') + INSTANCE_GROUP_POLICY_CONFLICT = Constant('instance-group-policy-conflict') + SW_PATCH_AUTO_APPLY_INPROGRESS = Constant('sw-patch-auto-apply-inprogress') + SW_PATCH_AUTO_APPLY_ABORTING = Constant('sw-patch-auto-apply-aborting') + SW_PATCH_AUTO_APPLY_FAILED = Constant('sw-patch-auto-apply-failed') + SW_UPGRADE_AUTO_APPLY_INPROGRESS = Constant('sw-upgrade-auto-apply-inprogress') + SW_UPGRADE_AUTO_APPLY_ABORTING = Constant('sw-upgrade-auto-apply-aborting') + SW_UPGRADE_AUTO_APPLY_FAILED = Constant('sw-upgrade-auto-apply-failed') + + +@six.add_metaclass(Singleton) +class _AlarmContext(Constants): + """ + Alarm Context Constants + """ + ADMIN = Constant('admin') + TENANT = Constant('tenant') + + +@six.add_metaclass(Singleton) +class _AlarmEventType(Constants): + """ + Alarm Event Type Constants + """ + UNKNOWN = Constant('unknown') + COMMUNICATIONS_ALARM = Constant('communications-alarm') + PROCESSING_ERROR_ALARM = Constant('processing-error-alarm') + ENVIRONMENTAL_ALARM = Constant('environmental-alarm') + QUALITY_OF_SERVICE_ALARM = Constant('quality-of-service-alarm') + EQUIPMENT_ALARM = Constant('equipment-alarm') + INTEGRITY_VIOLATION = Constant('integrity-violation') + OPERATIONAL_VIOLATION = Constant('operational-violation') + PHYSICAL_VIOLATION = Constant('physical-violation') + SECURITY_SERVICE_VIOLATION = Constant('security-service-violation') + MECHANISM_VIOLATION = Constant('mechanism-violation') + TIME_DOMAIN_VIOLATION = Constant('time-domain-violation') + + +@six.add_metaclass(Singleton) +class _AlarmProbableCause(Constants): + """ + Alarm Probable Cause Constants + """ + UNKNOWN = Constant('unknown') + INDETERMINATE = Constant('indeterminate') + SOFTWARE_ERROR = Constant('software-error') + SOFTWARE_PROGRAM_ERROR = Constant('software-program-error') + UNDERLYING_RESOURCE_UNAVAILABLE = Constant('underlying-resource-unavailable') + PROCEDURAL_ERROR = Constant('procedural-error') + + +@six.add_metaclass(Singleton) +class _AlarmSeverity(Constants): + """ + Alarm Severity Constants + """ + UNKNOWN = Constant('unknown') + CLEARED = Constant('cleared') + INDETERMINATE = Constant('indeterminate') + WARNING = Constant('warning') + MINOR = Constant('minor') + MAJOR = Constant('major') + CRITICAL = Constant('critical') + + +@six.add_metaclass(Singleton) +class _AlarmTrendIndication(Constants): + """ + Alarm Trend Indication Constants + """ + UNKNOWN = Constant('unknown') + LESS_SEVERE = Constant('less-severe') + NO_CHANGE = Constant('no-change') + MORE_SEVERE = Constant('more-severe') + + +# Constant Instantiation +ALARM_TYPE = _AlarmType() +ALARM_CONTEXT = _AlarmContext() +ALARM_EVENT_TYPE = _AlarmEventType() +ALARM_PROBABLE_CAUSE = _AlarmProbableCause() +ALARM_SEVERITY = _AlarmSeverity() +ALARM_TREND_INDICATION = _AlarmTrendIndication() diff --git a/nfv/nfv-common/nfv_common/catalog/__init__.py b/nfv/nfv-common/nfv_common/catalog/__init__.py new file mode 100755 index 00000000..01975855 --- /dev/null +++ b/nfv/nfv-common/nfv_common/catalog/__init__.py @@ -0,0 +1,8 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# flake8: noqa +# +import model diff --git a/nfv/nfv-common/nfv_common/catalog/_catalog_backend.py b/nfv/nfv-common/nfv_common/catalog/_catalog_backend.py new file mode 100755 index 00000000..7394e2b4 --- /dev/null +++ b/nfv/nfv-common/nfv_common/catalog/_catalog_backend.py @@ -0,0 +1,65 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import six +import stevedore + +from nfv_common import debug +from nfv_common.helpers import Singleton + +DLOG = debug.debug_get_logger('nfv_common.catalog.catalog_backend') + + +@six.add_metaclass(Singleton) +class CatalogBackend(stevedore.named.NamedExtensionManager): + """ + Catalog Backend + """ + _version = '1.0.0' + _signature = '7926ef8d-b04c-4f5b-8627-f40f59fd8d11' + + def __init__(self, plugin_namespace, plugin_name): + super(CatalogBackend, self).__init__(plugin_namespace, plugin_name, + invoke_on_load=True, + invoke_args=(), invoke_kwds={}) + self.plugin = None + + for plugin in self: + if self.valid_plugin(plugin): + self.plugin = plugin + DLOG.info("Loaded plugin %s version %s provided by %s." + % (plugin.obj.name, plugin.obj.version, + plugin.obj.provider)) + break + + @staticmethod + def valid_plugin(plugin): + """ Verify signature of plugin is valid """ + if CatalogBackend._signature == plugin.obj.signature: + return True + else: + DLOG.info("Plugin %s version %s from provider %s has an invalid " + "signature." % (plugin.obj.name, plugin.obj.version, + plugin.obj.provider)) + return False + + def read_vnf_descriptor(self, vnfd_id, vnf_vendor, vnf_version): + """ Read a particular vnf descriptor """ + vnfd_record = None + if self.plugin is not None: + vnfd_record = self.plugin.obj.read_vnf_descriptor(vnfd_id, + vnf_vendor, + vnf_version) + return vnfd_record + + def initialize(self): + """ Initialize plugin """ + if self.plugin is not None: + self.plugin.obj.initialize(self._version) + + def finalize(self): + """ Finalize plugin """ + if self.plugin is not None: + self.plugin.obj.finalize() diff --git a/nfv/nfv-common/nfv_common/catalog/_catalog_module.py b/nfv/nfv-common/nfv_common/catalog/_catalog_module.py new file mode 100755 index 00000000..41e3e7ce --- /dev/null +++ b/nfv/nfv-common/nfv_common/catalog/_catalog_module.py @@ -0,0 +1,28 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +from _catalog_backend import CatalogBackend + +_catalog_backend = None + + +def read_vnf_descriptor(vnfd_id, vnf_vendor, vnf_version): + """ Read a vnf descriptor """ + if _catalog_backend is not None: + return _catalog_backend.read_vnf_descriptor(vnfd_id, vnf_vendor, + vnf_version) + return None + + +def catalog_initialize(plugin_namespace, plugin_name): + """ Catalog Initialize """ + global _catalog_backend + _catalog_backend = CatalogBackend(plugin_namespace, plugin_name) + + +def catalog_finalize(): + """ Catalog Finalize """ + global _catalog_backend + _catalog_backend = None diff --git a/nfv/nfv-common/nfv_common/catalog/model/__init__.py b/nfv/nfv-common/nfv_common/catalog/model/__init__.py new file mode 100755 index 00000000..05ed99b3 --- /dev/null +++ b/nfv/nfv-common/nfv_common/catalog/model/__init__.py @@ -0,0 +1,11 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# flake8: noqa +# +from _defs import CONNECTION_TYPE, CONNECTIVITY_TYPE +from _vnf_descriptor import VNFD, ConnectionPointVNFD, VirtualLink +from _vnf_descriptor import DeploymentFlavor, ConstituentVDU +from _vnf_descriptor import VDU, VNFC, ConnectionPointVNFC diff --git a/nfv/nfv-common/nfv_common/catalog/model/_defs.py b/nfv/nfv-common/nfv_common/catalog/model/_defs.py new file mode 100755 index 00000000..f35a6f82 --- /dev/null +++ b/nfv/nfv-common/nfv_common/catalog/model/_defs.py @@ -0,0 +1,35 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import six +from nfv_common.helpers import Constant, Singleton + + +@six.add_metaclass(Singleton) +class ConnectionType(object): + """ + Connection Type Constants + """ + UNKNOWN = Constant('unknown') + VIRTUAL_PORT = Constant('virtual-port') + VIRTUAL_NIC_ADDRESS = Constant('virtual-nic-address') + PHYSICAL_PORT = Constant('physical-port') + PHYSICAL_NIC_ADDRESS = Constant('physical-nic-address') + + +@six.add_metaclass(Singleton) +class ConnectivityType(object): + """ + Connectivity Type Constants + """ + UNKNOWN = Constant('unknown') + E_LINE = Constant('E-Line') + E_LAN = Constant('E-LAN') + E_TREE = Constant('E-Tree') + + +# Constant Instantiation +CONNECTION_TYPE = ConnectionType() +CONNECTIVITY_TYPE = ConnectivityType() diff --git a/nfv/nfv-common/nfv_common/catalog/model/_vnf_descriptor.py b/nfv/nfv-common/nfv_common/catalog/model/_vnf_descriptor.py new file mode 100755 index 00000000..1def1d8c --- /dev/null +++ b/nfv/nfv-common/nfv_common/catalog/model/_vnf_descriptor.py @@ -0,0 +1,179 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +from _defs import CONNECTIVITY_TYPE, CONNECTION_TYPE + +# +# Virtual Network Function Descriptor - Class Layout +# +# VNFD -+------ ConnectionPointVNFD +# | +# +------ VirtualLink +# | +# +------ DeploymentFlavor ------ ConstituentVDU +# | +# +------ VDU ------ VNFC ------ ConnectionPointVNFC +# + + +class VNFD(object): + """ + Virtual Network Function Descriptor + id: unique name for this vnfd + vendor: who created this vnfd + descriptor_version: the version of the vnf descriptor + version: the version of the vnf software + vdu: one or more VDU() objects + virtual_link: zero or more VirtualLink() objects + connection_point: one or more connection points described by + ConnectionPointVNFD() objects + dependency: describes dependencies between vdus + monitoring_parameter: monitoring parameters which can be tracked for + this vnf + deployment_flavor: one or more DeploymentFlavor objects + auto_scale_policy: describes the policy in terms of criteria and action + manifest_file: a file that lists all files in the vnf package + manifest_file_security: a file that contains a digest of each file that + it lists as part of the vnf package + """ + def __init__(self, id): + self.id = id + self.vendor = None + self.descriptor_version = None + self.version = None + self.vdu = [] + self.virtual_link = [] + self.connection_point = [] + self.dependency = [] + self.monitoring_parameter = [] + self.deployment_flavor = [] + self.auto_scale_policy = [] + self.manifest_file = [] + self.manifest_file_security = [] + + +class ConnectionPointVNFD(object): + """ + Connection Point of a Virtual Network Function Descriptor + id: connection point identifier + virtual_link_reference: references zero or more virtual links by their + identifiers + type: type of connection + """ + def __init__(self, id): + self.id = id + self.virtual_link_reference = None + self.type = CONNECTION_TYPE.UNKNOWN + + +class VirtualLink(object): + """ + Virtual Link + id: unique identifier of this internal virtual link + connectivity_type: the type of connectivity + connection_points_references: 2 or more connection point identifiers + root_requirement: describes the throughput of the link + leaf_requirement: describes the throughput of the leaf connections + qos: describes the qos options to be supported on the virtual link + test_access: describes the test access facilities to be supported on + the virtual link + """ + def __init__(self, id): + self.id = id + self.connectivity_type = CONNECTIVITY_TYPE.UNKNOWN + self.connection_points_references = [] + self.root_requirement = None + self.leaf_requirement = None + self.qos = [] + self.test_access = None + + +class DeploymentFlavor(object): + """ + Deployment Flavor + id: vnf flavor identifier + flavor_key: monitoring parameter and it's value against which this + flavor is being described + constraint: zero or more deployment flavor constraints + constituent_vdu: one or more ConstituentVDU() objects + """ + def __init__(self, id): + self.id = id + self.flavor_key = None + self.constraint = [] + self.constituent_vdu = [] + + +class ConstituentVDU(object): + """ + Constituent Virtual Deployment Unit + vdu_reference: identifier of a vdu + number_of_instances: number of vdu instances required + constituent_vnfc: one or more vnfc identifiers that should be used for + this deployment + """ + def __init__(self, vdu_reference): + self.vdu_reference = vdu_reference + self.number_of_instances = 0 + self.constituent_vnfc = [] + + +class VDU(object): + """ + Virtual Deployment Unit + id: unique identifier for this vdu within the scope of the vnfd + vm_image: a reference to the vm image, does not need to be specified in + the case of null containers + computation_requirement: description of the required computation + resource characteristics + virtual_memory_resource_element: the virtual memory needed for this vdu + virtual_network_bandwidth_resource: the network bandwidth needed for + this vdu + lifecycle_event: describes vnfc functional scripts/workflows for + specific lifecycle events + constraint: vdu specific constraints + high_availability: specifies the redundancy model + scale_in_out: describes the minimum and maximum number of instances + that can be created when scaling + vnfc: describes one or more VNFC() objects created using this vdu + monitoring_parameter: zero or more monitoring parameters which can be + tracked for a vnfc based on this vdu + """ + def __init__(self, id): + self.id = id + self.vm_image = None + self.computation_requirement = None + self.virtual_memory_resource_element = None + self.virtual_network_bandwidth_resource = None + self.lifecycle_event = [] + self.constraint = None + self.high_availability = None + self.scale_in_out = None + self.vnfc = [] + self.monitoring_parameter = [] + + +class VNFC(object): + """ + Virtual Network Function Component + id: unique vnfc identification within the namespace of a specific vnf + connection_point: one or more network connections + """ + def __init__(self, id): + self.id = id + self.connection_point = [] + + +class ConnectionPointVNFC(object): + """ + Connection Point of a Virtual Network Function Component + id: connection point identifier + virtual_link_reference: references zero or more internal virtual links + type: type of network connection + """ + def __init__(self, id): + self.id = id + self.virtual_link_reference = None + self.type = CONNECTION_TYPE.UNKNOWN diff --git a/nfv/nfv-common/nfv_common/catalog/plugin/__init__.py b/nfv/nfv-common/nfv_common/catalog/plugin/__init__.py new file mode 100755 index 00000000..02083bd0 --- /dev/null +++ b/nfv/nfv-common/nfv_common/catalog/plugin/__init__.py @@ -0,0 +1,5 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# diff --git a/nfv/nfv-common/nfv_common/catalog/plugin/v1/__init__.py b/nfv/nfv-common/nfv_common/catalog/plugin/v1/__init__.py new file mode 100755 index 00000000..02083bd0 --- /dev/null +++ b/nfv/nfv-common/nfv_common/catalog/plugin/v1/__init__.py @@ -0,0 +1,5 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# diff --git a/nfv/nfv-common/nfv_common/catalog/plugin/v1/_catalog_plugin.py b/nfv/nfv-common/nfv_common/catalog/plugin/v1/_catalog_plugin.py new file mode 100755 index 00000000..3adf14bc --- /dev/null +++ b/nfv/nfv-common/nfv_common/catalog/plugin/v1/_catalog_plugin.py @@ -0,0 +1,48 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import six +from abc import ABCMeta, abstractproperty, abstractmethod + + +@six.add_metaclass(ABCMeta) +class CatalogPlugin(object): + """ + Abstract Catalog Plugin Class Definition + """ + @abstractproperty + def name(self): + """ The name of plugin """ + pass + + @abstractproperty + def version(self): + """ The versions of the plugin """ + pass + + @abstractproperty + def provider(self): + """ Vendor created the plugin """ + pass + + @abstractproperty + def signature(self): + """ Signature of the plugin """ + pass + + @abstractmethod + def read_vnf_descriptor(self, vnfd_id, vnf_vendor, vnf_version): + """ Read a particular vnf descriptor """ + pass + + @abstractmethod + def initialize(self, version): + """ Initialize the plugin """ + pass + + @abstractmethod + def finalize(self): + """ Finalize the plugin """ + pass diff --git a/nfv/nfv-common/nfv_common/config.py b/nfv/nfv-common/nfv_common/config.py new file mode 100755 index 00000000..28f92a82 --- /dev/null +++ b/nfv/nfv-common/nfv_common/config.py @@ -0,0 +1,45 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +from six.moves import configparser + +from nfv_common import debug + +DLOG = debug.debug_get_logger('nfv_common.config') + +# Configuration Global used by other modules to get access to the configuration +# specified in the ini file. +CONF = dict() + + +class Config(configparser.ConfigParser): + """ + Override ConfigParser class to add dictionary functionality. + """ + def as_dict(self): + d = dict(self._sections) + for key in d: + d[key] = dict(self._defaults, **d[key]) + d[key].pop('__name__', None) + return d + + +def section_exists(section): + """ + Returns true if configuration section exists + """ + section = CONF.get(section, None) + return section is not None + + +def load(config_file): + """ + Load the configuration file into a global CONF variable. + """ + global CONF + + config = Config() + config.read(config_file) + CONF = config.as_dict() diff --git a/nfv/nfv-common/nfv_common/debug/__init__.py b/nfv/nfv-common/nfv_common/debug/__init__.py new file mode 100755 index 00000000..982658c8 --- /dev/null +++ b/nfv/nfv-common/nfv_common/debug/__init__.py @@ -0,0 +1,12 @@ +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# flake8: noqa +# +from _debug_defs import DEBUG_LEVEL +from _debug_log import debug_trace, debug_get_logger, debug_dump_loggers +from _debug_module import debug_register_config_change_callback +from _debug_module import debug_deregister_config_change_callback +from _debug_module import debug_get_config, debug_reload_config +from _debug_module import debug_initialize, debug_finalize diff --git a/nfv/nfv-common/nfv_common/debug/_debug_config.py b/nfv/nfv-common/nfv_common/debug/_debug_config.py new file mode 100755 index 00000000..02ee4cf9 --- /dev/null +++ b/nfv/nfv-common/nfv_common/debug/_debug_config.py @@ -0,0 +1,138 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import six +from six.moves import configparser + +from _debug_defs import DEBUG_LEVEL + +from nfv_common.helpers import Singleton + +# File Format: +# [debug-overall] +# debug_level: +# trace_level: +# +# [debug-loggers] +# : +# +# Note: the python logging library does not have all of the debug levels +# specified above. What this means is that for external libraries using the +# logging library directly, the logger will be enabled for some debug levels. +# The level mappings are as follows: +# debug.level.none --> logging.NOTSET +# debug.level.verbose --> logging.DEBUG +# debug.level.debug --> logging.DEBUG +# debug.level.info --> logging.INFO +# debug.level.notice --> logging.INFO +# debug.level.warn --> logging.WARNING +# debug.level.error --> logging.ERROR +# debug.level.critical --> logging.CRITICAL +# + + +@six.add_metaclass(Singleton) +class DebugConfig(object): + """ + Debug Configuration + """ + debug_level_mapping = {'debug.level.none': DEBUG_LEVEL.NONE, + 'debug.level.verbose': DEBUG_LEVEL.VERBOSE, + 'debug.level.debug': DEBUG_LEVEL.DEBUG, + 'debug.level.info': DEBUG_LEVEL.INFO, + 'debug.level.notice': DEBUG_LEVEL.NOTICE, + 'debug.level.warn': DEBUG_LEVEL.WARN, + 'debug.level.error': DEBUG_LEVEL.ERROR, + 'debug.level.critical': DEBUG_LEVEL.CRITICAL} + + def __init__(self, filename): + """ + Create debug configuration + """ + self._filename = filename + self._config = None + + def load(self): + """ + Load debug configuration + """ + if self._config is None: + self._config = configparser.SafeConfigParser() + self._config.read(self._filename) + + @property + def filename(self): + """ + Returns the debug configuration file name + """ + return self._filename + + @property + def debug_level_overall(self): + """ + Returns the overall debug level + """ + debug_level = DEBUG_LEVEL.NONE + + if self._config is not None: + try: + level_str = self._config.get('debug-overall', 'debug_level') + debug_level = self.debug_level_mapping.get(level_str, + DEBUG_LEVEL.NONE) + + except configparser.NoOptionError: + print("Debug configuration has no debug_level option in the " + "debug-overall section.") + + except configparser.NoSectionError: + print("Debug configuration has no debug-overall section.") + + return debug_level + + @property + def trace_level_overall(self): + """ + Returns the overall trace level + """ + trace_level = DEBUG_LEVEL.NONE + + if self._config is not None: + try: + level_str = self._config.get('debug-overall', 'trace_level') + trace_level = self.debug_level_mapping.get(level_str, + DEBUG_LEVEL.NONE) + + except configparser.NoOptionError: + print("Debug configuration has no trace_level option in the " + "debug-overall section.") + + except configparser.NoSectionError: + print("Debug configuration has no debug-overall section.") + + return trace_level + + @property + def debug_loggers(self): + """ + Returns a list of debug loggers + """ + debug_list = [] + + if self._config is not None: + try: + for name, level_str in self._config.items('debug-loggers'): + debug_list.append((name, self.debug_level_mapping.get( + level_str, DEBUG_LEVEL.NONE))) + + except configparser.NoSectionError: + print("Debug configuration file has no debug-loggers section.") + + return debug_list diff --git a/nfv/nfv-common/nfv_common/debug/_debug_defs.py b/nfv/nfv-common/nfv_common/debug/_debug_defs.py new file mode 100755 index 00000000..3a3187d9 --- /dev/null +++ b/nfv/nfv-common/nfv_common/debug/_debug_defs.py @@ -0,0 +1,27 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import six + +from nfv_common.helpers import Constant, Singleton + + +@six.add_metaclass(Singleton) +class _DebugLevel(object): + """ + Debug Level Constants + """ + NONE = Constant(8000) + CRITICAL = Constant(7000) + ERROR = Constant(6000) + WARN = Constant(5000) + NOTICE = Constant(4000) + INFO = Constant(3000) + DEBUG = Constant(2000) + VERBOSE = Constant(1000) + + +# Constant Instantiation +DEBUG_LEVEL = _DebugLevel() diff --git a/nfv/nfv-common/nfv_common/debug/_debug_log.py b/nfv/nfv-common/nfv_common/debug/_debug_log.py new file mode 100755 index 00000000..07d6a97c --- /dev/null +++ b/nfv/nfv-common/nfv_common/debug/_debug_log.py @@ -0,0 +1,338 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import os +import six +import sys +import inspect +import logging +import datetime +import functools + +from _debug_defs import DEBUG_LEVEL +from _debug_thread import DebugLoggingThread +from _debug_module import Debug + +_debug_loggers = {} + + +class DebugLogFormatter(logging.Formatter): + """ + Debug Log Formatter + """ + def formatTime(self, record, date_format=None): + dt = datetime.datetime.fromtimestamp(record.created) + if date_format is None: + date_str = dt.strftime("%b %d %H:%M:%S") + ".%03d" % record.msecs + else: + date_str = dt.strftime(date_format) + return date_str + + +class DebugLogHandler(logging.Handler): + """ + Debug Log Handler + """ + def __init__(self): + super(DebugLogHandler, self).__init__() + self.process_name = None + self.thread_name = None + + # To keep syslog-ng happy, we need to add the who field twice. Newer + # syslog-ng removes the header formatting + fmt = ("%(asctime)s %(who)36s[%(process)d]: %(who)36s[%(process)d] " + "%(levelname)8s %(message)s") + formatter = DebugLogFormatter(fmt) + self.setFormatter(formatter) + + def set_process_name(self, process_name): + """ + Set the process name + """ + self.process_name = process_name + + def set_thread_name(self, thread_name): + """ + Set the thread name + """ + self.thread_name = thread_name + + @staticmethod + def send(log_record): + """ + Send log record to debug logging thread + """ + DebugLoggingThread().send_log_record(log_record) + + def _format_record(self, log_record): + """ + Format the record so that it can be pickled and sent + to the debug logging thread + """ + if log_record.args: + # Not all arguments can be pickled. + log_record.msg %= log_record.args + log_record.args = None + + if self.process_name is not None: + if self.thread_name is not None: + log_record.who = ("%s_%s_Thread" % (self.process_name, + self.thread_name)) + else: + log_record.who = ("%s_Thread" % self.process_name) + else: + log_record.who = "UNKNOWN" + + if log_record.exc_info: + # Not all exceptions can be pickled. + self.format(log_record) + log_record.exc_info = None + + log_record.formatted_log = self.format(log_record) + + return log_record + + def emit(self, record): + """ + Send record to debug logging thread + """ + try: + log_record = self._format_record(record) + DebugLoggingThread().send_log_record(log_record) + + except (KeyboardInterrupt, SystemExit): + raise + + except Exception: + self.handleError(record) + + +class DebugLogger(object): + """ + Debug Logger + """ + log_level_mapping = {DEBUG_LEVEL.NONE: logging.NOTSET, + DEBUG_LEVEL.VERBOSE: logging.DEBUG, + DEBUG_LEVEL.DEBUG: logging.DEBUG, + DEBUG_LEVEL.INFO: logging.INFO, + DEBUG_LEVEL.NOTICE: logging.INFO, + DEBUG_LEVEL.WARN: logging.WARNING, + DEBUG_LEVEL.ERROR: logging.ERROR, + DEBUG_LEVEL.CRITICAL: logging.CRITICAL} + + def __init__(self, name, debug_level=DEBUG_LEVEL.NONE, process_name=None, + thread_name=None): + """ + Create debug logger + """ + self.name = name + self.process_name = process_name + self.thread_name = thread_name + self.debug_level = debug_level + self.logger = logging.getLogger(name) + self.logger.propagate = False + self.logger.setLevel(logging.NOTSET) + for handler in self.logger.handlers: + self.logger.removeHandler(handler) + self.logger.addHandler(DebugLogHandler()) + + def set_level(self, debug_level): + """ + Set the debug level for the logger + """ + log_level = self.log_level_mapping.get(debug_level, logging.NOTSET) + self.debug_level = debug_level + self.logger.setLevel(log_level) + + def set_process_name(self, process_name): + """ + Set the process name + """ + for handler in self.logger.handlers: + handler.set_process_name(process_name) + + def set_thread_name(self, thread_name): + """ + Set the thread name + """ + for handler in self.logger.handlers: + handler.set_thread_name(thread_name) + + @staticmethod + def get_caller(): + """ + Get the calling function and line number + """ + caller = inspect.currentframe().f_back.f_back + _, filename = os.path.split(caller.f_code.co_filename) + return "%42s.%-4s " % (filename, caller.f_lineno) + + def verbose(self, msg, *args, **kwargs): + """ + Debug log with severity of VERBOSE + """ + if DEBUG_LEVEL.VERBOSE >= Debug().debug_level: + if DEBUG_LEVEL.VERBOSE >= self.debug_level: + caller = self.get_caller() + self.logger.debug(caller + msg, *args, **kwargs) + + def debug(self, msg, *args, **kwargs): + """ + Debug log with severity of DEBUG + """ + if DEBUG_LEVEL.DEBUG >= Debug().debug_level: + if DEBUG_LEVEL.DEBUG >= self.debug_level: + caller = self.get_caller() + self.logger.debug(caller + msg, *args, **kwargs) + + def info(self, msg, *args, **kwargs): + """ + Debug log with severity of INFO + """ + if DEBUG_LEVEL.INFO >= Debug().debug_level: + if DEBUG_LEVEL.INFO >= self.debug_level: + caller = self.get_caller() + self.logger.info(caller + msg, *args, **kwargs) + + def notice(self, msg, *args, **kwargs): + """ + Debug log with severity of NOTICE + """ + if DEBUG_LEVEL.NOTICE >= Debug().debug_level: + if DEBUG_LEVEL.NOTICE >= self.debug_level: + caller = self.get_caller() + self.logger.info(caller + msg, *args, **kwargs) + + def warn(self, msg, *args, **kwargs): + """ + Debug log with severity of WARNING + """ + if DEBUG_LEVEL.WARN >= Debug().debug_level: + if DEBUG_LEVEL.WARN >= self.debug_level: + caller = self.get_caller() + self.logger.warning(caller + msg, *args, **kwargs) + + def error(self, msg, *args, **kwargs): + """ + Debug log with severity of ERROR + """ + if DEBUG_LEVEL.ERROR >= Debug().debug_level: + if DEBUG_LEVEL.ERROR >= self.debug_level: + caller = self.get_caller() + self.logger.error(caller + msg, *args, **kwargs) + + def critical(self, msg, *args, **kwargs): + """ + Debug log with severity of CRITICAL + """ + if DEBUG_LEVEL.CRITICAL >= Debug().debug_level: + if DEBUG_LEVEL.CRITICAL >= self.debug_level: + caller = self.get_caller() + self.logger.critical(caller + msg, *args, **kwargs) + + def exception(self, msg, *args): + """ + Debug exception log + """ + self.logger.exception(msg, *args) + + +def debug_trace(trace_level): + """ + Decorator function used to trace entering and exiting functions + """ + def trace_wrap(func): + def trace_wrapper(*args, **kwargs): + if trace_level >= Debug().trace_level: + six.print_(" " * Debug().trace_depth, file=Debug().output, + end='', sep='') + six.print_("entering " + func.__name__ + ":", + file=Debug().output, end='', sep='') + + six.print_(" args=", file=Debug().output, end='', sep='') + for arg in args: + six.print_("{0} ".format(arg), file=Debug().output, + end='', sep='') + + six.print_(" kwargs=", file=Debug().output, end='', sep='') + for name, value in kwargs.items(): + six.print_("{0}={1} ".format(name, value), + file=Debug().output, end='', sep='') + + try: + Debug().trace_depth += 1 + result = func(*args, **kwargs) + except Exception: + six.print_(" " * Debug().trace_depth, + file=Debug().output, end='', sep='') + six.print_("exception", file=Debug().output, + end='\n', sep='') + raise + finally: + Debug().trace_depth -= 1 + + six.print_(" " * Debug().trace_depth, file=Debug().output, + end='\n', sep='') + six.print_("exiting " + func.__name__, file=Debug().output, + end='\n', sep='') + return result + else: + return func(*args, **kwargs) + + functools.update_wrapper(trace_wrapper, func) + return trace_wrapper + return trace_wrap + + +def debug_dump_loggers(directory): + """ + Dump all available loggers + """ + if os.path.exists(directory): + program_name = os.path.basename(sys.argv[0]) + + with open(directory + program_name + '.dbg.conf', 'a') as f: + for name in _debug_loggers: + f.write(name + '\n') + + +def debug_get_logger(name, debug_level=None, process_name=None, + thread_name=None): + """ + Create a logger if it does not already exist + """ + global _debug_loggers + + logger = _debug_loggers.get(name, None) + if logger is None: + logger = DebugLogger(name, process_name, thread_name) + _debug_loggers[name] = logger + if debug_level is None: + logger.set_level(DEBUG_LEVEL.NONE) + else: + logger.set_level(debug_level) + else: + if debug_level is not None: + logger.set_level(debug_level) + + if process_name is not None: + logger.set_process_name(process_name) + + if thread_name is not None: + logger.set_thread_name(thread_name) + + return logger + + +def debug_set_loggers_level(debug_level=None): + """ + Set the level of all loggers + """ + global _debug_loggers + + for name in _debug_loggers: + logger = _debug_loggers.get(name, None) + if logger is not None: + logger.set_level(debug_level) diff --git a/nfv/nfv-common/nfv_common/debug/_debug_module.py b/nfv/nfv-common/nfv_common/debug/_debug_module.py new file mode 100755 index 00000000..6cafd0b6 --- /dev/null +++ b/nfv/nfv-common/nfv_common/debug/_debug_module.py @@ -0,0 +1,174 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import six +import sys + +from nfv_common.helpers import Singleton + +from _debug_defs import DEBUG_LEVEL +from _debug_config import DebugConfig +from _debug_thread import DebugLoggingThread + + +@six.add_metaclass(Singleton) +class Debug(object): + """ + Debug + """ + def __init__(self): + self._config_change_callbacks = list() + self._config = None + self._debug_config = None + self._debug_level = DEBUG_LEVEL.VERBOSE + self._debug_trace_depth = 0 + self._debug_trace_level = DEBUG_LEVEL.VERBOSE + self._debug_output = sys.stdout + + def reinitialize(self): + self._config_change_callbacks = list() + + def register_config_change_callback(self, callback): + """ + Register a configuration change callback + """ + if callback not in self._config_change_callbacks: + self._config_change_callbacks.append(callback) + + def deregister_config_change_callback(self, callback): + """ + Deregister a configuration change callback + """ + self._config_change_callbacks.remove(callback) + + def load(self, process_name=None, thread_name=None): + """ + Load debug configuration settings + """ + from _debug_log import debug_get_logger, debug_set_loggers_level + + if self._debug_config is not None: + self._debug_config.load() + self._debug_level = self._debug_config.debug_level_overall + self._debug_trace_level = self._debug_config.trace_level_overall + debug_set_loggers_level(self._debug_level) + for name, debug_level in self._debug_config.debug_loggers: + debug_get_logger(name, debug_level, process_name, thread_name) + + def reload(self): + """ + Reload debug configuration settings + """ + from _debug_log import debug_get_logger, debug_set_loggers_level + + if self._debug_config is not None: + self._debug_config.load() + self._debug_level = self._debug_config.debug_level_overall + self._debug_trace_level = self._debug_config.trace_level_overall + debug_set_loggers_level(self._debug_level) + for name, debug_level in self._debug_config.debug_loggers: + debug_get_logger(name, debug_level) + + for callback in self._config_change_callbacks: + callback() + + @property + def config(self): + """ + Returns the debug configuration + """ + return self._config + + @config.setter + def config(self, config): + """ + Set the debug configuration file + """ + if self._config is None: + self._config = config + self._debug_config = DebugConfig(config['config_file']) + DebugLoggingThread().send_log_config(config) + + @property + def debug_level(self): + """ + Returns the debug level currently set + """ + return self._debug_level + + @property + def trace_level(self): + """ + Returns the debug trace level currently set + """ + return self._debug_trace_level + + @property + def trace_depth(self): + """ + Returns the debug trace depth currently set + """ + return self._debug_trace_depth + + @trace_depth.setter + def trace_depth(self, trace_depth): + """ + Set the current trace depth + """ + self._debug_trace_depth = trace_depth + + @property + def output(self): + """ + Returns where the debug output should go + """ + return self._debug_output + + +def debug_register_config_change_callback(callback): + """ + Register debug configuration change callback + """ + return Debug().register_config_change_callback(callback) + + +def debug_deregister_config_change_callback(callback): + """ + Deregister debug configuration change callback + """ + return Debug().deregister_config_change_callback(callback) + + +def debug_get_config(): + """ + Get debug configuration + """ + return Debug().config + + +def debug_reload_config(): + """ + Reload debug configuration + """ + Debug().reload() + + +def debug_initialize(config, process_name=None, thread_name=None): + """ + Initializes the debug subsystem + """ + Debug().reinitialize() + + if config is not None: + Debug().config = config + + Debug().load(process_name, thread_name) + + +def debug_finalize(): + """ + Finalizes the debug subsystem + """ + return diff --git a/nfv/nfv-common/nfv_common/debug/_debug_thread.py b/nfv/nfv-common/nfv_common/debug/_debug_thread.py new file mode 100755 index 00000000..12c6daec --- /dev/null +++ b/nfv/nfv-common/nfv_common/debug/_debug_thread.py @@ -0,0 +1,113 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import six +import sys +import logging +import threading +import multiprocessing + +from logging.handlers import SysLogHandler + +from nfv_common.helpers import Singleton + + +class DebugLoggingThreadFormatter(logging.Formatter): + """ + Debug Log Formatter + """ + def format(self, record): + """ + Override the formatter if the record has already been formatted + """ + if hasattr(record, 'formatted_log'): + return record.formatted_log + else: + super(DebugLoggingThreadFormatter, self).format(record) + + +@six.add_metaclass(Singleton) +class DebugLoggingThread(object): + """ + Debug Logging Thread + """ + def __init__(self): + self._handlers = list() + self._log_queue = multiprocessing.Queue() + self._thread = threading.Thread(target=self._receive_logs) + self._thread.daemon = True + self._thread.start() + + def send_log_record(self, log_record): + """ + Send a log record to debug logging thread + """ + self._log_queue.put_nowait(['log-record', log_record]) + + def send_log_config(self, config): + """ + Send log configuration to debug logging thread + """ + self._log_queue.put_nowait(['log-config', config]) + + def _receive_logs(self): + """ + Receive log records sent to the debug logging thread + """ + formatter = DebugLoggingThreadFormatter() + + while True: + try: + log_work = self._log_queue.get() + + if log_work is not None: + action, work = log_work + + if 'log-record' == action: + if self._handlers: + for handler in self._handlers: + if hasattr(handler, 'is_stdout'): + try: + date_time = work.asctime + text = str(work.formatted_log) + text = text.split('[', 1)[-1] + text = text.split(':', 1)[-1] + work.formatted_log = date_time + ' ' + text + except Exception: + pass + + handler.emit(work) + + elif 'log-config' == action: + if self._handlers: + for handler in self._handlers: + handler.close() + + handler_names = work.get('handlers', '') + handler_list = [handler.strip() + for handler in handler_names.split(',')] + + self._handlers[:] = list() + + if 'syslog' in handler_list: + address = work.get('syslog_address', '/dev/log') + facility = work.get('syslog_facility', 'user') + facility = SysLogHandler.facility_names[facility] + syslog_handler = SysLogHandler(address=address, + facility=facility) + syslog_handler.setFormatter(formatter) + self._handlers.append(syslog_handler) + + if 'stdout' in handler_list: + stdout_handler = logging.StreamHandler(sys.stdout) + stdout_handler.setFormatter(formatter) + stdout_handler.is_stdout = True + self._handlers.append(stdout_handler) + + except EOFError: + return + + except (KeyboardInterrupt, SystemExit): + raise diff --git a/nfv/nfv-common/nfv_common/event_log/__init__.py b/nfv/nfv-common/nfv_common/event_log/__init__.py new file mode 100755 index 00000000..db979e93 --- /dev/null +++ b/nfv/nfv-common/nfv_common/event_log/__init__.py @@ -0,0 +1,16 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# flake8: noqa +# +from objects.v1 import EVENT_ID +from objects.v1 import EVENT_CONTEXT +from objects.v1 import EVENT_TYPE +from objects.v1 import EVENT_IMPORTANCE +from objects.v1 import EVENT_INITIATED_BY +from objects.v1 import EventLogStateData +from objects.v1 import EventLogThresholdData, EventLogData +from _event_log_module import event_log, event_log_subsystem_sane +from _event_log_module import event_log_initialize, event_log_finalize diff --git a/nfv/nfv-common/nfv_common/event_log/_event_log_handlers.py b/nfv/nfv-common/nfv_common/event_log/_event_log_handlers.py new file mode 100755 index 00000000..94e8c667 --- /dev/null +++ b/nfv/nfv-common/nfv_common/event_log/_event_log_handlers.py @@ -0,0 +1,73 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import six +import stevedore + +from nfv_common import debug +from nfv_common.helpers import Singleton + +DLOG = debug.debug_get_logger('nfv_common.event_log.event_log_handlers') + + +@six.add_metaclass(Singleton) +class EventLogHandlers(stevedore.enabled.EnabledExtensionManager): + """ + Event Log Handlers + """ + _version = '1.0.0' + _signature = 'e33d7cf6-f270-4256-893e-16266ee4dd2e' + + def __init__(self, namespace, handler_names): + super(EventLogHandlers, self).__init__(namespace, + EventLogHandlers.valid_handler, + invoke_on_load=True, + invoke_args=(), invoke_kwds={}) + + handler_list = [handler.strip() for handler in handler_names.split(',')] + + self._handlers = {} + for handler in self: + if handler.obj.name in handler_list: + handler_id = handler.obj.provider + ':' + handler.obj.name + if handler_id not in self._handlers: + self._handlers[handler_id] = handler + DLOG.info("Loaded handler %s version %s provided by %s." + % (handler.obj.name, handler.obj.version, + handler.obj.provider)) + + @staticmethod + def valid_handler(handler): + """ + Verify signature of the handler is valid + """ + if EventLogHandlers._signature == handler.obj.signature: + return True + else: + DLOG.info("Handler %s version %s from provider %s has an invalid " + "signature." % (handler.obj.name, handler.obj.version, + handler.obj.provider)) + return False + + def log(self, log_data): + """ + Log a particular event using the handlers + """ + for handler_type, handler in self._handlers.iteritems(): + handler.obj.log(log_data) + + def initialize(self, config_file): + """ + Initialize handlers + """ + for handler_id, handler in self._handlers.iteritems(): + handler.obj.initialize(config_file) + + def finalize(self): + """ + Finalize handlers + """ + for handler_id, handler in self._handlers.iteritems(): + handler.obj.finalize() diff --git a/nfv/nfv-common/nfv_common/event_log/_event_log_module.py b/nfv/nfv-common/nfv_common/event_log/_event_log_module.py new file mode 100755 index 00000000..a7baa334 --- /dev/null +++ b/nfv/nfv-common/nfv_common/event_log/_event_log_module.py @@ -0,0 +1,37 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + + +def event_log(log_data): + """ + Log a particular event + """ + from _event_log_thread import EventLogThread + EventLogThread().log(log_data) + + +def event_log_subsystem_sane(): + """ + Returns true if the event log subsystem is healthy + """ + from _event_log_thread import EventLogThread + return 600 >= EventLogThread().stall_elapsed_secs + + +def event_log_initialize(config): + """ + Initialize the event log subsystem + """ + from _event_log_thread import EventLogThread + EventLogThread(config).start() + + +def event_log_finalize(): + """ + Finalize the event log subsystem + """ + from _event_log_thread import EventLogThread + EventLogThread().stop(max_wait_in_seconds=5) diff --git a/nfv/nfv-common/nfv_common/event_log/_event_log_thread.py b/nfv/nfv-common/nfv_common/event_log/_event_log_thread.py new file mode 100755 index 00000000..dc78c07f --- /dev/null +++ b/nfv/nfv-common/nfv_common/event_log/_event_log_thread.py @@ -0,0 +1,67 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import six + +from nfv_common import debug +from nfv_common import thread +from nfv_common.helpers import Singleton + +from _event_log_handlers import EventLogHandlers + +DLOG = debug.debug_get_logger('nfv_common.event_log.event_log_thread') + + +@six.add_metaclass(Singleton) +class EventLogWorker(thread.ThreadWorker): + """ + Event Log Worker + """ + def __init__(self, name, config): + super(EventLogWorker, self).__init__(name) + self._config = config + self._handlers = None + + def initialize(self): + """ + Initialize the Event Log Worker + """ + self._handlers = EventLogHandlers(self._config['namespace'], + self._config['handlers']) + self._handlers.initialize(self._config['config_file']) + + def finalize(self): + """ + Finalize the Event Log Worker + """ + if self._handlers is not None: + self._handlers.finalize() + + def do_work(self, action, work): + """ + Do work given to the Event Log Worker + """ + if EventLogThread.ACTION_LOG_EVENT == action: + self._handlers.log(work['log-data']) + + +@six.add_metaclass(Singleton) +class EventLogThread(thread.Thread): + """ + Event Log Thread + """ + ACTION_LOG_EVENT = "thread-log-event" + + def __init__(self, config=None): + self._worker = EventLogWorker('Event-Log', config) + super(EventLogThread, self).__init__('Event-Log', self._worker) + + def log(self, log_data): + """ + Send log data to the Event Log Thread + """ + work = dict() + work['log-data'] = log_data + self.send_work(EventLogThread.ACTION_LOG_EVENT, work) diff --git a/nfv/nfv-common/nfv_common/event_log/handlers/__init__.py b/nfv/nfv-common/nfv_common/event_log/handlers/__init__.py new file mode 100755 index 00000000..02083bd0 --- /dev/null +++ b/nfv/nfv-common/nfv_common/event_log/handlers/__init__.py @@ -0,0 +1,5 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# diff --git a/nfv/nfv-common/nfv_common/event_log/handlers/v1/__init__.py b/nfv/nfv-common/nfv_common/event_log/handlers/v1/__init__.py new file mode 100755 index 00000000..29930d0c --- /dev/null +++ b/nfv/nfv-common/nfv_common/event_log/handlers/v1/__init__.py @@ -0,0 +1,8 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# flake8: noqa +# +from _event_log_handler import EventLogHandler diff --git a/nfv/nfv-common/nfv_common/event_log/handlers/v1/_event_log_handler.py b/nfv/nfv-common/nfv_common/event_log/handlers/v1/_event_log_handler.py new file mode 100755 index 00000000..6fe78272 --- /dev/null +++ b/nfv/nfv-common/nfv_common/event_log/handlers/v1/_event_log_handler.py @@ -0,0 +1,48 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import abc +import six + + +@six.add_metaclass(abc.ABCMeta) +class EventLogHandler(object): + """ + Abstract Event Log Handler Class Definition + """ + @abc.abstractproperty + def name(self): + """ The name of handler """ + pass + + @abc.abstractproperty + def version(self): + """ The versions of the handler """ + pass + + @abc.abstractproperty + def provider(self): + """ Who created the handler """ + pass + + @abc.abstractproperty + def signature(self): + """ Signature of the handler """ + pass + + @abc.abstractmethod + def log(self, log_data): + """ Log an event via the handler """ + pass + + @abc.abstractmethod + def initialize(self, config_file): + """ Initialize the handler """ + pass + + @abc.abstractmethod + def finalize(self): + """ Finalize the handler """ + pass diff --git a/nfv/nfv-common/nfv_common/event_log/objects/__init__.py b/nfv/nfv-common/nfv_common/event_log/objects/__init__.py new file mode 100755 index 00000000..90b33192 --- /dev/null +++ b/nfv/nfv-common/nfv_common/event_log/objects/__init__.py @@ -0,0 +1,8 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# flake8: noqa +# +import v1 diff --git a/nfv/nfv-common/nfv_common/event_log/objects/v1/__init__.py b/nfv/nfv-common/nfv_common/event_log/objects/v1/__init__.py new file mode 100755 index 00000000..a8ad418a --- /dev/null +++ b/nfv/nfv-common/nfv_common/event_log/objects/v1/__init__.py @@ -0,0 +1,12 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# flake8: noqa +# +from _event_log_defs import EVENT_ID, EVENT_CONTEXT, EVENT_TYPE, EVENT_IMPORTANCE +from _event_log_defs import EVENT_INITIATED_BY +from _event_log_data import EventLogStateData +from _event_log_data import EventLogThresholdData +from _event_log_data import EventLogData diff --git a/nfv/nfv-common/nfv_common/event_log/objects/v1/_event_log_data.py b/nfv/nfv-common/nfv_common/event_log/objects/v1/_event_log_data.py new file mode 100755 index 00000000..480bcc52 --- /dev/null +++ b/nfv/nfv-common/nfv_common/event_log/objects/v1/_event_log_data.py @@ -0,0 +1,54 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +from datetime import datetime + + +class EventLogStateData(object): + """ + Event Log State Data + """ + def __init__(self, state): + self.state = state + + +class EventLogThresholdData(object): + """ + Event Log Threshold Data + """ + def __init__(self, threshold_value, observed_value): + self.threshold_value = threshold_value + self.observed_value = observed_value + + +class EventLogData(object): + """ + Event Log Data + """ + _id = 1 + + def __init__(self, event_id, event_type, event_context, entity_type, entity, + reason_text, importance, additional_text="", state_data=None, + threshold_data=None, suppression_allowed=True): + self.log_id = EventLogData._id + self.event_id = event_id + self.event_type = event_type + self.event_context = event_context + self.entity_type = entity_type + self.entity = entity + self.reason_text = reason_text + self.importance = importance + self.additional_text = additional_text + self.state_data = state_data + self.threshold_data = threshold_data + self.suppression_allowed = suppression_allowed + self.timestamp = datetime.utcnow() + EventLogData._id += 1 + + def as_dict(self): + """ + Render this object as a dictionary of its fields + """ + return dict.copy(self.__dict__) diff --git a/nfv/nfv-common/nfv_common/event_log/objects/v1/_event_log_defs.py b/nfv/nfv-common/nfv_common/event_log/objects/v1/_event_log_defs.py new file mode 100755 index 00000000..4e40146f --- /dev/null +++ b/nfv/nfv-common/nfv_common/event_log/objects/v1/_event_log_defs.py @@ -0,0 +1,212 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# flake8: noqa +# +import six + +from nfv_common.helpers import Constants, Constant, Singleton + + +@six.add_metaclass(Singleton) +class _EventId(Constants): + """ + Event Type Constants + """ + UNKNOWN = Constant('unknown') + MULTI_NODE_RECOVERY_MODE_ENTER = Constant('multi-node-recovery-enter') + MULTI_NODE_RECOVERY_MODE_EXIT = Constant('multi-node-recovery-exit') + HOST_SERVICES_ENABLED = Constant('host-services-enabled') + HOST_SERVICES_DISABLED = Constant('host-services-disabled') + HOST_SERVICES_FAILED = Constant('host-services-failed') + HYPERVISOR_STATE_CHANGE = Constant('hypervisor-state-change') + INSTANCE_RENAMED = Constant('instance-renamed') + INSTANCE_ENABLED = Constant('instance-enabled') + INSTANCE_FAILED = Constant('instance-failed') + INSTANCE_SCHEDULING_FAILED = Constant('instance-scheduling-failed') + INSTANCE_CREATE_BEGIN = Constant('instance-create-begin') + INSTANCE_CREATING = Constant('instance-creating') + INSTANCE_CREATE_REJECTED = Constant('instance-create-rejected') + INSTANCE_CREATE_CANCELLED = Constant('instance-create-cancelled') + INSTANCE_CREATE_FAILED = Constant('instance-create-failed') + INSTANCE_CREATED = Constant('instance-created') + INSTANCE_DELETE_BEGIN = Constant('instance-delete-begin') + INSTANCE_DELETING = Constant('instance-deleting') + INSTANCE_DELETE_REJECTED = Constant('instance-delete-rejected') + INSTANCE_DELETE_CANCELLED = Constant('instance-delete-cancelled') + INSTANCE_DELETE_FAILED = Constant('instance-delete-failed') + INSTANCE_DELETED = Constant('instance-deleted') + INSTANCE_PAUSE_BEGIN = Constant('instance-pause-begin') + INSTANCE_PAUSING = Constant('instance-pausing') + INSTANCE_PAUSE_REJECTED = Constant('instance-pause-rejected') + INSTANCE_PAUSE_CANCELLED = Constant('instance-pause-cancelled') + INSTANCE_PAUSE_FAILED = Constant('instance-pause-failed') + INSTANCE_PAUSED = Constant('instance-paused') + INSTANCE_UNPAUSE_BEGIN = Constant('instance-unpause-begin') + INSTANCE_UNPAUSING = Constant('instance-unpausing') + INSTANCE_UNPAUSE_REJECTED = Constant('instance-unpause-rejected') + INSTANCE_UNPAUSE_CANCELLED = Constant('instance-unpause-cancelled') + INSTANCE_UNPAUSE_FAILED = Constant('instance-unpause-failed') + INSTANCE_UNPAUSED = Constant('instance-unpaused') + INSTANCE_SUSPEND_BEGIN = Constant('instance-suspend-begin') + INSTANCE_SUSPENDING = Constant('instance-suspending') + INSTANCE_SUSPEND_REJECTED = Constant('instance-suspend-rejected') + INSTANCE_SUSPEND_CANCELLED = Constant('instance-suspend-cancelled') + INSTANCE_SUSPEND_FAILED = Constant('instance-suspend-failed') + INSTANCE_SUSPENDED = Constant('instance-suspended') + INSTANCE_RESUME_BEGIN = Constant('instance-resume-begin') + INSTANCE_RESUMING = Constant('instance-resuming') + INSTANCE_RESUME_REJECTED = Constant('instance-resume-rejected') + INSTANCE_RESUME_CANCELLED = Constant('instance-resume-cancelled') + INSTANCE_RESUME_FAILED = Constant('instance-resume-failed') + INSTANCE_RESUMED = Constant('instance-resumed') + INSTANCE_START_BEGIN = Constant('instance-start-begin') + INSTANCE_STARTING = Constant('instance-starting') + INSTANCE_START_REJECTED = Constant('instance-start-rejected') + INSTANCE_START_CANCELLED = Constant('instance-start-cancelled') + INSTANCE_START_FAILED = Constant('instance-start-failed') + INSTANCE_STARTED = Constant('instance-started') + INSTANCE_STOP_BEGIN = Constant('instance-stop-begin') + INSTANCE_STOPPING = Constant('instance-stopping') + INSTANCE_STOP_REJECTED = Constant('instance-stop-rejected') + INSTANCE_STOP_CANCELLED = Constant('instance-stop-cancelled') + INSTANCE_STOP_FAILED = Constant('instance-stop-failed') + INSTANCE_STOPPED = Constant('instance-stopped') + INSTANCE_LIVE_MIGRATE_BEGIN = Constant('instance-live-migrate-begin') + INSTANCE_LIVE_MIGRATING = Constant('instance-live-migrating') + INSTANCE_LIVE_MIGRATE_REJECTED = Constant('instance-live-migrate-rejected') + INSTANCE_LIVE_MIGRATE_CANCELLED = Constant('instance-live-migrate-cancelled') + INSTANCE_LIVE_MIGRATE_FAILED = Constant('instance-live-migrate-failed') + INSTANCE_LIVE_MIGRATED = Constant('instance-live-migrated') + INSTANCE_COLD_MIGRATE_BEGIN = Constant('instance-cold-migrate-begin') + INSTANCE_COLD_MIGRATING = Constant('instance-cold-migrating') + INSTANCE_COLD_MIGRATE_REJECTED = Constant('instance-cold-migrate-rejected') + INSTANCE_COLD_MIGRATE_CANCELLED = Constant('instance-cold-migrate-cancelled') + INSTANCE_COLD_MIGRATE_FAILED = Constant('instance-cold-migrate-failed') + INSTANCE_COLD_MIGRATED = Constant('instance-cold-migrated') + INSTANCE_COLD_MIGRATE_CONFIRM_BEGIN = Constant('instance-cold-migrate-confirm-begin') + INSTANCE_COLD_MIGRATE_CONFIRMING = Constant('instance-cold-migrate-confirming') + INSTANCE_COLD_MIGRATE_CONFIRM_REJECTED = Constant('instance-cold-migrate-confirm-rejected') + INSTANCE_COLD_MIGRATE_CONFIRM_CANCELLED = Constant('instance-cold-migrate-confirm-cancelled') + INSTANCE_COLD_MIGRATE_CONFIRM_FAILED = Constant('instance-cold-migrate-confirm-failed') + INSTANCE_COLD_MIGRATE_CONFIRMED = Constant('instance-cold-migrate-confirmed') + INSTANCE_COLD_MIGRATE_REVERT_BEGIN = Constant('instance-cold-migrate-revert-begin') + INSTANCE_COLD_MIGRATE_REVERTING = Constant('instance-cold-migrate-reverting') + INSTANCE_COLD_MIGRATE_REVERT_REJECTED = Constant('instance-cold-migrate-revert-rejected') + INSTANCE_COLD_MIGRATE_REVERT_CANCELLED = Constant('instance-cold-migrate-revert-cancelled') + INSTANCE_COLD_MIGRATE_REVERT_FAILED = Constant('instance-cold-migrate-revert-failed') + INSTANCE_COLD_MIGRATE_REVERTED = Constant('instance-cold-migrate-reverted') + INSTANCE_EVACUATE_BEGIN = Constant('instance-evacuate-begin') + INSTANCE_EVACUATING = Constant('instance-evacuating') + INSTANCE_EVACUATE_REJECTED = Constant('instance-evacuate-rejected') + INSTANCE_EVACUATE_CANCELLED = Constant('instance-evacuate-cancelled') + INSTANCE_EVACUATE_FAILED = Constant('instance-evacuate-failed') + INSTANCE_EVACUATED = Constant('instance-evacuated') + INSTANCE_REBOOT_BEGIN = Constant('instance-reboot-begin') + INSTANCE_REBOOTING = Constant('instance-rebooting') + INSTANCE_REBOOT_REJECTED = Constant('instance-reboot-rejected') + INSTANCE_REBOOT_CANCELLED = Constant('instance-reboot-cancelled') + INSTANCE_REBOOT_FAILED = Constant('instance-reboot-failed') + INSTANCE_REBOOTED = Constant('instance-rebooted') + INSTANCE_REBUILD_BEGIN = Constant('instance-rebuild-begin') + INSTANCE_REBUILDING = Constant('instance-rebuilding') + INSTANCE_REBUILD_REJECTED = Constant('instance-rebuild-rejected') + INSTANCE_REBUILD_CANCELLED = Constant('instance-rebuild-cancelled') + INSTANCE_REBUILD_FAILED = Constant('instance-rebuild-failed') + INSTANCE_REBUILT = Constant('instance-rebuilt') + INSTANCE_RESIZE_BEGIN = Constant('instance-resize-begin') + INSTANCE_RESIZING = Constant('instance-resizing') + INSTANCE_RESIZE_REJECTED = Constant('instance-resize-rejected') + INSTANCE_RESIZE_CANCELLED = Constant('instance-resize-cancelled') + INSTANCE_RESIZE_FAILED = Constant('instance-resize-failed') + INSTANCE_RESIZED = Constant('instance-resized') + INSTANCE_RESIZE_CONFIRM_BEGIN = Constant('instance-resize-confirm-begin') + INSTANCE_RESIZE_CONFIRMING = Constant('instance-resize-confirming') + INSTANCE_RESIZE_CONFIRM_REJECTED = Constant('instance-resize-confirm-rejected') + INSTANCE_RESIZE_CONFIRM_CANCELLED = Constant('instance-resize-confirm-cancelled') + INSTANCE_RESIZE_CONFIRM_FAILED = Constant('instance-resize-confirm-failed') + INSTANCE_RESIZE_CONFIRMED = Constant('instance-resize-confirmed') + INSTANCE_RESIZE_REVERT_BEGIN = Constant('instance-resize-revert-begin') + INSTANCE_RESIZE_REVERTING = Constant('instance-resize-reverting') + INSTANCE_RESIZE_REVERT_REJECTED = Constant('instance-resize-revert-rejected') + INSTANCE_RESIZE_REVERT_CANCELLED = Constant('instance-resize-revert-cancelled') + INSTANCE_RESIZE_REVERT_FAILED = Constant('instance-resize-revert-failed') + INSTANCE_RESIZE_REVERTED = Constant('instance-resize-reverted') + INSTANCE_GUEST_HEARTBEAT_ESTABLISHED = Constant('instance-guest-heartbeat-established') + INSTANCE_GUEST_HEARTBEAT_DISCONNECTED = Constant('instance-guest-heartbeat-disconnected') + INSTANCE_GUEST_HEARTBEAT_FAILED = Constant('instance-guest-heartbeat-failed') + INSTANCE_GUEST_HEALTH_CHECK_FAILED = Constant('instance-guest-health_check-failed') + SW_PATCH_AUTO_APPLY_START = Constant('sw-patch-auto-apply-started') + SW_PATCH_AUTO_APPLY_INPROGRESS = Constant('sw-patch-auto-apply-inprogress') + SW_PATCH_AUTO_APPLY_REJECTED = Constant('sw-patch-auto-apply-rejected') + SW_PATCH_AUTO_APPLY_CANCELLED = Constant('sw-patch-auto-apply-cancelled') + SW_PATCH_AUTO_APPLY_FAILED = Constant('sw-patch-auto-apply-failed') + SW_PATCH_AUTO_APPLY_COMPLETED = Constant('sw-patch-auto-apply-completed') + SW_PATCH_AUTO_APPLY_ABORT = Constant('sw-patch-auto-apply-abort') + SW_PATCH_AUTO_APPLY_ABORTING = Constant('sw-patch-auto-apply-aborting') + SW_PATCH_AUTO_APPLY_ABORT_REJECTED = Constant('sw-patch-auto-apply-abort-rejected') + SW_PATCH_AUTO_APPLY_ABORT_FAILED = Constant('sw-patch-auto-apply-abort-failed') + SW_PATCH_AUTO_APPLY_ABORTED = Constant('sw-patch-auto-apply-aborted') + SW_UPGRADE_AUTO_APPLY_START = Constant('sw-upgrade-auto-apply-started') + SW_UPGRADE_AUTO_APPLY_INPROGRESS = Constant('sw-upgrade-auto-apply-inprogress') + SW_UPGRADE_AUTO_APPLY_REJECTED = Constant('sw-upgrade-auto-apply-rejected') + SW_UPGRADE_AUTO_APPLY_CANCELLED = Constant('sw-upgrade-auto-apply-cancelled') + SW_UPGRADE_AUTO_APPLY_FAILED = Constant('sw-upgrade-auto-apply-failed') + SW_UPGRADE_AUTO_APPLY_COMPLETED = Constant('sw-upgrade-auto-apply-completed') + SW_UPGRADE_AUTO_APPLY_ABORT = Constant('sw-upgrade-auto-apply-abort') + SW_UPGRADE_AUTO_APPLY_ABORTING = Constant('sw-upgrade-auto-apply-aborting') + SW_UPGRADE_AUTO_APPLY_ABORT_REJECTED = Constant('sw-upgrade-auto-apply-abort-rejected') + SW_UPGRADE_AUTO_APPLY_ABORT_FAILED = Constant('sw-upgrade-auto-apply-abort-failed') + SW_UPGRADE_AUTO_APPLY_ABORTED = Constant('sw-upgrade-auto-apply-aborted') + + +@six.add_metaclass(Singleton) +class _EventType(Constants): + """ + Event Type Constants + """ + UNKNOWN = Constant('unknown') + STATE_EVENT = Constant('state-event') + ACTION_EVENT = Constant('action-event') + GUEST_HEARTBEAT_EVENT = Constant('guest-heartbeat-event') + PROCESSING_ERROR = Constant('processing-error') + + +@six.add_metaclass(Singleton) +class _EventContext(Constants): + """ + Event Context Constants + """ + ADMIN = Constant('admin') + TENANT = Constant('tenant') + + +@six.add_metaclass(Singleton) +class _EventImportance(Constants): + """ + Event Importance Constants + """ + UNKNOWN = Constant('unknown') + HIGH = Constant('high') + MEDIUM = Constant('medium') + LOW = Constant('low') + + +@six.add_metaclass(Singleton) +class _EventInitiatedBy(Constants): + """ + Initiated-By Constants + """ + UNKNOWN = Constant('unknown') + TENANT = Constant('tenant') + INSTANCE = Constant('instance') + INSTANCE_DIRECTOR = Constant('instance-director') + +# Constant Instantiation +EVENT_ID = _EventId() +EVENT_TYPE = _EventType() +EVENT_CONTEXT = _EventContext() +EVENT_IMPORTANCE = _EventImportance() +EVENT_INITIATED_BY = _EventInitiatedBy() diff --git a/nfv/nfv-common/nfv_common/exceptions.py b/nfv/nfv-common/nfv_common/exceptions.py new file mode 100755 index 00000000..14681c09 --- /dev/null +++ b/nfv/nfv-common/nfv_common/exceptions.py @@ -0,0 +1,13 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + + +class PickleableException(Exception): + """ + Pickleable Exception + Used to mark custom exception classes that can be pickled. + """ + pass diff --git a/nfv/nfv-common/nfv_common/forensic/__init__.py b/nfv/nfv-common/nfv_common/forensic/__init__.py new file mode 100755 index 00000000..2b87fd9d --- /dev/null +++ b/nfv/nfv-common/nfv_common/forensic/__init__.py @@ -0,0 +1,25 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# flake8: noqa +# +from _evidence import evidence_from_files +from _analysis import analysis_instance_start_success +from _analysis import analysis_instance_stop_success +from _analysis import analysis_instance_pause_success +from _analysis import analysis_instance_unpause_success +from _analysis import analysis_instance_suspend_success +from _analysis import analysis_instance_resume_success +from _analysis import analysis_instance_reboot_success +from _analysis import analysis_instance_rebuild_success +from _analysis import analysis_instance_live_migrate_success +from _analysis import analysis_instance_cold_migrate_success +from _analysis import analysis_instance_cold_migrate_confirm_success +from _analysis import analysis_instance_cold_migrate_revert_success +from _analysis import analysis_instance_resize_success +from _analysis import analysis_instance_resize_confirm_success +from _analysis import analysis_instance_resize_revert_success +from _analysis import analysis_stdout +from _forensic_module import forensic_initialize, forensic_finalize diff --git a/nfv/nfv-common/nfv_common/forensic/_analysis.py b/nfv/nfv-common/nfv_common/forensic/_analysis.py new file mode 100755 index 00000000..309126a1 --- /dev/null +++ b/nfv/nfv-common/nfv_common/forensic/_analysis.py @@ -0,0 +1,766 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +from nfv_common import debug + +from _defs import NFV_VIM + +DLOG = debug.debug_get_logger('forensic-analysis') + + +def _analysis_instances_success(instance_uuid, instance_name, records, + expected_records, action_types=None, callback=None): + """ + Analyze records and determine if instance success + """ + def default_callback(idx, record): + return True + + if action_types is None: + action_types = list() + + if callback is None: + callback = default_callback + + DLOG.verbose("%s" % '-' * 80) + + idx = 0 + + for record in records: + record_data = record['data'] + + if record_data['type'] == expected_records[idx]: + if NFV_VIM.INSTANCE_NFVI_ACTION_START == record_data['type']: + if record_data['instance_uuid'] == instance_uuid: + if callback(idx, record): + DLOG.verbose("accept: %s" % record_data['type']) + idx += 1 + + elif record_data['type'] \ + in [NFV_VIM.INSTANCE_START_STATE, + NFV_VIM.INSTANCE_START_STATE_COMPLETED, + NFV_VIM.INSTANCE_STOP_STATE, + NFV_VIM.INSTANCE_STOP_STATE_COMPLETED, + NFV_VIM.INSTANCE_PAUSE_STATE, + NFV_VIM.INSTANCE_PAUSE_STATE_COMPLETED, + NFV_VIM.INSTANCE_UNPAUSE_STATE, + NFV_VIM.INSTANCE_UNPAUSE_STATE_COMPLETED, + NFV_VIM.INSTANCE_SUSPEND_STATE, + NFV_VIM.INSTANCE_SUSPEND_STATE_COMPLETED, + NFV_VIM.INSTANCE_RESUME_STATE, + NFV_VIM.INSTANCE_RESUME_STATE_COMPLETED, + NFV_VIM.INSTANCE_REBOOT_STATE, + NFV_VIM.INSTANCE_REBOOT_STATE_COMPLETED, + NFV_VIM.INSTANCE_REBUILD_STATE, + NFV_VIM.INSTANCE_REBUILD_STATE_COMPLETED, + NFV_VIM.INSTANCE_LIVE_MIGRATE_STATE, + NFV_VIM.INSTANCE_LIVE_MIGRATE_FINISH_STATE, + NFV_VIM.INSTANCE_COLD_MIGRATE_STATE, + NFV_VIM.INSTANCE_COLD_MIGRATE_CONFIRM_STATE, + NFV_VIM.INSTANCE_COLD_MIGRATE_REVERT_STATE, + NFV_VIM.INSTANCE_RESIZE_STATE, + NFV_VIM.INSTANCE_RESIZE_CONFIRM_STATE, + NFV_VIM.INSTANCE_RESIZE_REVERT_STATE, + NFV_VIM.INSTANCE_INITIAL_STATE]: + if record_data['instance_name'] == instance_name: + if callback(idx, record): + DLOG.verbose("accept: %s" % record_data['type']) + idx += 1 + + elif record_data['type'] \ + in [NFV_VIM.INSTANCE_START_CALLBACK, + NFV_VIM.INSTANCE_STOP_CALLBACK, + NFV_VIM.INSTANCE_PAUSE_CALLBACK, + NFV_VIM.INSTANCE_UNPAUSE_CALLBACK, + NFV_VIM.INSTANCE_SUSPEND_CALLBACK, + NFV_VIM.INSTANCE_RESUME_CALLBACK, + NFV_VIM.INSTANCE_REBOOT_CALLBACK, + NFV_VIM.INSTANCE_REBUILD_CALLBACK, + NFV_VIM.INSTANCE_LIVE_MIGRATE_CALLBACK, + NFV_VIM.INSTANCE_COLD_MIGRATE_CALLBACK, + NFV_VIM.INSTANCE_COLD_MIGRATE_CONFIRM_CALLBACK, + NFV_VIM.INSTANCE_COLD_MIGRATE_REVERT_CALLBACK, + NFV_VIM.INSTANCE_RESIZE_CALLBACK, + NFV_VIM.INSTANCE_RESIZE_CONFIRM_CALLBACK, + NFV_VIM.INSTANCE_RESIZE_REVERT_CALLBACK]: + if record_data['instance_name'] == instance_name: + if "True" == record_data['completed']: + if callback(idx, record): + DLOG.verbose("accept: %s" % record_data['type']) + idx += 1 + + elif record_data['type'] == NFV_VIM.INSTANCE_GUEST_SERVICES_NOTIFY: + if record_data['instance_uuid'] == instance_uuid: + if record_data['service_name'] == 'heartbeat': + if callback(idx, record): + DLOG.verbose("accept: %s" % record_data['type']) + idx += 1 + + elif record_data['type'] == NFV_VIM.INSTANCE_GUEST_SERVICES_ENABLE: + if record_data['instance_name'] == instance_name: + if record_data['service_name'] == 'heartbeat': + if record_data['service_admin_state'] == 'unlocked': + if callback(idx, record): + DLOG.verbose("accept: %s" % record_data['type']) + idx += 1 + + elif record_data['type'] \ + == NFV_VIM.INSTANCE_GUEST_SERVICES_ENABLE_CALLBACK: + if record_data['instance_name'] == instance_name: + if "True" == record_data['completed']: + if record_data['service_name'] == 'heartbeat': + if record_data['service_admin_state'] == 'unlocked': + if callback(idx, record): + DLOG.verbose("accept: %s" % record_data['type']) + idx += 1 + + elif record_data['type'] == NFV_VIM.INSTANCE_GUEST_SERVICES_DISABLE: + if record_data['instance_name'] == instance_name: + if record_data['service_name'] == 'heartbeat': + if record_data['service_admin_state'] == 'locked': + if callback(idx, record): + DLOG.verbose("accept: %s" % record_data['type']) + idx += 1 + + elif record_data['type'] \ + == NFV_VIM.INSTANCE_GUEST_SERVICES_DISABLE_CALLBACK: + if record_data['instance_name'] == instance_name: + if "True" == record_data['completed']: + if record_data['service_name'] == 'heartbeat': + if record_data['service_admin_state'] == 'locked': + if callback(idx, record): + DLOG.verbose("accept: %s" % record_data['type']) + idx += 1 + + elif record_data['type'] == NFV_VIM.INSTANCE_GUEST_SERVICES_VOTE_RESULT: + if record_data['instance_name'] == instance_name: + if record_data['vote_result'] == 'allow': + if callback(idx, record): + DLOG.verbose("accept: %s" % record_data['type']) + idx += 1 + + elif record_data['type'] \ + in [NFV_VIM.INSTANCE_GUEST_SERVICES_VOTE, + NFV_VIM.INSTANCE_GUEST_SERVICES_PRE_NOTIFY, + NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY]: + if record_data['instance_name'] == instance_name: + if record_data['action_type'] in action_types: + if callback(idx, record): + DLOG.verbose("accept: %s" % record_data['type']) + idx += 1 + + elif record_data['type'] \ + in [NFV_VIM.INSTANCE_GUEST_SERVICES_VOTE_CALLBACK, + NFV_VIM.INSTANCE_GUEST_SERVICES_PRE_NOTIFY_CALLBACK, + NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY_CALLBACK]: + if record_data['instance_name'] == instance_name: + if record_data['action_type'] in action_types: + if "True" == record_data['completed']: + if callback(idx, record): + DLOG.verbose("accept: %s" % record_data['type']) + idx += 1 + + elif record_data['type'] \ + == NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY_HEARTBEAT: + if record_data['instance_name'] == instance_name: + if callback(idx, record): + DLOG.verbose("accept: %s" % record_data['type']) + idx += 1 + + if len(expected_records) == idx: + return True, 'analysis successful' + + if len(expected_records) > idx: + data_type = expected_records[idx] + return False, "record %s was not found" % data_type + + return False, 'analysis unsuccessful' + + +def analysis_instance_start_success(instance_uuid, instance_name, records, + action=False, guest_hb=False): + """ + Analyze records and determine if instance is started + """ + always = True + + possible_records \ + = [(action, NFV_VIM.INSTANCE_NFVI_ACTION_START), + (always, NFV_VIM.INSTANCE_START_STATE), + (always, NFV_VIM.INSTANCE_START_CALLBACK), + (always, NFV_VIM.INSTANCE_START_STATE_COMPLETED), + (always, NFV_VIM.INSTANCE_INITIAL_STATE)] + + expected_records = list() + for allowed, data_type in possible_records: + if allowed: + expected_records.append(data_type) + + return _analysis_instances_success(instance_uuid, instance_name, records, + expected_records) + + +def analysis_instance_stop_success(instance_uuid, instance_name, records, + action=False, guest_hb=False): + """ + Analyze records and determine if instance is stopped + """ + always = True + + possible_records \ + = [(action, NFV_VIM.INSTANCE_NFVI_ACTION_START), + (always, NFV_VIM.INSTANCE_STOP_STATE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_VOTE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_VOTE_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_VOTE_RESULT), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_PRE_NOTIFY), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_PRE_NOTIFY_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_DISABLE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_DISABLE_CALLBACK), + (always, NFV_VIM.INSTANCE_STOP_CALLBACK), + (always, NFV_VIM.INSTANCE_STOP_STATE_COMPLETED), + (always, NFV_VIM.INSTANCE_INITIAL_STATE)] + + expected_records = list() + for allowed, data_type in possible_records: + if allowed: + expected_records.append(data_type) + + return _analysis_instances_success(instance_uuid, instance_name, records, + expected_records, action_types=['stop']) + + +def analysis_instance_pause_success(instance_uuid, instance_name, records, + action=False, guest_hb=False): + """ + Analyze records and determine if instance is paused + """ + always = True + + possible_records \ + = [(action, NFV_VIM.INSTANCE_NFVI_ACTION_START), + (always, NFV_VIM.INSTANCE_PAUSE_STATE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_VOTE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_VOTE_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_VOTE_RESULT), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_PRE_NOTIFY), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_PRE_NOTIFY_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_DISABLE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_DISABLE_CALLBACK), + (always, NFV_VIM.INSTANCE_PAUSE_CALLBACK), + (always, NFV_VIM.INSTANCE_PAUSE_STATE_COMPLETED), + (always, NFV_VIM.INSTANCE_INITIAL_STATE)] + + expected_records = list() + for allowed, data_type in possible_records: + if allowed: + expected_records.append(data_type) + + return _analysis_instances_success(instance_uuid, instance_name, records, + expected_records, action_types=['pause']) + + +def analysis_instance_unpause_success(instance_uuid, instance_name, records, + action=False, guest_hb=False): + """ + Analyze records and determine if instance is unpaused + """ + always = True + + possible_records \ + = [(action, NFV_VIM.INSTANCE_NFVI_ACTION_START), + (always, NFV_VIM.INSTANCE_UNPAUSE_STATE), + (always, NFV_VIM.INSTANCE_UNPAUSE_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_ENABLE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_ENABLE_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY_HEARTBEAT), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY_CALLBACK), + (always, NFV_VIM.INSTANCE_UNPAUSE_STATE_COMPLETED), + (always, NFV_VIM.INSTANCE_INITIAL_STATE)] + + expected_records = list() + for allowed, data_type in possible_records: + if allowed: + expected_records.append(data_type) + + return _analysis_instances_success(instance_uuid, instance_name, records, + expected_records, action_types=['unpause']) + + +def analysis_instance_suspend_success(instance_uuid, instance_name, records, + action=False, guest_hb=False): + """ + Analyze records and determine if instance is suspended + """ + always = True + + possible_records \ + = [(action, NFV_VIM.INSTANCE_NFVI_ACTION_START), + (always, NFV_VIM.INSTANCE_SUSPEND_STATE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_VOTE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_VOTE_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_VOTE_RESULT), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_PRE_NOTIFY), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_PRE_NOTIFY_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_DISABLE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_DISABLE_CALLBACK), + (always, NFV_VIM.INSTANCE_SUSPEND_CALLBACK), + (always, NFV_VIM.INSTANCE_SUSPEND_STATE_COMPLETED), + (always, NFV_VIM.INSTANCE_INITIAL_STATE)] + + expected_records = list() + for allowed, data_type in possible_records: + if allowed: + expected_records.append(data_type) + + return _analysis_instances_success(instance_uuid, instance_name, records, + expected_records, action_types=['suspend']) + + +def analysis_instance_resume_success(instance_uuid, instance_name, records, + action=False, guest_hb=False): + """ + Analyze records and determine if instance is resumed + """ + always = True + + possible_records \ + = [(action, NFV_VIM.INSTANCE_NFVI_ACTION_START), + (always, NFV_VIM.INSTANCE_RESUME_STATE), + (always, NFV_VIM.INSTANCE_RESUME_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_ENABLE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_ENABLE_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY_HEARTBEAT), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY_CALLBACK), + (always, NFV_VIM.INSTANCE_RESUME_STATE_COMPLETED), + (always, NFV_VIM.INSTANCE_INITIAL_STATE)] + + expected_records = list() + for allowed, data_type in possible_records: + if allowed: + expected_records.append(data_type) + + return _analysis_instances_success(instance_uuid, instance_name, records, + expected_records, action_types=['resume']) + + +def analysis_instance_reboot_success(instance_uuid, instance_name, records, + action=False, guest_hb=False): + """ + Analyze records and determine if instance rebooted + """ + def callback(idx, record): + record_data = record['data'] + if record_data['type'] == NFV_VIM.INSTANCE_GUEST_SERVICES_NOTIFY: + if record_data['restart_timeout'] == 0: + return False + return True + + always = True + + possible_records \ + = [(action, NFV_VIM.INSTANCE_NFVI_ACTION_START), + (always, NFV_VIM.INSTANCE_REBOOT_STATE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_VOTE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_VOTE_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_VOTE_RESULT), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_PRE_NOTIFY), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_PRE_NOTIFY_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_DISABLE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_DISABLE_CALLBACK), + (always, NFV_VIM.INSTANCE_REBOOT_CALLBACK), + (always, NFV_VIM.INSTANCE_REBOOT_STATE_COMPLETED), + (always, NFV_VIM.INSTANCE_INITIAL_STATE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_NOTIFY)] + + expected_records = list() + for allowed, data_type in possible_records: + if allowed: + expected_records.append(data_type) + + return _analysis_instances_success(instance_uuid, instance_name, records, + expected_records, action_types=['reboot'], + callback=callback) + + +def analysis_instance_rebuild_success(instance_uuid, instance_name, records, + action=False, guest_hb=False): + """ + Analyze records and determine if instance was rebuilt + """ + always = True + + possible_records \ + = [(action, NFV_VIM.INSTANCE_NFVI_ACTION_START), + (always, NFV_VIM.INSTANCE_REBUILD_STATE), + (always, NFV_VIM.INSTANCE_REBUILD_CALLBACK), + (always, NFV_VIM.INSTANCE_REBUILD_STATE_COMPLETED), + (always, NFV_VIM.INSTANCE_INITIAL_STATE)] + + expected_records = list() + for allowed, data_type in possible_records: + if allowed: + expected_records.append(data_type) + + return _analysis_instances_success(instance_uuid, instance_name, records, + expected_records) + + +def analysis_instance_live_migrate_success(instance_uuid, instance_name, + records, action=False, + guest_hb=False): + """ + Analyze records and determine if instance live-migrated + """ + always = True + + possible_records \ + = [(action, NFV_VIM.INSTANCE_NFVI_ACTION_START), + (always, NFV_VIM.INSTANCE_LIVE_MIGRATE_STATE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_VOTE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_VOTE_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_VOTE_RESULT), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_PRE_NOTIFY), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_PRE_NOTIFY_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_DISABLE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_DISABLE_CALLBACK), + (always, NFV_VIM.INSTANCE_LIVE_MIGRATE_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_LIVE_MIGRATE_FINISH_STATE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_ENABLE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_ENABLE_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY_HEARTBEAT), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY_CALLBACK), + (always, NFV_VIM.INSTANCE_INITIAL_STATE)] + + expected_records = list() + for allowed, data_type in possible_records: + if allowed: + expected_records.append(data_type) + + return _analysis_instances_success(instance_uuid, instance_name, records, + expected_records, + action_types=['live-migrate', + 'live_migrate', + 'live_migrate_begin', + 'live_migrate_end']) + + +def analysis_instance_cold_migrate_success(instance_uuid, instance_name, + records, action=False, + guest_hb=False): + """ + Analyze records and determine if instance cold-migrated + """ + always = True + guest_hb_only = not action and guest_hb + + possible_records \ + = [(action, NFV_VIM.INSTANCE_NFVI_ACTION_START), + (always, NFV_VIM.INSTANCE_COLD_MIGRATE_STATE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_VOTE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_VOTE_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_VOTE_RESULT), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_PRE_NOTIFY), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_PRE_NOTIFY_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_DISABLE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_DISABLE_CALLBACK), + (always, NFV_VIM.INSTANCE_COLD_MIGRATE_CALLBACK), + (not action, NFV_VIM.INSTANCE_COLD_MIGRATE_CONFIRM_STATE), + (not action, NFV_VIM.INSTANCE_COLD_MIGRATE_CONFIRM_CALLBACK), + (guest_hb_only, NFV_VIM.INSTANCE_GUEST_SERVICES_ENABLE), + (guest_hb_only, NFV_VIM.INSTANCE_GUEST_SERVICES_ENABLE_CALLBACK), + (guest_hb_only, NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY_HEARTBEAT), + (guest_hb_only, NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY), + (guest_hb_only, NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY_CALLBACK), + (always, NFV_VIM.INSTANCE_INITIAL_STATE)] + + expected_records = list() + for allowed, data_type in possible_records: + if allowed: + expected_records.append(data_type) + + return _analysis_instances_success(instance_uuid, instance_name, records, + expected_records, + action_types=['cold-migrate', + 'cold_migrate', + 'cold_migrate_begin', + 'cold_migrate_end']) + + +def analysis_instance_cold_migrate_confirm_success(instance_uuid, instance_name, + records, action=False, + guest_hb=False): + """ + Analyze records and determine if instance cold-migrate confirmed + """ + possible_records \ + = [(action, NFV_VIM.INSTANCE_NFVI_ACTION_START), + (action, NFV_VIM.INSTANCE_COLD_MIGRATE_CONFIRM_STATE), + (action, NFV_VIM.INSTANCE_COLD_MIGRATE_CONFIRM_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_ENABLE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_ENABLE_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY_HEARTBEAT), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY_CALLBACK), + (action, NFV_VIM.INSTANCE_INITIAL_STATE)] + + expected_records = list() + for allowed, data_type in possible_records: + if allowed: + expected_records.append(data_type) + + return _analysis_instances_success(instance_uuid, instance_name, records, + expected_records, + action_types=['cold-migrate', + 'cold_migrate', + 'cold_migrate_begin', + 'cold_migrate_end']) + + +def analysis_instance_cold_migrate_revert_success(instance_uuid, instance_name, + records, action=False, + guest_hb=False): + """ + Analyze records and determine if instance cold-migrate reverted + """ + possible_records \ + = [(action, NFV_VIM.INSTANCE_NFVI_ACTION_START), + (action, NFV_VIM.INSTANCE_COLD_MIGRATE_REVERT_STATE), + (action, NFV_VIM.INSTANCE_COLD_MIGRATE_REVERT_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_ENABLE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_ENABLE_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY_HEARTBEAT), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY_CALLBACK), + (action, NFV_VIM.INSTANCE_INITIAL_STATE)] + + expected_records = list() + for allowed, data_type in possible_records: + if allowed: + expected_records.append(data_type) + + return _analysis_instances_success(instance_uuid, instance_name, records, + expected_records, + action_types=['cold-migrate', + 'cold_migrate', + 'cold_migrate_begin', + 'cold_migrate_end']) + + +def analysis_instance_resize_success(instance_uuid, instance_name, records, + action=False, guest_hb=False): + """ + Analyze records and determine if instance resized + """ + possible_records \ + = [(action, NFV_VIM.INSTANCE_NFVI_ACTION_START), + (action, NFV_VIM.INSTANCE_RESIZE_STATE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_VOTE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_VOTE_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_VOTE_RESULT), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_PRE_NOTIFY), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_PRE_NOTIFY_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_DISABLE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_DISABLE_CALLBACK), + (action, NFV_VIM.INSTANCE_RESIZE_CALLBACK), + (action, NFV_VIM.INSTANCE_INITIAL_STATE)] + + expected_records = list() + for allowed, data_type in possible_records: + if allowed: + expected_records.append(data_type) + + return _analysis_instances_success(instance_uuid, instance_name, records, + expected_records, + action_types=['resize', 'resize_begin', + 'resize_end']) + + +def analysis_instance_resize_confirm_success(instance_uuid, instance_name, + records, action=False, + guest_hb=False): + """ + Analyze records and determine if instance resize confirmed + """ + possible_records \ + = [(action, NFV_VIM.INSTANCE_NFVI_ACTION_START), + (action, NFV_VIM.INSTANCE_RESIZE_CONFIRM_STATE), + (action, NFV_VIM.INSTANCE_RESIZE_CONFIRM_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_ENABLE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_ENABLE_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY_HEARTBEAT), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY_CALLBACK), + (action, NFV_VIM.INSTANCE_INITIAL_STATE)] + + expected_records = list() + for allowed, data_type in possible_records: + if allowed: + expected_records.append(data_type) + + return _analysis_instances_success(instance_uuid, instance_name, records, + expected_records, + action_types=['confirm-resize', 'resize', + 'resize_begin', 'resize_end']) + + +def analysis_instance_resize_revert_success(instance_uuid, instance_name, + records, action=False, + guest_hb=False): + """ + Analyze records and determine if instance resize reverted + """ + possible_records \ + = [(action, NFV_VIM.INSTANCE_NFVI_ACTION_START), + (action, NFV_VIM.INSTANCE_RESIZE_REVERT_STATE), + (action, NFV_VIM.INSTANCE_RESIZE_REVERT_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_ENABLE), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_ENABLE_CALLBACK), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY_HEARTBEAT), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY), + (guest_hb, NFV_VIM.INSTANCE_GUEST_SERVICES_POST_NOTIFY_CALLBACK), + (action, NFV_VIM.INSTANCE_INITIAL_STATE)] + + expected_records = list() + for allowed, data_type in possible_records: + if allowed: + expected_records.append(data_type) + + return _analysis_instances_success(instance_uuid, instance_name, records, + expected_records, + action_types=['revert-resize', 'resize', + 'resize_begin', 'resize_end']) + + +def analysis_stdout(records): + """ + Analyze records and display results to stdout + """ + def timestamp_str(timestamp_data): + return timestamp_data.strftime('%Y-%m-%d %H:%M:%S.%f')[:-3] + + hosts = dict() + instances = dict() + + hosts_state_change = dict() + instances_state_change = dict() + + print("\nAnalysis:") + + for idx, record in enumerate(records): + data = record['data'] + if data['type'] in [NFV_VIM.HOST_CONFIGURE_STATE, + NFV_VIM.HOST_ENABLING_STATE, + NFV_VIM.HOST_ENABLED_STATE, + NFV_VIM.HOST_DISABLING_STATE, + NFV_VIM.HOST_DISABLED_STATE, + NFV_VIM.HOST_DISABLING_FAILED_STATE, + NFV_VIM.HOST_DELETING_STATE, + NFV_VIM.HOST_DELETED_STATE]: + + if data['host_name'] in hosts: + prev_record = hosts[data['host_name']] + elapsed_time = record['timestamp'] - prev_record['timestamp'] + print(" %s (%s=%s) %s" + % (timestamp_str(record['timestamp']), u"\u0394", + elapsed_time, data['log'])) + else: + print(" %s %s" % (timestamp_str(record['timestamp']), + data['log'])) + + hosts[data['host_name']] = record + + elif data['type'] in [NFV_VIM.INSTANCE_DIRECTOR_EVACUATE_FAILED, + NFV_VIM.INSTANCE_DIRECTOR_EVACUATE_TIMEOUT, + NFV_VIM.INSTANCE_DIRECTOR_MIGRATE_FAILED, + NFV_VIM.INSTANCE_DIRECTOR_MIGRATE_TIMEOUT]: + print(" ** %s %s" % (timestamp_str(record['timestamp']), + data['log'])) + + elif data['type'] in [NFV_VIM.INSTANCE_DIRECTOR_EVACUATE_SUCCESS, + NFV_VIM.INSTANCE_DIRECTOR_MIGRATE_SUCCESS]: + print(" %s %s" % (timestamp_str(record['timestamp']), + data['log'])) + + elif data['type'] in [NFV_VIM.INSTANCE_LIVE_MIGRATE_STATE, + NFV_VIM.INSTANCE_LIVE_MIGRATE_FINISH_STATE, + NFV_VIM.INSTANCE_COLD_MIGRATE_STATE, + NFV_VIM.INSTANCE_COLD_MIGRATE_CONFIRM_STATE, + NFV_VIM.INSTANCE_COLD_MIGRATE_REVERT_STATE, + NFV_VIM.INSTANCE_EVACUATE_STATE, + NFV_VIM.INSTANCE_START_STATE, + NFV_VIM.INSTANCE_STOP_STATE, + NFV_VIM.INSTANCE_PAUSE_STATE, + NFV_VIM.INSTANCE_UNPAUSE_STATE, + NFV_VIM.INSTANCE_SUSPEND_STATE, + NFV_VIM.INSTANCE_RESUME_STATE, + NFV_VIM.INSTANCE_REBOOT_STATE, + NFV_VIM.INSTANCE_REBUILD_STATE, + NFV_VIM.INSTANCE_FAIL_STATE, + NFV_VIM.INSTANCE_DELETE_STATE, + NFV_VIM.INSTANCE_RESIZE_STATE, + NFV_VIM.INSTANCE_RESIZE_CONFIRM_STATE, + NFV_VIM.INSTANCE_RESIZE_REVERT_STATE, + NFV_VIM.INSTANCE_GUEST_SERVICES_CREATE_STATE, + NFV_VIM.INSTANCE_GUEST_SERVICES_DELETE_STATE, + NFV_VIM.INSTANCE_GUEST_SERVICES_ENABLE_STATE, + NFV_VIM.INSTANCE_GUEST_SERVICES_DISABLE_STATE, + NFV_VIM.INSTANCE_GUEST_SERVICES_SET_STATE]: + instances[data['instance_name']] = record + + elif data['type'] in [NFV_VIM.INSTANCE_INITIAL_STATE]: + if data['instance_name'] in instances: + prev_record = instances[data['instance_name']] + elapsed_time = record['timestamp'] - prev_record['timestamp'] + + print(" %s (%s=%s) %s" + % (timestamp_str(prev_record['timestamp']), u"\u0394", + elapsed_time, prev_record['data']['log'])) + + elif data['type'] in [NFV_VIM.INSTANCE_NFVI_ACTION_START]: + print(" %s %s" % (timestamp_str(record['timestamp']), + data['log'])) + + elif data['type'] == NFV_VIM.HOST_NFVI_STATE_CHANGE: + hosts_state_change[data['host_name']] = record + + elif data['type'] == NFV_VIM.INSTANCE_NFVI_STATE_CHANGE: + instances_state_change[data['instance_name']] = record + + print("\nHost-States (last-seen):") + hosts_state = sorted(hosts_state_change.values(), + key=lambda k: k['timestamp']) + + for idx, host in enumerate(hosts_state): + timestamp = host['timestamp'] + host_data = host['data'] + print("%4i. %s %16s: %s %s %s" % ( + idx, timestamp_str(timestamp), + host_data['host_name'], + host_data['nfvi_admin_state'], + host_data['nfvi_oper_state'], + host_data['nfvi_avail_state'])) + + print("\nInstance-States (last-seen):") + instances_state = sorted(instances_state_change.values(), + key=lambda k: k['timestamp']) + + for idx, instance in enumerate(instances_state): + timestamp = instance['timestamp'] + instance_data = instance['data'] + print("%4i. %s %32s (%s): %s %s %s (%s %s %s) on host %s" % ( + idx, timestamp_str(timestamp), + instance_data['instance_name'], + instance_data['instance_uuid'], + instance_data['instance_admin_state'], + instance_data['instance_oper_state'], + instance_data['instance_avail_status'], + instance_data['nfvi_vm_state'], + instance_data['nfvi_task_state'], + instance_data['nfvi_power_state'], + instance_data['host_name'])) diff --git a/nfv/nfv-common/nfv_common/forensic/_defs.py b/nfv/nfv-common/nfv_common/forensic/_defs.py new file mode 100755 index 00000000..3415b3c7 --- /dev/null +++ b/nfv/nfv-common/nfv_common/forensic/_defs.py @@ -0,0 +1,125 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# flake8: noqa +# + +class _NfvVimRecordType(object): + """ + NFV-VIM Record Type Constants + """ + HOST_NFVI_STATE_CHANGE = 'nfv_vim_host_nfvi_state_change' + HOST_CONFIGURE_STATE = 'nfv_vim_host_configure_state' + HOST_ENABLING_STATE = 'nfv_vim_host_enabling_state' + HOST_ENABLED_STATE = 'nfv_vim_host_enabled_state' + HOST_DISABLING_STATE = 'nfv_vim_host_disabling_state' + HOST_DISABLED_STATE = 'nfv_vim_host_disabled_state' + HOST_DISABLING_FAILED_STATE = 'nfv_vim_host_disabling_failed_state' + HOST_DELETING_STATE = 'nfv_vim_host_deleting_state' + HOST_DELETED_STATE = 'nfv_vim_host_deleted_state' + INSTANCE_DIRECTOR_EVACUATE_FAILED = 'nfv_vim_director_evacuate_failed' + INSTANCE_DIRECTOR_EVACUATE_TIMEOUT = 'nfv_vim_director_evacuate_timeout' + INSTANCE_DIRECTOR_EVACUATE_SUCCESS = 'nfv_vim_director_evacuate_success' + INSTANCE_DIRECTOR_MIGRATE_FAILED = 'nfv_vim_director_migrate_failed' + INSTANCE_DIRECTOR_MIGRATE_TIMEOUT = 'nfv_vim_director_migrate_timeout' + INSTANCE_DIRECTOR_MIGRATE_SUCCESS = 'nfv_vim_director_migrate_success' + INSTANCE_INITIAL_STATE = 'nfv_vim_instance_initial_state' + INSTANCE_LIVE_MIGRATE_STATE = 'nfv_vim_instance_live_migrate_state' + INSTANCE_LIVE_MIGRATE_STATE_FAILED = 'nfv_vim_instance_live_migrate_state_failed' + INSTANCE_LIVE_MIGRATE_STATE_TIMED_OUT = 'nfv_vim_instance_live_migrate_state_timed_out' + INSTANCE_LIVE_MIGRATE_FINISH_STATE = 'nfv_vim_instance_live_migrate_finish_state' + INSTANCE_LIVE_MIGRATE_FINISH_STATE_FAILED = 'nfv_vim_instance_live_migrate_finish_state_failed' + INSTANCE_LIVE_MIGRATE_FINISH_STATE_TIMED_OUT = 'nfv_vim_instance_live_migrate_finish_state_timed_out' + INSTANCE_COLD_MIGRATE_STATE = 'nfv_vim_instance_cold_migrate_state' + INSTANCE_COLD_MIGRATE_STATE_FAILED = 'nfv_vim_instance_cold_migrate_state_failed' + INSTANCE_COLD_MIGRATE_STATE_TIMED_OUT = 'nfv_vim_instance_cold_migrate_state_timed_out' + INSTANCE_COLD_MIGRATE_CONFIRM_STATE = 'nfv_vim_instance_cold_migrate_confirm_state' + INSTANCE_COLD_MIGRATE_CONFIRM_STATE_FAILED = 'nfv_vim_instance_cold_migrate_confirm_state_failed' + INSTANCE_COLD_MIGRATE_CONFIRM_STATE_TIMED_OUT = 'nfv_vim_instance_cold_migrate_confirm_state_timed_out' + INSTANCE_COLD_MIGRATE_REVERT_STATE = 'nfv_vim_instance_cold_migrate_revert_state' + INSTANCE_COLD_MIGRATE_REVERT_STATE_FAILED = 'nfv_vim_instance_cold_migrate_revert_state_failed' + INSTANCE_COLD_MIGRATE_REVERT_STATE_TIMED_OUT = 'nfv_vim_instance_cold_migrate_revert_state_timed_out' + INSTANCE_EVACUATE_STATE = 'nfv_vim_instance_evacuate_state' + INSTANCE_START_STATE = 'nfv_vim_instance_start_state' + INSTANCE_START_STATE_COMPLETED = 'nfv_vim_instance_start_state_completed' + INSTANCE_START_STATE_FAILED = 'nfv_vim_instance_start_state_failed' + INSTANCE_START_STATE_TIMED_OUT = 'nfv_vim_instance_start_state_timed_out' + INSTANCE_STOP_STATE = 'nfv_vim_instance_stop_state' + INSTANCE_STOP_STATE_COMPLETED = 'nfv_vim_instance_stop_state_completed' + INSTANCE_STOP_STATE_FAILED = 'nfv_vim_instance_stop_state_failed' + INSTANCE_STOP_STATE_TIMED_OUT = 'nfv_vim_instance_stop_state_timed_out' + INSTANCE_PAUSE_STATE = 'nfv_vim_instance_pause_state' + INSTANCE_PAUSE_STATE_COMPLETED = 'nfv_vim_instance_pause_state_completed' + INSTANCE_PAUSE_STATE_FAILED = 'nfv_vim_instance_pause_state_failed' + INSTANCE_PAUSE_STATE_TIMED_OUT = 'nfv_vim_instance_pause_state_timed_out' + INSTANCE_UNPAUSE_STATE = 'nfv_vim_instance_unpause_state' + INSTANCE_UNPAUSE_STATE_COMPLETED = 'nfv_vim_instance_unpause_state_completed' + INSTANCE_UNPAUSE_STATE_FAILED = 'nfv_vim_instance_unpause_state_failed' + INSTANCE_UNPAUSE_STATE_TIMED_OUT = 'nfv_vim_instance_unpause_state_timed_out' + INSTANCE_SUSPEND_STATE = 'nfv_vim_instance_suspend_state' + INSTANCE_SUSPEND_STATE_COMPLETED = 'nfv_vim_instance_suspend_state_completed' + INSTANCE_SUSPEND_STATE_FAILED = 'nfv_vim_instance_suspend_state_failed' + INSTANCE_SUSPEND_STATE_TIMED_OUT = 'nfv_vim_instance_suspend_state_timed_out' + INSTANCE_RESUME_STATE = 'nfv_vim_instance_resume_state' + INSTANCE_RESUME_STATE_COMPLETED = 'nfv_vim_instance_resume_state_completed' + INSTANCE_RESUME_STATE_FAILED = 'nfv_vim_instance_resume_state_failed' + INSTANCE_RESUME_STATE_TIMED_OUT = 'nfv_vim_instance_resume_state_timed_out' + INSTANCE_REBOOT_STATE = 'nfv_vim_instance_reboot_state' + INSTANCE_REBOOT_STATE_COMPLETED = 'nfv_vim_instance_reboot_state_completed' + INSTANCE_REBOOT_STATE_FAILED = 'nfv_vim_instance_reboot_state_failed' + INSTANCE_REBOOT_STATE_TIMED_OUT = 'nfv_vim_instance_reboot_state_timed_out' + INSTANCE_REBUILD_STATE = 'nfv_vim_instance_rebuild_state' + INSTANCE_REBUILD_STATE_COMPLETED = 'nfv_vim_instance_rebuild_state_completed' + INSTANCE_REBUILD_STATE_FAILED = 'nfv_vim_instance_rebuild_state_failed' + INSTANCE_REBUILD_STATE_TIMED_OUT = 'nfv_vim_instance_rebuild_state_timed_out' + INSTANCE_FAIL_STATE = 'nfv_vim_instance_fail_state' + INSTANCE_DELETE_STATE = 'nfv_vim_instance_delete_state' + INSTANCE_RESIZE_STATE = 'nfv_vim_instance_resize_state' + INSTANCE_RESIZE_STATE_FAILED = 'nfv_vim_instance_resize_state_failed' + INSTANCE_RESIZE_STATE_TIMED_OUT = 'nfv_vim_instance_resize_state_timed_out' + INSTANCE_RESIZE_CONFIRM_STATE = 'nfv_vim_instance_resize_confirm_state' + INSTANCE_RESIZE_CONFIRM_STATE_FAILED = 'nfv_vim_instance_resize_confirm_state_failed' + INSTANCE_RESIZE_CONFIRM_STATE_TIMED_OUT = 'nfv_vim_instance_resize_confirm_state_timed_out' + INSTANCE_RESIZE_REVERT_STATE = 'nfv_vim_instance_resize_revert_state' + INSTANCE_RESIZE_REVERT_STATE_FAILED = 'nfv_vim_instance_resize_revert_state_failed' + INSTANCE_RESIZE_REVERT_STATE_TIMED_OUT = 'nfv_vim_instance_resize_revert_state_timed_out' + INSTANCE_GUEST_SERVICES_CREATE_STATE = 'nfv_vim_instance_guest_services_create_state' + INSTANCE_GUEST_SERVICES_DELETE_STATE = 'nfv_vim_instance_guest_services_delete_state' + INSTANCE_GUEST_SERVICES_ENABLE_STATE = 'nfv_vim_instance_guest_services_enable_state' + INSTANCE_GUEST_SERVICES_DISABLE_STATE = 'nfv_vim_instance_guest_services_disable_state' + INSTANCE_GUEST_SERVICES_SET_STATE = 'nfv_vim_instance_guest_services_set_state' + INSTANCE_NFVI_ACTION_START = 'nfv_vim_instance_nfvi_action_initial' + INSTANCE_NFVI_STATE_CHANGE = 'nfv_vim_instance_nfvi_state_change' + INSTANCE_START_CALLBACK = 'nfv_vim_instance_start_callback' + INSTANCE_STOP_CALLBACK = 'nfv_vim_instance_stop_callback' + INSTANCE_PAUSE_CALLBACK = 'nfv_vim_instance_pause_callback' + INSTANCE_UNPAUSE_CALLBACK = 'nfv_vim_instance_unpause_callback' + INSTANCE_SUSPEND_CALLBACK = 'nfv_vim_instance_suspend_callback' + INSTANCE_RESUME_CALLBACK = 'nfv_vim_instance_resume_callback' + INSTANCE_REBOOT_CALLBACK = 'nfv_vim_instance_reboot_callback' + INSTANCE_REBUILD_CALLBACK = 'nfv_vim_instance_rebuild_callback' + INSTANCE_LIVE_MIGRATE_CALLBACK = 'nfv_vim_instance_live_migrate_callback' + INSTANCE_COLD_MIGRATE_CALLBACK = 'nfv_vim_instance_cold_migrate_callback' + INSTANCE_COLD_MIGRATE_CONFIRM_CALLBACK = 'nfv_vim_instance_cold_migrate_confirm_callback' + INSTANCE_COLD_MIGRATE_REVERT_CALLBACK = 'nfv_vim_instance_cold_migrate_revert_callback' + INSTANCE_RESIZE_CALLBACK = 'nfv_vim_instance_resize_callback' + INSTANCE_RESIZE_CONFIRM_CALLBACK = 'nfv_vim_instance_resize_confirm_callback' + INSTANCE_RESIZE_REVERT_CALLBACK = 'nfv_vim_instance_resize_revert_callback' + INSTANCE_GUEST_SERVICES_NOTIFY = 'nfv_vim_instance_guest_services_notify' + INSTANCE_GUEST_SERVICES_ENABLE = 'nfv_vim_instance_guest_services_enable' + INSTANCE_GUEST_SERVICES_ENABLE_CALLBACK = 'nfv_vim_instance_guest_services_enable_callback' + INSTANCE_GUEST_SERVICES_DISABLE = 'nfv_vim_instance_guest_services_disable' + INSTANCE_GUEST_SERVICES_DISABLE_CALLBACK = 'nfv_vim_instance_guest_services_disable_callback' + INSTANCE_GUEST_SERVICES_VOTE = 'nfv_vim_instance_guest_services_vote' + INSTANCE_GUEST_SERVICES_VOTE_RESULT = 'nfv_vim_instance_guest_services_vote_result' + INSTANCE_GUEST_SERVICES_VOTE_CALLBACK = 'nfv_vim_instance_guest_services_vote_callback' + INSTANCE_GUEST_SERVICES_PRE_NOTIFY = 'nfv_vim_instance_guest_services_pre_notify' + INSTANCE_GUEST_SERVICES_PRE_NOTIFY_CALLBACK = 'nfv_vim_instance_guest_services_pre_notify_callback' + INSTANCE_GUEST_SERVICES_POST_NOTIFY = 'nfv_vim_instance_guest_services_post_notify' + INSTANCE_GUEST_SERVICES_POST_NOTIFY_CALLBACK = 'nfv_vim_instance_guest_services_post_notify_callback' + INSTANCE_GUEST_SERVICES_POST_NOTIFY_HEARTBEAT = 'nfv_vim_instance_guest_services_post_notify_heartbeat' + +# Constant Instantiation +NFV_VIM = _NfvVimRecordType() diff --git a/nfv/nfv-common/nfv_common/forensic/_evidence.py b/nfv/nfv-common/nfv_common/forensic/_evidence.py new file mode 100755 index 00000000..07c340b5 --- /dev/null +++ b/nfv/nfv-common/nfv_common/forensic/_evidence.py @@ -0,0 +1,63 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +from nfv_common import debug + +import _parsers + +DLOG = debug.debug_get_logger('forensic-evidence') + + +def evidence_from_files(files, start_date, end_date, progress=None): + """ + Gather evidence + """ + records = list() + file_ctrl = dict() + + total_lines = 0 + for parser_name, file_name in files.items(): + total_lines += sum(1 for _ in open(file_name)) + file_ctrl[file_name] = (parser_name, open(file_name), None) + + lines_read = 0 + while 0 < len(file_ctrl): + # Read a record from each file. + for file_name, (parser_name, f, record) in file_ctrl.items(): + if record is None: + line = f.readline() + while line: + parser = _parsers.parser_get(parser_name) + record = parser.parse(start_date, end_date, line) + if record is not None: + file_ctrl[file_name] = (parser_name, f, record) + break + line = f.readline() + lines_read += 1 + + # Remove files that do not have any more records. + for file_name, (parser_name, f, record) in file_ctrl.items(): + if record is None: + del file_ctrl[file_name] + + # Sort records and take the earliest record. + earliest = None + for file_name, (parser_name, f, record) in file_ctrl.items(): + if earliest is None: + earliest = (file_name, parser_name, f, record) + else: + _, _, _, earliest_record = earliest + if earliest_record['timestamp'] > record['timestamp']: + earliest = (file_name, parser_name, f, record) + + if earliest is not None: + file_name, parser_name, f, record = earliest + file_ctrl[file_name] = (parser_name, f, None) + records.append(record) + + if progress is not None: + progress((float(lines_read) / total_lines) * 100) + + return records diff --git a/nfv/nfv-common/nfv_common/forensic/_forensic_module.py b/nfv/nfv-common/nfv_common/forensic/_forensic_module.py new file mode 100755 index 00000000..f6ebfe11 --- /dev/null +++ b/nfv/nfv-common/nfv_common/forensic/_forensic_module.py @@ -0,0 +1,19 @@ +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import _parsers + + +def forensic_initialize(): + """ + Initialize forensic module + """ + _parsers.parser_initialize() + + +def forensic_finalize(): + """ + Finalize forensic module + """ + _parsers.parser_finalize() diff --git a/nfv/nfv-common/nfv_common/forensic/_nfv_vim_parser.py b/nfv/nfv-common/nfv_common/forensic/_nfv_vim_parser.py new file mode 100755 index 00000000..f2fc1141 --- /dev/null +++ b/nfv/nfv-common/nfv_common/forensic/_nfv_vim_parser.py @@ -0,0 +1,115 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import os +import re +import yaml +import datetime + +from pyparsing import Word, Combine, Suppress, Literal, Regex +from pyparsing import nums, alphas + + +class NfvVimParser(object): + """ + NFV-VIM Parser + """ + def __init__(self, config_data): + self._config_data = config_data + + year = Word(nums) + month = Suppress('-') + Word(nums) + day = Suppress('-') + Word(nums) + + hour = Suppress('T') + Word(nums) + minute = Suppress(':') + Word(nums) + second = Suppress(':') + Word(nums) + millisecond = Suppress('.') + Word(nums) + + hostname = Word(alphas + nums + '_' + '-' + '.') + + pid = (Suppress(Word(alphas + '_' + alphas + '-' + '[')) + Word(nums) + + Suppress(']')) + + ignore = Suppress(Word(alphas)) + + filename = Combine(Word(alphas + '_') + Literal(".py")) + + lineno = Suppress('.') + Word(nums) + + message = Regex(".*") + + self.__pattern = (year + month + day + hour + minute + second + + millisecond + hostname + pid + ignore + filename + + lineno + message) + + def parse_message(self, filename, lineno, message): + for log in self._config_data['logs']: + if log.get('file', None) is not None: + if filename != log['file']: + continue + + if log.get('lineno', None) is not None: + if lineno != str(log['lineno']): + continue + + query_obj = re.match(log['regex'], message) + if query_obj is not None: + message_data = dict() + message_data['type'] = log['type'] + message_data['log'] = message + for idx, field_name in enumerate(log['fields'], 1): + message_data[field_name] = query_obj.group(idx) + return message_data + return None + + def parse(self, start_date, end_date, line): + record = None + + try: + # verify minimum line length is met + if 70 < len(line): + parsed = self.__pattern.parseString(line) + + timestamp = datetime.datetime(int(parsed[0]), int(parsed[1]), + int(parsed[2]), int(parsed[3]), + int(parsed[4]), int(parsed[5]), + int(parsed[6])*1000) + + if start_date <= timestamp <= end_date: + message_data = self.parse_message(parsed[9], parsed[10], + parsed[11]) + if message_data is not None: + record = dict() + record['timestamp'] = timestamp + record['hostname'] = parsed[7] + record['pid'] = parsed[8] + record['filename'] = parsed[9] + record['lineno'] = parsed[10] + record['data'] = message_data + + except Exception as e: + print("Bad line: %s, exception=%s." % (line, e)) + + return record + + +def parser_initialize(): + """ + Initialize module + """ + path = os.path.abspath(__file__) + config_file = os.path.dirname(path) + "/config/nfv-vim.yaml" + if os.path.isfile(config_file): + config_data = yaml.load(open(config_file)) + return NfvVimParser(config_data) + return None + + +def parser_finalize(): + """ + Finalize module + """ + pass diff --git a/nfv/nfv-common/nfv_common/forensic/_parsers.py b/nfv/nfv-common/nfv_common/forensic/_parsers.py new file mode 100755 index 00000000..7af5cb0d --- /dev/null +++ b/nfv/nfv-common/nfv_common/forensic/_parsers.py @@ -0,0 +1,30 @@ +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import _nfv_vim_parser + +_parsers = dict() + + +def parser_get(parser_name): + """ + Returns the associated parser + """ + return _parsers.get(parser_name, None) + + +def parser_initialize(): + """ + Initialize module + """ + global _parsers + + _parsers['nfv-vim'] = _nfv_vim_parser.parser_initialize() + + +def parser_finalize(): + """ + Finalize module + """ + _nfv_vim_parser.parser_finalize() diff --git a/nfv/nfv-common/nfv_common/forensic/config/nfv-vim.yaml b/nfv/nfv-common/nfv_common/forensic/config/nfv-vim.yaml new file mode 100755 index 00000000..6872b073 --- /dev/null +++ b/nfv/nfv-common/nfv_common/forensic/config/nfv-vim.yaml @@ -0,0 +1,929 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +logs: + - name: Host State Change + type: nfv_vim_host_nfvi_state_change + file: _host.py + regex: "Host State-Change detected: nfvi_admin_state=(.*) + host_admin_state=(.*), nfvi_oper_state=(.*) host_oper_state=(.*), + nfvi_avail_state=(.*) host_avail_status=(.*), locking=(.*) + unlocking=(.*) fsm current_state=(.*) for (.*)." + fields: + - nfvi_admin_state + - host_admin_state + - nfvi_oper_state + - host_oper_state + - nfvi_avail_state + - host_avail_status + - locking + - unlocking + - fsm_current_state + - host_name + + - name: Host Configure State + type: nfv_vim_host_configure_state + file: _host_state_configure.py + regex: "Entering state \\(configure\\) for (.*)." + fields: + - host_name + + - name: Host Enabling State + type: nfv_vim_host_enabling_state + file: _host_state_enabling.py + regex: "Entering state \\(enabling\\) for (.*)." + fields: + - host_name + + - name: Host Enabled State + type: nfv_vim_host_enabled_state + file: _host_state_enabled.py + regex: "Entering state \\(enabled\\) for (.*)." + fields: + - host_name + + - name: Host Disabling State + type: nfv_vim_host_disabling_state + file: _host_state_disabling.py + regex: "Entering state \\(disabling\\) for (.*)." + fields: + - host_name + + - name: Host Disabled State + type: nfv_vim_host_disabled_state + file: _host_state_disabled.py + regex: "Entering state \\(disabled\\) for (.*)." + fields: + - host_name + + - name: Host Disabling Failed State + type: nfv_vim_host_disabling_failed_state + file: _host_state_disabling_failed.py + regex: "Entering state \\(disabling-failed\\) for (.*)." + fields: + - host_name + + - name: Host Deleting State + type: nfv_vim_host_deleting_state + file: _host_state_deleting.py + regex: "Entering state \\(deleting\\) for (.*)." + fields: + - host_name + + - name: Host Deleted State + type: nfv_vim_host_deleted_state + file: _host_state_deleted.py + regex: "Entering state \\(deleted\\) for (.*)." + fields: + - host_name + + - name: Instance Director Evacuate Failed + type: nfv_vim_director_evacuate_failed + file: _instance_director.py + regex: "Evacuate of instance (.*) from host (.*) failed." + fields: + - instance_name + - host_name + + - name: Instance Director Evacuate Timeout + type: nfv_vim_director_evacuate_timeout + file: _instance_director.py + regex: "Evacuate of instance (.*) from host (.*) timed out." + fields: + - instance_name + - host_name + + - name: Instance Director Evacuate Success + type: nfv_vim_director_evacuate_success + file: _instance_director.py + regex: "Evacuate of instance (.*) from host (.*) succeeded." + fields: + - instance_name + - host_name + + - name: Instance Director Migrate Failed + type: nfv_vim_director_migrate_failed + file: _instance_director.py + regex: "Migrate of instance (.*) from host (.*) failed." + fields: + - instance_name + - host_name + + - name: Instance Director Migrate Timeout + type: nfv_vim_director_migrate_timeout + file: _instance_director.py + regex: "Migrate of instance (.*) from host (.*) timed out." + fields: + - instance_name + - host_name + + - name: Instance Director Migrate Success + type: nfv_vim_director_migrate_success + file: _instance_director.py + regex: "Migrate of instance (.*) from host (.*) succeeded." + fields: + - instance_name + - host_name + + - name: Instance Initial State + type: nfv_vim_instance_initial_state + file: _instance_state_initial.py + regex: "Entering state \\(initial\\) for (.*)." + fields: + - instance_name + + - name: Instance Live-Migrate State + type: nfv_vim_instance_live_migrate_state + file: _instance_state_live_migrate.py + regex: "Entering state \\(live-migrate\\) for (.*)." + fields: + - instance_name + + - name: Instance Live-Migrate State + type: nfv_vim_instance_live_migrate_state + file: _instance_state_live_migrate.py + regex: "Entering state \\(live-migrate\\) for (.*)." + fields: + - instance_name + + - name: Instance Live-Migrate State Failed + type: nfv_vim_instance_live_migrate_state_failed + file: _instance_state_live_migrate.py + regex: "Live-Migrate failed for (.*)." + fields: + - instance_name + + - name: Instance Live-Migrate State Timed out + type: nfv_vim_instance_live_migrate_state_timed_out + file: _instance_state_live_migrate.py + regex: "Live-Migrate timed out for (.*)." + fields: + - instance_name + + - name: Instance Live-Migrate Finish State + type: nfv_vim_instance_live_migrate_finish_state + file: _instance_state_live_migrate_finish.py + regex: "Entering state \\(live-migrate-finish\\) for (.*)." + fields: + - instance_name + + - name: Instance Live-Migrate Finish State Failed + type: nfv_vim_instance_live_migrate_finish_state_failed + file: _instance_state_live_migrate_finish.py + regex: "Live-Migrate-Finish failed for (.*)." + fields: + - instance_name + + - name: Instance Live-Migrate Finish State Timed out + type: nfv_vim_instance_live_migrate_finish_state_timed_out + file: _instance_state_live_migrate_finish.py + regex: "Live-Migrate-Finish timed out for (.*)." + fields: + - instance_name + + - name: Instance Cold-Migrate State + type: nfv_vim_instance_cold_migrate_state + file: _instance_state_cold_migrate.py + regex: "Entering state \\(cold-migrate\\) for (.*)." + fields: + - instance_name + + - name: Instance Cold-Migrate State Failed + type: nfv_vim_instance_cold_migrate_state_failed + file: _instance_state_cold_migrate.py + regex: "Cold-Migrate failed for (.*)." + fields: + - instance_name + + - name: Instance Cold-Migrate State Timed out + type: nfv_vim_instance_cold_migrate_state_timed_out + file: _instance_state_cold_migrate.py + regex: "Cold-Migrate timed out for (.*)." + fields: + - instance_name + + - name: Instance Cold-Migrate Confirm State + type: nfv_vim_instance_cold_migrate_confirm_state + file: _instance_state_cold_migrate_confirm.py + regex: "Entering state \\(cold-migrate-confirm\\) for (.*)." + fields: + - instance_name + + - name: Instance Cold-Migrate Confirm State Failed + type: nfv_vim_instance_cold_migrate_confirm_state_failed + file: _instance_state_cold_migrate_confirm.py + regex: "Cold-Migrate-Confirm failed for (.*)." + fields: + - instance_name + + - name: Instance Cold-Migrate Confirm State Timed out + type: nfv_vim_instance_cold_migrate_confirm_state_timed_out + file: _instance_state_cold_migrate_confirm.py + regex: "Cold-Migrate-Confirm timed out for (.*)." + fields: + - instance_name + + - name: Instance Cold-Migrate Revert State + type: nfv_vim_instance_cold_migrate_revert_state + file: _instance_state_cold_migrate_revert.py + regex: "Entering state \\(cold-migrate-revert\\) for (.*)." + fields: + - instance_name + + - name: Instance Cold-Migrate Revert State Failed + type: nfv_vim_instance_cold_migrate_revert_state_failed + file: _instance_state_cold_migrate_revert.py + regex: "Cold-Migrate-Revert failed for (.*)." + fields: + - instance_name + + - name: Instance Cold-Migrate Revert State Timed out + type: nfv_vim_instance_cold_migrate_revert_state_timed_out + file: _instance_state_cold_migrate_revert.py + regex: "Cold-Migrate-Revert timed out for (.*)." + fields: + - instance_name + + - name: Instance Evacuate State + type: nfv_vim_instance_evacuate_state + file: _instance_state_evacuate.py + regex: "Entering state \\(evacuate\\) for (.*)." + fields: + - instance_name + + - name: Instance Start State + type: nfv_vim_instance_start_state + file: _instance_state_start.py + regex: "Entering state \\(start\\) for (.*)." + fields: + - instance_name + + - name: Instance Start State Completed + type: nfv_vim_instance_start_state_completed + file: _instance_state_start.py + regex: "Start completed for (.*)." + fields: + - instance_name + + - name: Instance Start State Failed + type: nfv_vim_instance_start_state_failed + file: _instance_state_start.py + regex: "Start failed for (.*)." + fields: + - instance_name + + - name: Instance Start State Timed out + type: nfv_vim_instance_start_state_timed_out + file: _instance_state_start.py + regex: "Start timed out for (.*)." + fields: + - instance_name + + - name: Instance Stop State + type: nfv_vim_instance_stop_state + file: _instance_state_stop.py + regex: "Entering state \\(stop\\) for (.*)." + fields: + - instance_name + + - name: Instance Stop State Completed + type: nfv_vim_instance_stop_state_completed + file: _instance_state_stop.py + regex: "Stop completed for (.*)." + fields: + - instance_name + + - name: Instance Stop State Failed + type: nfv_vim_instance_stop_state_failed + file: _instance_state_stop.py + regex: "Stop failed for (.*)." + fields: + - instance_name + + - name: Instance Stop State Timed out + type: nfv_vim_instance_stop_state_timed_out + file: _instance_state_stop.py + regex: "Stop timed out for (.*)." + fields: + - instance_name + + - name: Instance Pause State + type: nfv_vim_instance_pause_state + file: _instance_state_pause.py + regex: "Entering state \\(pause\\) for (.*)." + fields: + - instance_name + + - name: Instance Pause State Completed + type: nfv_vim_instance_pause_state_completed + file: _instance_state_pause.py + regex: "Pause completed for (.*)." + fields: + - instance_name + + - name: Instance Pause State Failed + type: nfv_vim_instance_pause_state_failed + file: _instance_state_pause.py + regex: "Pause failed for (.*)." + fields: + - instance_name + + - name: Instance Pause State Timed out + type: nfv_vim_instance_pause_state_timed_out + file: _instance_state_pause.py + regex: "Pause timed out for (.*)." + fields: + - instance_name + + - name: Instance Unpause State + type: nfv_vim_instance_unpause_state + file: _instance_state_unpause.py + regex: "Entering state \\(unpause\\) for (.*)." + fields: + - instance_name + + - name: Instance Unpause State Completed + type: nfv_vim_instance_unpause_state_completed + file: _instance_state_unpause.py + regex: "Unpause completed for (.*)." + fields: + - instance_name + + - name: Instance Unpause State Failed + type: nfv_vim_instance_unpause_state_failed + file: _instance_state_unpause.py + regex: "Unpause failed for (.*)." + fields: + - instance_name + + - name: Instance Unpause State Timed out + type: nfv_vim_instance_unpause_state_timed_out + file: _instance_state_unpause.py + regex: "Unpause timed out for (.*)." + fields: + - instance_name + + - name: Instance Suspend State + type: nfv_vim_instance_suspend_state + file: _instance_state_suspend.py + regex: "Entering state \\(suspend\\) for (.*)." + fields: + - instance_name + + - name: Instance Suspend State Completed + type: nfv_vim_instance_suspend_state_completed + file: _instance_state_suspend.py + regex: "Suspend completed for (.*)." + fields: + - instance_name + + - name: Instance Suspend State Failed + type: nfv_vim_instance_suspend_state_failed + file: _instance_state_suspend.py + regex: "Suspend failed for (.*)." + fields: + - instance_name + + - name: Instance Suspend State Timed out + type: nfv_vim_instance_suspend_state_timed_out + file: _instance_state_suspend.py + regex: "Suspend timed out for (.*)." + fields: + - instance_name + + - name: Instance Resume State + type: nfv_vim_instance_resume_state + file: _instance_state_resume.py + regex: "Entering state \\(resume\\) for (.*)." + fields: + - instance_name + + - name: Instance Resume State Completed + type: nfv_vim_instance_resume_state_completed + file: _instance_state_resume.py + regex: "Resume completed for (.*)." + fields: + - instance_name + + - name: Instance Resume State Failed + type: nfv_vim_instance_resume_state_failed + file: _instance_state_resume.py + regex: "Resume failed for (.*)." + fields: + - instance_name + + - name: Instance Resume State Timed out + type: nfv_vim_instance_resume_state_timed_out + file: _instance_state_resume.py + regex: "Resume timed out for (.*)." + fields: + - instance_name + + - name: Instance Reboot State + type: nfv_vim_instance_reboot_state + file: _instance_state_reboot.py + regex: "Entering state \\(reboot\\) for (.*)." + fields: + - instance_name + + - name: Instance Reboot State Completed + type: nfv_vim_instance_reboot_state_completed + file: _instance_state_reboot.py + regex: "Reboot completed for (.*)." + fields: + - instance_name + + - name: Instance Reboot State Failed + type: nfv_vim_instance_reboot_state_failed + file: _instance_state_reboot.py + regex: "Reboot failed for (.*)." + fields: + - instance_name + + - name: Instance Reboot State Timed out + type: nfv_vim_instance_reboot_state_timed_out + file: _instance_state_reboot.py + regex: "Reboot timed out for (.*)." + fields: + - instance_name + + - name: Instance Rebuild State + type: nfv_vim_instance_rebuild_state + file: _instance_state_rebuild.py + regex: "Entering state \\(rebuild\\) for (.*)." + fields: + - instance_name + + - name: Instance Rebuild State Completed + type: nfv_vim_instance_rebuild_state_completed + file: _instance_state_rebuild.py + regex: "Rebuild completed for (.*)." + fields: + - instance_name + + - name: Instance Rebuild State Failed + type: nfv_vim_instance_rebuild_state_failed + file: _instance_state_rebuild.py + regex: "Rebuild failed for (.*)." + fields: + - instance_name + + - name: Instance Rebuild State Timed out + type: nfv_vim_instance_rebuild_state_timed_out + file: _instance_state_rebuild.py + regex: "Rebuild timed out for (.*)." + fields: + - instance_name + + - name: Instance Fail State + type: nfv_vim_instance_fail_state + file: _instance_state_fail.py + regex: "Entering state \\(fail\\) for (.*)." + fields: + - instance_name + + - name: Instance Delete State + type: nfv_vim_instance_delete_state + file: _instance_state_delete.py + regex: "Entering state \\(delete\\) for (.*)." + fields: + - instance_name + + - name: Instance Resize State + type: nfv_vim_instance_resize_state + file: _instance_state_resize.py + regex: "Entering state \\(resize\\) for (.*)." + fields: + - instance_name + + - name: Instance Resize State Failed + type: nfv_vim_instance_resize_state_failed + file: _instance_state_resize.py + regex: "Resize failed for (.*)." + fields: + - instance_name + + - name: Instance Resize State Timed out + type: nfv_vim_instance_resize_state_timed_out + file: _instance_state_resize.py + regex: "Reisze timed out for (.*)." + fields: + - instance_name + + - name: Instance Resize-Confirm State + type: nfv_vim_instance_resize_confirm_state + file: _instance_state_resize_confirm.py + regex: "Entering state \\(resize-confirm\\) for (.*)." + fields: + - instance_name + + - name: Instance Resize Confirm State Failed + type: nfv_vim_instance_resize_confirm_state_failed + file: _instance_state_resize_confirm.py + regex: "Resize-Confirm failed for (.*)." + fields: + - instance_name + + - name: Instance Resize Confirm State Timed out + type: nfv_vim_instance_resize_confirm_state_timed_out + file: _instance_state_resize_confirm.py + regex: "Reisze-Confirm timed out for (.*)." + fields: + - instance_name + + - name: Instance Resize-Revert State + type: nfv_vim_instance_resize_revert_state + file: _instance_state_resize_revert.py + regex: "Entering state \\(resize-revert\\) for (.*)." + fields: + - instance_name + + - name: Instance Resize Revert State Failed + type: nfv_vim_instance_resize_revert_state_failed + file: _instance_state_resize_revert.py + regex: "Resize-Revert failed for (.*)." + fields: + - instance_name + + - name: Instance Resize Revert State Timed out + type: nfv_vim_instance_resize_revert_state_timed_out + file: _instance_state_resize_revert.py + regex: "Reisze-Revert timed out for (.*)." + fields: + - instance_name + + - name: Instance Guest-Services-Create State + type: nfv_vim_instance_guest_services_create_state + file: _instance_state_guest_services_create.py + regex: "Entering state \\(guest-services-create\\) for (.*)." + fields: + - instance_name + + - name: Instance Guest-Services-Delete State + type: nfv_vim_instance_guest_services_delete_state + file: _instance_state_guest_services_delete.py + regex: "Entering state \\(guest-services-delete\\) for (.*)." + fields: + - instance_name + + - name: Instance Guest-Services-Enable State + type: nfv_vim_instance_guest_services_enable_state + file: _instance_state_guest_services_enable.py + regex: "Entering state \\(guest-services-enable\\) for (.*)." + fields: + - instance_name + + - name: Instance Guest-Services-Disable State + type: nfv_vim_instance_guest_services_disable_state + file: _instance_state_guest_services_disable.py + regex: "Entering state \\(guest-services-disable\\) for (.*)." + fields: + - instance_name + + - name: Instance Guest-Services-Set State + type: nfv_vim_instance_guest_services_set_state + file: _instance_state_guest_services_set.py + regex: "Entering state \\(guest-services-set\\) for (.*)." + fields: + - instance_name + + - name: Instance NFVI Action Initial + type: nfv_vim_instance_nfvi_action_initial + file: _vim_nfvi_events.py + regex: "Instance action, uuid=(.*), nfvi_action_data=Instance action, + type=(.*), params=(.*), state=initial, reason=" + fields: + - instance_uuid + - action_type + - action_params + + - name: Instance NFVI State Change + type: nfv_vim_instance_nfvi_state_change + file: _vim_nfvi_events.py + regex: "Instance state-change, nfvi_instance={'instance_type_uuid': u'(.*)', + 'name': u'(.*)', 'tenant_id': '(.*)', 'avail_status': \\[(.*)], 'nfvi_data': + {'vm_state': u'(.*)', 'task_state': u?'(.*)', 'power_state': '(.*)'}, + 'live_migration_support': (.*), 'oper_state': '(.*)', 'host_name': u'(.*)', + 'admin_state': '(.*)', 'action': '(.*)', 'image_uuid': (.*), 'uuid': u'(.*)'}." + fields: + - instance_type_uuid + - instance_name + - tenant_uuid + - instance_avail_status + - nfvi_vm_state + - nfvi_task_state + - nfvi_power_state + - live_migrate_support + - instance_oper_state + - host_name + - instance_admin_state + - instance_action_state + - image_uuid + - instance_uuid + + - name: Instance Start Callback + type: nfv_vim_instance_start_callback + file: _instance_task_work.py + regex: "Start-Instance callback for (.*), response={'completed': (.*), + 'reason': (.*)}." + fields: + - instance_name + - completed + - reason + + - name: Instance Stop Callback + type: nfv_vim_instance_stop_callback + file: _instance_task_work.py + regex: "Stop-Instance callback for (.*), response={'completed': (.*), + 'reason': (.*)}." + fields: + - instance_name + - completed + - reason + + - name: Instance Pause Callback + type: nfv_vim_instance_pause_callback + file: _instance_task_work.py + regex: "Pause-Instance callback for (.*), response={'completed': (.*), + 'reason': (.*)}." + fields: + - instance_name + - completed + - reason + + - name: Instance Unpause Callback + type: nfv_vim_instance_unpause_callback + file: _instance_task_work.py + regex: "Unpause-Instance callback for (.*), response={'completed': (.*), + 'reason': (.*)}." + fields: + - instance_name + - completed + - reason + + - name: Instance Suspend Callback + type: nfv_vim_instance_suspend_callback + file: _instance_task_work.py + regex: "Suspend-Instance callback for (.*), response={'completed': (.*), + 'reason': (.*)}." + fields: + - instance_name + - completed + - reason + + - name: Instance Resume Callback + type: nfv_vim_instance_resume_callback + file: _instance_task_work.py + regex: "Resume-Instance callback for (.*), response={'completed': (.*), + 'reason': (.*)}." + fields: + - instance_name + - completed + - reason + + - name: Instance Reboot Callback + type: nfv_vim_instance_reboot_callback + file: _instance_task_work.py + regex: "Reboot-Instance callback for (.*), response={'completed': (.*), + 'reason': (.*)}." + fields: + - instance_name + - completed + - reason + + - name: Instance Rebuild Callback + type: nfv_vim_instance_rebuild_callback + file: _instance_task_work.py + regex: "Rebuild-Instance callback for (.*), response={'completed': (.*), + 'reason': (.*)}." + fields: + - instance_name + - completed + - reason + + - name: Instance Live-Migrate Callback + type: nfv_vim_instance_live_migrate_callback + file: _instance_task_work.py + regex: "Live-Migrate-Instance callback for (.*), response={'completed': (.*), + 'reason': (.*)}." + fields: + - instance_name + - completed + - reason + + - name: Instance Cold-Migrate Callback + type: nfv_vim_instance_cold_migrate_callback + file: _instance_task_work.py + regex: "Cold-Migrate-Instance callback for (.*), response={'completed': (.*), + 'reason': (.*)}." + fields: + - instance_name + - completed + - reason + + - name: Instance Cold-Migrate Confirm Callback + type: nfv_vim_instance_cold_migrate_confirm_callback + file: _instance_task_work.py + regex: "Cold-Migrate-Confirm-Instance callback for (.*), + response={'completed': (.*), 'reason': (.*)}." + fields: + - instance_name + - completed + - reason + + - name: Instance Cold-Migrate Revert Callback + type: nfv_vim_instance_cold_migrate_revert_callback + file: _instance_task_work.py + regex: "Cold-Migrate-Revert-Instance callback for (.*), + response={'completed': (.*), 'reason': (.*)}." + fields: + - instance_name + - completed + - reason + + - name: Instance Resize Callback + type: nfv_vim_instance_resize_callback + file: _instance_task_work.py + regex: "Resize-Instance callback for (.*), response={'completed': (.*), + 'reason': (.*)}." + fields: + - instance_name + - completed + - reason + + - name: Instance Resize Confirm Callback + type: nfv_vim_instance_resize_confirm_callback + file: _instance_task_work.py + regex: "Resize-Confirm-Instance callback for (.*), + response={'completed': (.*), 'reason': (.*)}." + fields: + - instance_name + - completed + - reason + + - name: Instance Resize Revert Callback + type: nfv_vim_instance_resize_revert_callback + file: _instance_task_work.py + regex: "Resize-Revert-Instance callback for (.*), response={'completed': (.*), + 'reason': (.*)}." + fields: + - instance_name + - completed + - reason + + - name: Instance Guest Services Notify + type: nfv_vim_instance_guest_services_notify + file: _vim_nfvi_events.py + regex: "Guest-Services state notify, instance_uuid=(.*), host_name=(.*) + guest_services=\\[{'admin_state': '(.*)', 'restart_timeout': (.*), + 'oper_state': '(.*)', 'name': '(.*)'}]." + fields: + - instance_uuid + - host_name + - service_admin_state + - restart_timeout + - service_oper_state + - service_name + + - name: Instance Guest Services Enable + type: nfv_vim_instance_guest_services_enable + file: _instance_task_work.py + regex: "Guest-Services-Enable for (.*), nfvi_guest_services=\\[{'admin_state': + '(.*)', 'service': '(.*)'}]." + fields: + - instance_name + - service_admin_state + - service_name + + - name: Instance Guest Services Enable Callback + type: nfv_vim_instance_guest_services_enable_callback + file: _instance_task_work.py + regex: "Guest-Services-Enable callback for (.*), response={'completed': (.*), + 'reason': (.*), 'result-data': {'instance_uuid': u'(.*)', 'services': + \\[{'admin_state': '(.*)', 'restart_timeout': (.*), 'oper_state': '(.*)', + 'name': '(.*)'}], 'host_name': u'(.*)'}}." + fields: + - instance_name + - completed + - reason + - instance_uuid + - service_admin_state + - restart_timeout + - service_oper_state + - service_name + - host_name + + - name: Instance Guest Services Disable + type: nfv_vim_instance_guest_services_disable + file: _instance_task_work.py + regex: "Guest-Services-Disable for (.*), nfvi_guest_services=\\[{'admin_state': + '(.*)', 'service': '(.*)'}]." + fields: + - instance_name + - service_admin_state + - service_name + + - name: Instance Guest Services Disable Callback + type: nfv_vim_instance_guest_services_disable_callback + file: _instance_task_work.py + regex: "Guest-Services-Disable callback for (.*), response={'completed': (.*), + 'reason': (.*), 'result-data': {'instance_uuid': u'(.*)', 'services': + \\[{'admin_state': '(.*)', 'restart_timeout': (.*), 'oper_state': '(.*)', + 'name': '(.*)'}], 'host_name': u'(.*)'}}." + fields: + - instance_name + - completed + - reason + - instance_uuid + - service_admin_state + - restart_timeout + - service_oper_state + - service_name + - host_name + + - name: Instance Guest Services Vote + type: nfv_vim_instance_guest_services_vote + file: _instance_task_work.py + regex: "Guest-Services-Vote for (.*), action_type=(.*)." + fields: + - instance_name + - action_type + + - name: Instance Guest Services Vote Result + type: nfv_vim_instance_guest_services_vote_result + file: _instance_task_work.py + regex: "Guest-Services-Vote for (.*), vote=(.*)." + fields: + - instance_name + - vote_result + + - name: Instance Guest Services Vote Callback + type: nfv_vim_instance_guest_services_vote_callback + file: _instance_task_work.py + regex: "Guest-Services-Vote callback for (.*), response={'action-type': u'(.*)', + 'uuid': u'(.*)', 'completed': (.*), 'reason': (.*), 'host-name': u'(.*)', + 'timeout': (.*)}." + fields: + - instance_name + - action_type + - instance_uuid + - completed + - reason + - host_name + - timeout + + - name: Instance Guest Services Pre-Notify + type: nfv_vim_instance_guest_services_pre_notify + file: _instance_task_work.py + regex: "Guest-Services-Pre-Notify for (.*), action_type=(.*)." + fields: + - instance_name + - action_type + + - name: Instance Guest Services Pre-Notify Callback + type: nfv_vim_instance_guest_services_pre_notify_callback + file: _instance_task_work.py + regex: "Guest-Services-Pre-Notify callback for (.*), + response={'action-type': u'(.*)', 'uuid': u'(.*)', 'completed': (.*), + 'reason': (.*), 'host-name': u'(.*)', 'timeout': (.*)}." + fields: + - instance_name + - action_type + - instance_uuid + - completed + - reason + - host_name + - timeout + + - name: Instance Guest Services Post-Notify + type: nfv_vim_instance_guest_services_post_notify + file: _instance_task_work.py + regex: "Guest-Services-Post-Notify for (.*), action_type=(.*)." + fields: + - instance_name + - action_type + + - name: Instance Guest Services Post-Notify Callback + type: nfv_vim_instance_guest_services_post_notify_callback + file: _instance_task_work.py + regex: "Guest-Services-Post-Notify callback for (.*), + response={'action-type': u'(.*)', 'uuid': u'(.*)', 'completed': (.*), + 'reason': (.*), 'host-name': u'(.*)', 'timeout': (.*)}." + fields: + - instance_name + - action_type + - instance_uuid + - completed + - reason + - host_name + - timeout + + - name: Instance Guest Services Post-Notify Heartbeat + type: nfv_vim_instance_guest_services_post_notify_heartbeat + file: _instance_task_work.py + regex: "Guest-Services-Post-Notify for (.*), guest communication + re-established." + fields: + - instance_name diff --git a/nfv/nfv-common/nfv_common/helpers.py b/nfv/nfv-common/nfv_common/helpers.py new file mode 100755 index 00000000..f85e49e9 --- /dev/null +++ b/nfv/nfv-common/nfv_common/helpers.py @@ -0,0 +1,116 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import socket +import select +import errno +import functools + + +def syscall_retry_on_interrupt(func, *args): + """ Attempt system call again if interrupted by EINTR """ + for _ in range(0, 5): + try: + return func(*args) + except (OSError, socket.error, select.error) as e: + if errno.EINTR != e.args[0]: + raise + + +def local_uptime_in_secs(): + try: + with open('/proc/uptime', 'r') as f: + uptime_secs = int(float(f.readline().split()[0])) + except IOError: + uptime_secs = 0 + return uptime_secs + + +_process_start_time = local_uptime_in_secs() + + +def process_uptime_in_secs(): + return local_uptime_in_secs() - _process_start_time + + +class Object(object): + """ + Class Object Type Definition + """ + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + def as_dict(self): + return self.__dict__ + + +class Result(object): + """ + Generic Result Object Type Definition + """ + def __init__(self, result_data, ancillary_data=None): + self.result_data = result_data + self.ancillary_data = ancillary_data + + def __str__(self): + return("Result: result-data: %s ancillary-data: %s" + % (self.result_data, self.ancillary_data)) + + +class Constants(object): + def __iter__(self): + for attr in dir(self): + if not callable(attr) and not attr.startswith("__"): + value = getattr(self, attr) + yield value + + +class Constant(object): + """ + Constant Type Definition + """ + def __init__(self, value): + self.value = value + + def __get__(self, obj, obj_type): + return self.value + + def __set__(self, obj, value): + raise AttributeError("ERROR: attempting to set a constant.") + + def __delete__(self, obj): + raise AttributeError("ERROR: attempting to delete a constant.") + + +class Singleton(type): + """ + Singleton Type Definition + """ + _instances = {} + + def __call__(cls, *args, **kwargs): + if cls not in cls._instances: + cls._instances[cls] = \ + super(Singleton, cls).__call__(*args, **kwargs) + return cls._instances[cls] + + +def coroutine(func): + """ + Co-Routine decorator that wraps a function and starts the co-routine + """ + def start(*args, **kwargs): + target = func(*args, **kwargs) + target.send(None) + functools.update_wrapper(start, func) + return target + return start + + +def get_local_host_name(): + """ + Returns the name of the local host + """ + return socket.gethostname() diff --git a/nfv/nfv-common/nfv_common/histogram.py b/nfv/nfv-common/nfv_common/histogram.py new file mode 100755 index 00000000..07e3ab90 --- /dev/null +++ b/nfv/nfv-common/nfv_common/histogram.py @@ -0,0 +1,187 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import math +import array +import datetime + +from nfv_common import debug + +DLOG = debug.debug_get_logger('nfv_common.histogram') + + +class Histogram(object): + """ + Histogram Object + """ + def __init__(self, name, num_buckets, units): + self._name = name + self._units = units + self._num_buckets = num_buckets + self._created_date = datetime.datetime.now() + self._reset_date = self._created_date + self._sample_total = 0 + self._num_samples = 0 + self._average_sample = None + self._max_sample = -1 + self._max_sample_date = None + self._buckets = array.array("L", [0] * num_buckets) + + @property + def name(self): + """ + Returns the name of the histogram + """ + return self._name + + def add_data(self, sample): + """ + Convert data given to the nearest power of two. + """ + sample_as_int = int(sample) + if 0 == sample_as_int: + bucket_idx = sample_as_int.bit_length() + else: + bucket_idx = (sample_as_int-1).bit_length() + + if bucket_idx > self._num_buckets: + bucket_idx = self._num_buckets-1 + + if sample_as_int > self._max_sample: + self._max_sample = sample_as_int + self._max_sample_date = datetime.datetime.now() + + self._sample_total += sample_as_int + self._num_samples += 1 + self._average_sample = (self._sample_total / self._num_samples) + + self._buckets[bucket_idx] += 1 + + def reset_data(self): + """ + Clear out the collected samples. + """ + self._reset_date = datetime.datetime.now() + self._sample_total = 0 + self._num_samples = 0 + self._average_sample = None + self._max_sample = -1 + self._max_sample_date = None + for idx, _ in enumerate(self._buckets): + self._buckets[idx] = 0 + + @staticmethod + def _scale_sample(scale_min, scale_max, sample_min, sample_max, sample): + """ + Normalize sample to be compared with other samples + """ + if scale_min == scale_max: + return sample + + if sample_min == sample_max: + return sample + + return ((((scale_max - scale_min) * (sample - sample_min)) / + (sample_max - sample_min)) + sample_max) + + def display_data(self, pretty_format=True): + """ + Output the histogram to a log. + """ + date_str = "" + values_str = "" + + if pretty_format: + date_str += " created-date: %s" % self._created_date + if self._reset_date is not None: + date_str += " reset-date: %s" % self._reset_date + + values_str += " total: %s" % self._num_samples + + if self._average_sample is not None: + values_str += " avg: %s" % self._average_sample + + if self._max_sample_date is not None: + values_str += (" max: %s (%s)" % (self._max_sample, + self._max_sample_date)) + + DLOG.info("%s" % '-' * 120) + + DLOG.info("Histogram: %s" % self._name) + + if "" != date_str: + DLOG.info("%s" % date_str) + + if "" != values_str: + DLOG.info(" %s" % values_str) + + for idx, bucket_value in enumerate(self._buckets): + if 0 != bucket_value: + if pretty_format: + scaled_bucket_value \ + = self._scale_sample(0, 60, 0, self._max_sample, + bucket_value) + + DLOG.info(" %03i [up to %03i %s]: %07i %s" + % (idx, math.pow(2, idx), self._units, bucket_value, + '*' * min(60, scaled_bucket_value))) + else: + DLOG.info(" %03i [up to %03i %s]: %07i" + % (idx, math.pow(2, idx), self._units, bucket_value)) + + if pretty_format: + DLOG.info("%s" % '-' * 120) + + +_histograms = dict() + + +def _find_histogram(name): + """ + Lookup a histogram with a particular name + """ + if name in _histograms: + return _histograms[name] + return None + + +def add_histogram_data(name, sample, units): + """ + Add a sample to a histogram + """ + global _histograms + + histogram = _find_histogram(name) + if histogram is None: + histogram = Histogram(name, 16, units) + _histograms[name] = histogram + + histogram.add_data(sample) + + +def reset_histogram_data(name=None): + """ + Reset histogram data + """ + if name is None: + for histogram in _histograms.itervalues(): + histogram.reset_data() + else: + histogram = _find_histogram(name) + if histogram is not None: + histogram.reset_data() + + +def display_histogram_data(name=None, pretty_format=True): + """ + Display histogram data captured + """ + if name is None: + for histogram in _histograms.itervalues(): + histogram.display_data(pretty_format) + else: + histogram = _find_histogram(name) + if histogram is not None: + histogram.display_data(pretty_format) diff --git a/nfv/nfv-common/nfv_common/profiler.py b/nfv/nfv-common/nfv_common/profiler.py new file mode 100755 index 00000000..a9a8b014 --- /dev/null +++ b/nfv/nfv-common/nfv_common/profiler.py @@ -0,0 +1,63 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +from nfv_common import debug + +DLOG = debug.debug_get_logger('nfv_common.profiler') + +try: + import objgraph + import guppy + memory_profiling = guppy.hpy() + +except ImportError: + memory_profiling = None + + +def profile_memory_references(obj_type, obj_name): + """ + Graph memory references + """ + if memory_profiling is not None: + objs = objgraph.by_type(obj_type) + objgraph.show_backrefs(objs, max_depth=5, + filename='/tmp/%s_memory_back_refs.dot' % obj_name) + objgraph.show_refs(objs, max_depth=5, + filename='/tmp/%s_memory_refs.dot' % obj_name) + + +def profile_memory_set_reference(): + """ + Set the memory usage reference + """ + if memory_profiling is not None: + memory_profiling.setref() + + +def profile_memory_dump(): + """ + Dumps the current memory usage + """ + if memory_profiling is not None: + DLOG.info("%s" % '-' * 120) + DLOG.info("Memory Profile: %s" % memory_profiling.heap()) + profile_memory_references('nfv_vim.objects._instance.Instance', 'instance') + DLOG.info("%s" % '-' * 120) + + +def profiler_initialize(): + """ + Profiler - Initialize + """ + if memory_profiling is not None: + DLOG.info("Memory Profiling Enabled") + memory_profiling.setref() + + +def profiler_finalize(): + """ + Profiler - Finalize + """ + pass diff --git a/nfv/nfv-common/nfv_common/schedule/__init__.py b/nfv/nfv-common/nfv_common/schedule/__init__.py new file mode 100755 index 00000000..d7f84472 --- /dev/null +++ b/nfv/nfv-common/nfv_common/schedule/__init__.py @@ -0,0 +1,9 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# flake8: noqa +# +from _schedule_module import schedule_function_call +from _schedule_module import schedule_initialize, schedule_finalize diff --git a/nfv/nfv-common/nfv_common/schedule/_schedule_module.py b/nfv/nfv-common/nfv_common/schedule/_schedule_module.py new file mode 100755 index 00000000..00038549 --- /dev/null +++ b/nfv/nfv-common/nfv_common/schedule/_schedule_module.py @@ -0,0 +1,70 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import socket + +from nfv_common import selobj +from nfv_common.helpers import coroutine + +_send_socket = None +_receive_socket = None +_pending_function_calls = list() + + +def schedule_function_call(func, *args, **kwargs): + """ + Schedule a function call to be performed at a later time + """ + global _send_socket, _pending_function_calls + + function_data = (func, args, kwargs) + _pending_function_calls.append(function_data) + _send_socket.send('1') + + +@coroutine +def _schedule_dispatch(): + global _receive_socket, _pending_function_calls + + while True: + select_obj = (yield) + if select_obj == _receive_socket.fileno(): + _receive_socket.recv(1) + + for func, args, kwargs in _pending_function_calls: + func(*args, **kwargs) + + _pending_function_calls[:] = list() + + +def schedule_initialize(): + """ + Initialize the schedule module + """ + global _send_socket, _receive_socket, _pending_function_calls + + _send_socket, _receive_socket = socket.socketpair() + _receive_socket.setblocking(False) + selobj.selobj_add_read_obj(_receive_socket.fileno(), _schedule_dispatch) + + del _pending_function_calls + _pending_function_calls = list() # flake8: noqa + + +def schedule_finalize(): + """ + Finalize the schedule module + """ + global _send_socket, _receive_socket, _pending_function_calls + + if _send_socket is not None: + _send_socket.close() + + if _receive_socket is not None: + selobj.selobj_del_read_obj(_receive_socket.fileno()) + _receive_socket.close() + + del _pending_function_calls + _pending_function_calls = list() # flake8: noqa diff --git a/nfv/nfv-common/nfv_common/selectable.py b/nfv/nfv-common/nfv_common/selectable.py new file mode 100755 index 00000000..f01296c4 --- /dev/null +++ b/nfv/nfv-common/nfv_common/selectable.py @@ -0,0 +1,59 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import socket +import multiprocessing +import Queue as threading_queue + + +class ThreadQueue(object): + def __init__(self, queue_id): + self._queue_id = queue_id + self._send_socket, self._receive_socket = socket.socketpair() + self._receive_socket.setblocking(False) + self._message_queue = threading_queue.Queue() + + @property + def selobj(self): + return self._receive_socket.fileno() + + def put(self, message): + self._message_queue.put(message) + self._send_socket.send(self._queue_id) + + def get_nowait(self): + self._receive_socket.recv(1) + try: + return self._message_queue.get_nowait() + + except threading_queue.Empty: + return None + + +class MultiprocessQueue(object): + def __init__(self): + self._queue = multiprocessing.Queue() + + @property + def selobj(self): + return self._queue._reader + + def put(self, data): + self._queue.put(data) + + def get(self): + try: + entry = self._queue.get_nowait() + return entry + + except threading_queue.Empty: + return None + + def close(self): + self._queue.close() + if self._queue._writer is not None: + # Fix memory leak with pipes in the multiprocessing.queue module + self._queue._writer.close() + self._queue.join_thread() diff --git a/nfv/nfv-common/nfv_common/selobj/__init__.py b/nfv/nfv-common/nfv_common/selobj/__init__.py new file mode 100755 index 00000000..074c19fc --- /dev/null +++ b/nfv/nfv-common/nfv_common/selobj/__init__.py @@ -0,0 +1,11 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# flake8: noqa +# +from _selobj_module import selobj_add_read_obj, selobj_del_read_obj +from _selobj_module import selobj_add_write_obj, selobj_del_write_obj +from _selobj_module import selobj_add_error_callback, selobj_del_error_callback +from _selobj_module import selobj_dispatch, selobj_initialize, selobj_finalize diff --git a/nfv/nfv-common/nfv_common/selobj/_selobj_module.py b/nfv/nfv-common/nfv_common/selobj/_selobj_module.py new file mode 100755 index 00000000..9d83540f --- /dev/null +++ b/nfv/nfv-common/nfv_common/selobj/_selobj_module.py @@ -0,0 +1,168 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import socket +import select +import errno + +from nfv_common import debug + +DLOG = debug.debug_get_logger('nfv_common.selobj') + +_read_callbacks = dict() +_write_callbacks = dict() +_error_callbacks = dict() + + +def selobj_add_read_obj(selobj, callback, *callback_args, **callback_kwargs): + """ + Add read selection object, callback is a co-routine that is + sent the selection object that has become readable + """ + global _read_callbacks + + coroutine = callback(*callback_args, **callback_kwargs) + _read_callbacks[selobj] = coroutine + + +def selobj_del_read_obj(selobj): + """ + Delete read selection object + """ + global _read_callbacks + + if selobj in _read_callbacks.keys(): + _read_callbacks.pop(selobj) + + +def selobj_add_write_obj(selobj, callback, *callback_args, **callback_kwargs): + """ + Add write selection object, callback is a co-routine that is + sent the selection object that has become writeable + """ + global _write_callbacks + + coroutine = callback(*callback_args, **callback_kwargs) + _write_callbacks[selobj] = coroutine + + +def selobj_del_write_obj(selobj): + """ + Delete write selection object + """ + global _write_callbacks + + if selobj in _write_callbacks.keys(): + _write_callbacks.pop(selobj) + + +def selobj_add_error_callback(selobj, callback, *callback_args, + **callback_kwargs): + """ + Add selection object error callback which is a co-routine that is + called when the selection object is in error + """ + global _error_callbacks + + coroutine = callback(*callback_args, **callback_kwargs) + _error_callbacks[selobj] = coroutine + + +def selobj_del_error_callback(selobj): + """ + Delete selection object error callback + """ + global _error_callbacks + + if selobj in _error_callbacks.keys(): + _error_callbacks.pop(selobj) + + +def selobj_dispatch(timeout_in_ms): + """ + Dispatch selection objects that have become readable or writeable + within the given time period + """ + from nfv_common import timers, histogram + + global _read_callbacks, _write_callbacks, _error_callbacks + + read_objs = _read_callbacks.keys() + write_objs = _write_callbacks.keys() + + try: + readable, writeable, in_error = select.select(read_objs, write_objs, [], + timeout_in_ms/1000.0) + + for selobj in readable: + callback = _read_callbacks.get(selobj, None) + if callback is not None: + start_ms = timers.get_monotonic_timestamp_in_ms() + try: + callback.send(selobj) + except StopIteration: + _read_callbacks.pop(selobj) + elapsed_ms = timers.get_monotonic_timestamp_in_ms() - start_ms + histogram.add_histogram_data("selobj read: " + callback.__name__, + elapsed_ms / 100, "decisecond") + + for selobj in writeable: + callback = _write_callbacks.get(selobj, None) + if callback is not None: + start_ms = timers.get_monotonic_timestamp_in_ms() + try: + callback.send(selobj) + except StopIteration: + _write_callbacks.pop(selobj) + elapsed_ms = timers.get_monotonic_timestamp_in_ms() - start_ms + histogram.add_histogram_data("selobj write: " + callback.__name__, + elapsed_ms / 100, "decisecond") + + for selobj in in_error: + callback = _error_callbacks.get(selobj, None) + if callback is not None: + start_ms = timers.get_monotonic_timestamp_in_ms() + try: + callback.send(selobj) + except StopIteration: + _error_callbacks.pop(selobj) + elapsed_ms = timers.get_monotonic_timestamp_in_ms() - start_ms + histogram.add_histogram_data("selobj error: " + callback.__name__, + elapsed_ms / 100, "decisecond") + + if selobj in _read_callbacks.keys(): + _read_callbacks.pop(selobj) + + if selobj in _write_callbacks.keys(): + _write_callbacks.pop(selobj) + + except (OSError, socket.error, select.error) as e: + if errno.EINTR == e.args[0]: + pass + + +def selobj_initialize(): + """ + Initialize the selection object module + """ + global _read_callbacks, _write_callbacks + + del _read_callbacks + _read_callbacks = dict() # flake8: noqa + + del _write_callbacks + _write_callbacks = dict() # flake8: noqa + + +def selobj_finalize(): + """ + Finalize the selection object module + """ + global _read_callbacks, _write_callbacks + + del _read_callbacks + _read_callbacks = dict() # flake8: noqa + del _write_callbacks + _write_callbacks = dict() # flake8: noqa diff --git a/nfv/nfv-common/nfv_common/state_machine/__init__.py b/nfv/nfv-common/nfv_common/state_machine/__init__.py new file mode 100755 index 00000000..b44ff531 --- /dev/null +++ b/nfv/nfv-common/nfv_common/state_machine/__init__.py @@ -0,0 +1,14 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# flake8: noqa +# +from _state import State +from _state_task import StateTask +from _state_task_work import StateTaskWork +from _state_task_result import STATE_TASK_RESULT, state_task_result_update +from _state_task_work_result import STATE_TASK_WORK_RESULT +from _state_exception import StateException +from _state_machine import StateMachine diff --git a/nfv/nfv-common/nfv_common/state_machine/_state.py b/nfv/nfv-common/nfv_common/state_machine/_state.py new file mode 100755 index 00000000..dc59a410 --- /dev/null +++ b/nfv/nfv-common/nfv_common/state_machine/_state.py @@ -0,0 +1,56 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +from nfv_common import debug + +DLOG = debug.debug_get_logger('nfv_common.state_machine.state') + + +class State(object): + """ + State Object + """ + def __init__(self, name): + """ + Create State + """ + self._name = name + + @property + def name(self): + """ + Returns the name of the state + """ + return self._name + + def __str__(self): + """ + Returns the name of the state + """ + return self._name + + def enter(self, context, *context_args, **context_kwargs): + """ + Called by the State Machine to enter this state + """ + raise NotImplementedError + + def exit(self, context): + """ + Called by the State Machine to exit this state + """ + raise NotImplementedError + + def transition(self, context, event, event_data, to_state): + """ + Called by the State Machine to transition from this state + """ + raise NotImplementedError + + def handle_event(self, context, event, event_data=None): + """ + Called by the State Machine to handle an event + """ + raise NotImplementedError diff --git a/nfv/nfv-common/nfv_common/state_machine/_state_exception.py b/nfv/nfv-common/nfv_common/state_machine/_state_exception.py new file mode 100755 index 00000000..34627ae1 --- /dev/null +++ b/nfv/nfv-common/nfv_common/state_machine/_state_exception.py @@ -0,0 +1,47 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +from nfv_common import debug + +DLOG = debug.debug_get_logger('nfv_common.state_machine.state_exception') + + +class StateException(Exception): + """ + State Exception + """ + def __init__(self, message, reason): + """ + Create a State exception + """ + super(StateException, self).__init__(message, reason) + self._reason = reason # a message string or another exception + self._message = message + + def __str__(self): + """ + Return a string representing the exception + """ + return "[State Exception:reason=%s]" % self._reason + + def __repr__(self): + """ + Provide a representation of the exception + """ + return str(self) + + @property + def message(self): + """ + Returns the message for the exception + """ + return self._message + + @property + def reason(self): + """ + Returns the reason for the exception + """ + return self._reason diff --git a/nfv/nfv-common/nfv_common/state_machine/_state_machine.py b/nfv/nfv-common/nfv_common/state_machine/_state_machine.py new file mode 100755 index 00000000..75bc0f04 --- /dev/null +++ b/nfv/nfv-common/nfv_common/state_machine/_state_machine.py @@ -0,0 +1,155 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import weakref + +from nfv_common import debug + +from _state_exception import StateException + +DLOG = debug.debug_get_logger('nfv_common.state_machine.state_machine') + + +class StateMachine(object): + """ + State Machine Object + """ + def __init__(self, context, context_args, context_kwargs, initial_state, + states): + """ + Create State Machine + """ + if context_args is None: + context_args = tuple() + + if context_kwargs is None: + context_kwargs = dict() + + self._context_reference = weakref.ref(context) + self._context_args = context_args + self._context_kwargs = context_kwargs + self._states = states + self._current_state = initial_state + self._state_change_callbacks = list() + self._transitioning = False + self._event_backlog_state = None + self._event_backlog = list() + + @property + def _context(self): + """ + Returns the context + """ + context = self._context_reference() + return context + + @property + def current_state(self): + """ + Returns the current state + """ + return self._current_state + + def register_state_change_callback(self, callback): + """ + Register state change callback + """ + if callback not in self._state_change_callbacks: + self._state_change_callbacks.append(callback) + + def handle_event(self, event, event_data=None): + """ + Handle event + """ + if self._transitioning: + self._event_backlog.append((self._event_backlog_state, event, + event_data)) + return + + try: + prev_state = self._current_state + next_state_name = self._current_state.handle_event( + self._context, event, event_data) + next_state = self._states[next_state_name] + + if prev_state != self._current_state: + # Nested handle event calls, we have already moved away + DLOG.verbose("Nested handle event calls detected, " + "prev_state=%s, current_state=%s." + % (prev_state.name, self.current_state.name)) + return + + if next_state_name != prev_state.name: + # Attempt to exit the current state + try: + prev_state.exit(self._context) + + except StateException as e: + DLOG.error("Caught exception while trying to exit state " + "(%s), event=%s, error=%s." + % (prev_state, event, e)) + return + + # Attempt to transition from the current state + try: + prev_state.transition(self._context, event, event_data, + next_state) + + except StateException as e: + DLOG.error("Caught exception while trying to transition " + "from state (%s) to state (%s), event=%s, " + "error=%s." % (prev_state, next_state, event, e)) + prev_state.enter(self._context, *self._context_args, + **self._context_kwargs) + return + + # Attempt to enter the next state + try: + self._transitioning = True + self._event_backlog_state = next_state + next_state.enter(self._context, *self._context_args, + **self._context_kwargs) + self._current_state = next_state + + for callback in self._state_change_callbacks: + callback(prev_state, next_state, event) + + event_backlog = list(self._event_backlog) + + self._transitioning = False + self._event_backlog_state = None + del self._event_backlog[:] + + for event_state, event, event_data in event_backlog: + if event_state != self._current_state: + DLOG.info("Ignoring event %s, no longer in state " + "%s, now in state %s." + % (event, event_state, + self._current_state)) + else: + DLOG.info("Handling event backlog, event=%s while " + "transitioning to state %s." + % (event, self._current_state)) + self.handle_event(event, event_data) + + del event_backlog[:] + + except StateException as e: + DLOG.error("Caught exception while trying to enter state " + "(%s) from state (%s), event=%s, error=%s." + % (next_state, prev_state, event, e)) + self._transitioning = False + self._event_backlog_state = None + del self._event_backlog[:] + prev_state.transition(self._context, event, event_data, + prev_state) + prev_state.enter(self._context, *self._context_args, + **self._context_kwargs) + return + + except StateException as e: + DLOG.error("Caught exception while trying to handle event (%s), " + "error=%s." % (event, e)) + return diff --git a/nfv/nfv-common/nfv_common/state_machine/_state_task.py b/nfv/nfv-common/nfv_common/state_machine/_state_task.py new file mode 100755 index 00000000..78643f49 --- /dev/null +++ b/nfv/nfv-common/nfv_common/state_machine/_state_task.py @@ -0,0 +1,304 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +from nfv_common import timers +from nfv_common import debug +from nfv_common.helpers import coroutine + +from _state_task_result import STATE_TASK_RESULT, state_task_result_update +from _state_task_work_result import STATE_TASK_WORK_RESULT + +DLOG = debug.debug_get_logger('nfv_common.state_machine.state_task') + + +class StateTask(object): + """ + State Task + """ + def __init__(self, name, task_work_list): + self._name = name + self._current_task_work = 0 + self._task_work_timer_id = None + self._task_work_list = task_work_list + self._task_result = STATE_TASK_RESULT.SUCCESS + self._task_result_reason = '' + self._timer_id = None + self._timeout_in_secs = 0 + for task_work in task_work_list: + self._timeout_in_secs += task_work.timeout_in_secs + if 0 < self._timeout_in_secs: + self._timeout_in_secs += 1 + self._task_inprogress = False + + def __del__(self): + self._cleanup() + + @property + def name(self): + """ + Returns the name of the task + """ + return self._name + + def _cleanup(self): + """ + State Task Cleanup + """ + if self._timer_id is not None: + timers.timers_delete_timer(self._timer_id) + self._timer_id = None + + if self._task_work_timer_id is not None: + timers.timers_delete_timer(self._task_work_timer_id) + self._task_work_timer_id = None + + def _abort(self): + """ + State Task Internal Abort + """ + DLOG.debug("Task (%s) abort." % self._name) + + if self._task_inprogress and 0 < len(self._task_work_list): + for idx in range(self._current_task_work, -1, -1): + task_work = self._task_work_list[idx] + task_work.abort() + DLOG.debug("Task (%s) aborted work (%s)." + % (self._name, task_work.name)) + self._current_task_work = 0 + self._task_inprogress = False + self._cleanup() + + def abort(self): + """ + State Task Abort + """ + self._task_result = STATE_TASK_RESULT.ABORTED + self._task_result_reason = 'aborted' + self._abort() + + def start(self): + """ + State Task Start + """ + self._cleanup() + self._current_task_work = 0 + self._task_inprogress = True + self._task_result = STATE_TASK_RESULT.SUCCESS + self._task_result_reason = 'success' + self._timer_id = timers.timers_create_timer(self._name, + self._timeout_in_secs, + self._timeout_in_secs, + self._timeout) + self._run() + + def refresh_timeouts(self): + """ + State Task Refresh Timeouts + """ + if self._timer_id is None: + # No need to refresh task timer, task not started + return + + timers.timers_delete_timer(self._timer_id) + self._timer_id = None + + # Calculate overall task timeout + self._timeout_in_secs = 0 + for task_work in self._task_work_list: + self._timeout_in_secs += task_work.timeout_in_secs + if 0 < self._timeout_in_secs: + self._timeout_in_secs += 1 + + # Re-start task timer + self._timer_id = timers.timers_create_timer(self._name, + self._timeout_in_secs, + self._timeout_in_secs, + self._timeout) + + if self._task_work_timer_id is None: + # No need to refresh task work timer, no task work running + return + + timers.timers_delete_timer(self._task_work_timer_id) + self._task_work_timer_id = None + + if len(self._task_work_list) <= self._current_task_work: + # No need to refresh task work timer, no current task work running + return + + # Re-start task work timer + task_work = self._task_work_list[self._current_task_work] + if 0 < task_work.timeout_in_secs: + self._task_work_timer_id = timers.timers_create_timer( + task_work.name, task_work.timeout_in_secs, + task_work.timeout_in_secs, self._task_work_timeout) + + def _run(self): + """ + State Task Run + """ + if not self._task_inprogress: + DLOG.debug("Task (%s) not inprogress." % self._name) + return + + for idx in range(self._current_task_work, len(self._task_work_list), 1): + task_work = self._task_work_list[idx] + if self._task_work_timer_id is not None: + timers.timers_delete_timer(self._task_work_timer_id) + self._task_work_timer_id = None + + DLOG.debug("Task %s running %s work." % (self._name, + task_work.name)) + + task_work_result, task_work_result_reason = task_work.run() + self._current_task_work = idx + + if STATE_TASK_WORK_RESULT.WAIT == task_work_result: + if 0 < task_work.timeout_in_secs: + self._task_work_timer_id = timers.timers_create_timer( + task_work.name, task_work.timeout_in_secs, + task_work.timeout_in_secs, self._task_work_timeout) + + DLOG.debug("Task (%s) is waiting for work (%s) to complete, " + "timeout_in_secs=%s." % (self._name, task_work.name, + task_work.timeout_in_secs)) + break + else: + self._task_result, self._task_result_reason = \ + state_task_result_update( + self._task_result, self._task_result_reason, + task_work_result, task_work_result_reason) + + if STATE_TASK_RESULT.FAILED == self._task_result \ + or STATE_TASK_RESULT.ABORTED == self._task_result \ + or STATE_TASK_RESULT.TIMED_OUT == self._task_result: + self._abort() + self._complete(self._task_result, self._task_result_reason) + break + else: + DLOG.debug("Task (%s) done running." % self._name) + self._task_inprogress = False + self._cleanup() + self._complete(self._task_result, self._task_result_reason) + + def inprogress(self): + """ + Returns if the task is inprogress or not + """ + return self._task_inprogress + + def is_failed(self): + """ + Return true if this task is failed + """ + return STATE_TASK_RESULT.FAILED == self._task_result + + def timed_out(self): + """ + Return true if this task has timed out + """ + return STATE_TASK_RESULT.TIMED_OUT == self._task_result + + def aborted(self): + """ + Return true if this task was aborted + """ + return STATE_TASK_RESULT.ABORTED == self._task_result + + @coroutine + def _timeout(self): + """ + State Task Timeout + """ + (yield) + DLOG.debug("Task (%s) timed out, timeout_in_secs=%s." + % (self._name, self._timeout_in_secs)) + self._abort() + self._task_result = STATE_TASK_RESULT.TIMED_OUT + self._task_result_reason = 'timeout' + self._complete(self._task_result, self._task_result_reason) + + def task_work_complete(self, task_work_result, task_work_result_reason=None): + """ + State Task Work Complete + """ + task_work = self._task_work_list[self._current_task_work] + DLOG.debug("Task (%s) work (%s) complete, result=%s, reason=%s." + % (self._name, task_work.name, task_work_result, + task_work_result_reason)) + + updated_task_work_result, updated_task_work_result_reason = \ + task_work.complete(task_work_result, task_work_result_reason) + + if task_work_result != updated_task_work_result: + DLOG.debug("Task (%s) work (%s) complete, result updated, " + "was_result=%s, now_result=%s." + % (self._name, task_work.name, task_work_result, + updated_task_work_result)) + task_work_result = updated_task_work_result + task_work_result_reason = updated_task_work_result_reason + + self._task_result, self._task_result_reason = \ + state_task_result_update(self._task_result, self._task_result_reason, + task_work_result, task_work_result_reason) + + if STATE_TASK_RESULT.FAILED == self._task_result \ + or STATE_TASK_RESULT.ABORTED == self._task_result \ + or STATE_TASK_RESULT.TIMED_OUT == self._task_result: + self._abort() + self._complete(self._task_result, self._task_result_reason) + else: + self._current_task_work += 1 + self._run() + + @coroutine + def _task_work_timeout(self): + """ + State Task Work Timeout + """ + (yield) + if len(self._task_work_list) <= self._current_task_work: + DLOG.error("Task work timeout timer fired, but current task " + "work is invalid, current_task_work=%i." + % self._current_task_work) + return + + task_work = self._task_work_list[self._current_task_work] + DLOG.debug("Task (%s) work (%s) timed out, timeout_in_secs=%s." + % (self._name, task_work.name, task_work.timeout_in_secs)) + + task_work_result, task_work_result_reason = task_work.timeout() + if STATE_TASK_WORK_RESULT.TIMED_OUT == task_work_result: + self._abort() + self._task_result = STATE_TASK_RESULT.TIMED_OUT + self._task_result_reason = task_work_result_reason + self._complete(self._task_result, self._task_result_reason) + else: + self.task_work_complete(task_work_result, task_work_result_reason) + + def _complete(self, result, reason): + """ + State Task Internal Complete + """ + self.complete(result, reason) + + def complete(self, result, reason): + """ + State Task Complete (expected to be overridden by child class) + """ + DLOG.debug("Task (%s) complete." % self._name) + + def handle_event(self, event, event_data=None): + """ + State Task Handle Event (expected to be overridden by child class) + """ + DLOG.debug("Task (%s) handle event (%s)." % (self._name, event)) + handled = False + + if self._task_inprogress: + task_work = self._task_work_list[self._current_task_work] + handled = task_work.handle_event(event, event_data) + + return handled diff --git a/nfv/nfv-common/nfv_common/state_machine/_state_task_result.py b/nfv/nfv-common/nfv_common/state_machine/_state_task_result.py new file mode 100755 index 00000000..5265adb2 --- /dev/null +++ b/nfv/nfv-common/nfv_common/state_machine/_state_task_result.py @@ -0,0 +1,63 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import six + +from nfv_common.helpers import Constant, Singleton + +from _state_task_work_result import STATE_TASK_WORK_RESULT + + +@six.add_metaclass(Singleton) +class _StateTaskResult(object): + """ + State Task Result - Constants + """ + SUCCESS = Constant('success') + FAILED = Constant('failed') + DEGRADED = Constant('degraded') + ABORTED = Constant('aborted') + TIMED_OUT = Constant('timed-out') + + +def state_task_result_update(task_result, task_result_reason, task_work_result, + task_work_result_reason): + """ + Update State Task Result given a state task work result + """ + if STATE_TASK_WORK_RESULT.WAIT == task_result: + # Nothing to update + return task_result, task_result_reason + + if STATE_TASK_RESULT.SUCCESS == task_result: + + if STATE_TASK_WORK_RESULT.FAILED == task_work_result: + return STATE_TASK_RESULT.FAILED, task_work_result_reason + + elif STATE_TASK_WORK_RESULT.DEGRADED == task_work_result: + return STATE_TASK_RESULT.DEGRADED, task_work_result_reason + + elif STATE_TASK_WORK_RESULT.ABORTED == task_work_result: + return STATE_TASK_RESULT.ABORTED, task_work_result_reason + + elif STATE_TASK_WORK_RESULT.TIMED_OUT == task_work_result: + return STATE_TASK_RESULT.TIMED_OUT, task_work_result_reason + + elif STATE_TASK_RESULT.DEGRADED == task_result: + + if STATE_TASK_WORK_RESULT.FAILED == task_work_result: + return STATE_TASK_RESULT.FAILED, task_work_result_reason + + elif STATE_TASK_WORK_RESULT.ABORTED == task_work_result: + return STATE_TASK_RESULT.ABORTED, task_work_result_reason + + elif STATE_TASK_WORK_RESULT.TIMED_OUT == task_work_result: + return STATE_TASK_RESULT.TIMED_OUT, task_work_result_reason + + return task_result, task_result_reason + + +# Constant Instantiation +STATE_TASK_RESULT = _StateTaskResult() diff --git a/nfv/nfv-common/nfv_common/state_machine/_state_task_work.py b/nfv/nfv-common/nfv_common/state_machine/_state_task_work.py new file mode 100755 index 00000000..8a245618 --- /dev/null +++ b/nfv/nfv-common/nfv_common/state_machine/_state_task_work.py @@ -0,0 +1,104 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import weakref + +from nfv_common import debug + +from _state_task_result import STATE_TASK_WORK_RESULT + +DLOG = debug.debug_get_logger('nfv_common.state_machine.state_task_work') + + +class StateTaskWork(object): + """ + State Task Work + """ + def __init__(self, name, task, force_pass=False, timeout_in_secs=1, + max_retries=1): + self._name = name + self._force_pass = force_pass + self._max_retries = max_retries + self._timeout_in_secs = timeout_in_secs + self._task_reference = weakref.ref(task) + + @property + def name(self): + """ + Returns the name of the task work + """ + return self._name + + @property + def force_pass(self): + """ + Returns the true if force_pass has been set, otherwise false + """ + return self._force_pass + + @property + def max_retries(self): + """ + Returns the maximum retry attempts for task work to be completed + """ + return self._max_retries + + @property + def timeout_in_secs(self): + """ + Returns the maximum amount of time to wait for completion + """ + return self._timeout_in_secs + + @property + def task(self): + task = self._task_reference() + return task + + def extend_timeout(self, timeout_in_secs): + """ + Allow the task work timeout to be extended + """ + DLOG.verbose("Extending state task work timeout for %s to %s." + % (self._name, timeout_in_secs)) + self._timeout_in_secs = timeout_in_secs + self.task.refresh_timeouts() + + def run(self): + """ + State Task Work Run (expected to be overridden by child class) + """ + DLOG.verbose("Default state task work run for %s." % self._name) + return STATE_TASK_WORK_RESULT.SUCCESS, '' + + def complete(self, result, reason): + """ + State Task Work Completed (can be overridden by child class) + """ + DLOG.verbose("Default state task work complete for %s, result=%s, " + "reason=%s." % (self._name, result, reason)) + return result, reason + + def abort(self): + """ + State Task Work Abort (can be overridden by child class) + """ + DLOG.verbose("Default state task work abort for %s." % self._name) + + def timeout(self): + """ + State Task Work Timeout (can be overridden by child class) + """ + DLOG.verbose("Default state task work timeout for %s, timeout=%s secs." + % (self._name, self._timeout_in_secs)) + return STATE_TASK_WORK_RESULT.TIMED_OUT, '' + + def handle_event(self, event, event_data=None): + """ + State Task Work Handle Event (expected to be overridden by child class) + """ + DLOG.verbose("Default state task work handle event for %s." + % self._name) + return False diff --git a/nfv/nfv-common/nfv_common/state_machine/_state_task_work_result.py b/nfv/nfv-common/nfv_common/state_machine/_state_task_work_result.py new file mode 100755 index 00000000..dec9746f --- /dev/null +++ b/nfv/nfv-common/nfv_common/state_machine/_state_task_work_result.py @@ -0,0 +1,25 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import six + +from nfv_common.helpers import Constant, Singleton + + +@six.add_metaclass(Singleton) +class _StateTaskWorkResult(object): + """ + State Task Work Result - Constants + """ + WAIT = Constant('wait') + SUCCESS = Constant('success') + FAILED = Constant('failed') + DEGRADED = Constant('degraded') + ABORTED = Constant('aborted') + TIMED_OUT = Constant('timed-out') + + +# Constant Instantiation +STATE_TASK_WORK_RESULT = _StateTaskWorkResult() diff --git a/nfv/nfv-common/nfv_common/strategy/__init__.py b/nfv/nfv-common/nfv_common/strategy/__init__.py new file mode 100755 index 00000000..8c4bf407 --- /dev/null +++ b/nfv/nfv-common/nfv_common/strategy/__init__.py @@ -0,0 +1,15 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# flake8: noqa +# +from _strategy_defs import STRATEGY_APPLY_TYPE, STRATEGY_STATE, STRATEGY_PHASE +from _strategy_defs import STRATEGY_ALARM_RESTRICTION_TYPES +from _strategy_result import STRATEGY_RESULT, STRATEGY_PHASE_RESULT +from _strategy_result import STRATEGY_STAGE_RESULT, STRATEGY_STEP_RESULT +from _strategy_step import StrategyStep +from _strategy_stage import StrategyStage +from _strategy_phase import StrategyPhase +from _strategy import Strategy diff --git a/nfv/nfv-common/nfv_common/strategy/_strategy.py b/nfv/nfv-common/nfv_common/strategy/_strategy.py new file mode 100755 index 00000000..8b0c8b28 --- /dev/null +++ b/nfv/nfv-common/nfv_common/strategy/_strategy.py @@ -0,0 +1,526 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import json + +from nfv_common import debug + +from _strategy_defs import STRATEGY_PHASE, STRATEGY_STATE +from _strategy_result import STRATEGY_RESULT, strategy_result_update +from _strategy_phase import StrategyPhase + +DLOG = debug.debug_get_logger('nfv_common.strategy') + + +class Strategy(object): + """ + Strategy + """ + def __init__(self, uuid, name, state=None, current_phase=None, build_phase=None, + apply_phase=None, abort_phase=None): + self._uuid = uuid + self._name = name + + if state is None: + self._state = STRATEGY_STATE.INITIAL + else: + self._state = state + + if current_phase is None: + self._current_phase = STRATEGY_PHASE.INITIAL + else: + self._current_phase = current_phase + + if build_phase is None: + build_phase = StrategyPhase(STRATEGY_PHASE.BUILD) + + if apply_phase is None: + apply_phase = StrategyPhase(STRATEGY_PHASE.APPLY) + + if abort_phase is None: + abort_phase = StrategyPhase(STRATEGY_PHASE.ABORT) + + build_phase.strategy = self + apply_phase.strategy = self + abort_phase.strategy = self + + self._phase = dict() + self._phase[STRATEGY_PHASE.BUILD] = build_phase + self._phase[STRATEGY_PHASE.APPLY] = apply_phase + self._phase[STRATEGY_PHASE.ABORT] = abort_phase + + def __del__(self): + del self._phase[STRATEGY_PHASE.BUILD] + del self._phase[STRATEGY_PHASE.APPLY] + del self._phase[STRATEGY_PHASE.ABORT] + + @property + def uuid(self): + """ + Returns the uuid of the strategy + """ + return self._uuid + + @property + def name(self): + """ + Returns the name of the strategy + """ + return self._name + + @property + def state(self): + """ + Returns the state of the strategy + """ + return self._state + + @property + def current_phase(self): + """ + Returns the current phase being executed for the strategy + """ + return self._current_phase + + @property + def build_phase(self): + """ + Returns the build phase of the strategy + """ + return self._phase[STRATEGY_PHASE.BUILD] + + @property + def apply_phase(self): + """ + Returns the apply phase of the strategy + """ + return self._phase[STRATEGY_PHASE.APPLY] + + @property + def abort_phase(self): + """ + Returns the abort phase of the strategy + """ + return self._phase[STRATEGY_PHASE.ABORT] + + def is_building(self): + """ + Returns true if the strategy is building + """ + return STRATEGY_STATE.BUILDING == self._state + + def is_build_failed(self): + """ + Returns true if the strategy build failed + """ + return (STRATEGY_STATE.BUILD_FAILED == self._state or + self.build_phase.is_failed()) + + def is_build_timed_out(self): + """ + Returns true if the strategy build timed out + """ + return (STRATEGY_STATE.BUILD_TIMEOUT == self._state or + self.build_phase.is_timed_out()) + + def is_ready_to_apply(self): + """ + Returns true if the strategy is ready to apply + """ + return STRATEGY_STATE.READY_TO_APPLY == self._state + + def is_applying(self): + """ + Returns true if the strategy is applying + """ + return STRATEGY_STATE.APPLYING == self._state + + def is_apply_failed(self): + """ + Returns true if the strategy apply failed + """ + return (STRATEGY_STATE.APPLY_FAILED == self._state or + self.apply_phase.is_failed()) + + def is_apply_timed_out(self): + """ + Returns true if the strategy apply timed out + """ + return (STRATEGY_STATE.APPLY_TIMEOUT == self._state or + self.apply_phase.is_timed_out()) + + def is_applied(self): + """ + Returns true if the strategy is applied + """ + return STRATEGY_STATE.APPLIED == self._state + + def is_aborting(self): + """ + Returns true if the strategy is aborting + """ + return STRATEGY_STATE.ABORTING == self._state + + def is_abort_failed(self): + """ + Returns true if the strategy abort failed + """ + return (STRATEGY_STATE.ABORT_FAILED == self._state or + self.abort_phase.is_failed()) + + def is_abort_timed_out(self): + """ + Returns true if the strategy abort timed out + """ + return (STRATEGY_STATE.ABORT_TIMEOUT == self._state or + self.abort_phase.is_timed_out()) + + def is_aborted(self): + """ + Returns true if the strategy is aborted + """ + return STRATEGY_STATE.ABORTED == self._state + + def _build(self): + """ + Strategy Build + """ + if STRATEGY_PHASE.INITIAL == self._current_phase: + if STRATEGY_STATE.INITIAL == self._state: + self._state = STRATEGY_STATE.BUILDING + self._current_phase = STRATEGY_PHASE.BUILD + self.build_phase.apply() + + def _apply(self, stage_id=None): + """ + Strategy Apply + """ + success = True + reason = '' + + if STRATEGY_PHASE.BUILD == self._current_phase: + if STRATEGY_STATE.READY_TO_APPLY == self._state: + if stage_id is None: + self._state = STRATEGY_STATE.APPLYING + self._current_phase = STRATEGY_PHASE.APPLY + self.apply_phase.apply() + + elif 0 == stage_id and stage_id < self.apply_phase.total_stages: + self._state = STRATEGY_STATE.APPLYING + self._current_phase = STRATEGY_PHASE.APPLY + self.apply_phase.apply(stage_id+1) + + else: + success = False + reason = ("invalid stage id %s for the apply, total-stages " + "are %s" % (stage_id, self.apply_phase.total_stages)) + + else: + if stage_id is None: + success = False + reason = self.build_phase.result_reason + else: + success = False + reason = ("apply of stage id %s failed: %s " + % (stage_id, self.build_phase.result_reason)) + + elif STRATEGY_PHASE.APPLY == self._current_phase: + if self._state in [STRATEGY_STATE.APPLIED, STRATEGY_STATE.APPLY_FAILED, + STRATEGY_STATE.APPLY_TIMEOUT, + STRATEGY_STATE.ABORTED, STRATEGY_STATE.ABORT_FAILED, + STRATEGY_STATE.ABORT_TIMEOUT]: + success = False + reason = "apply already completed" + + # Allow an apply after a single stage apply has completed + elif stage_id is None and self.apply_phase.current_stage == \ + self.apply_phase.stop_at_stage: + self.apply_phase.apply() + + elif stage_id is None or self.apply_phase.is_inprogress(): + success = False + reason = "apply already inprogress" + + elif stage_id < self.apply_phase.current_stage: + success = False + reason = "apply already complete for stage id %s" % stage_id + + elif stage_id >= self.apply_phase.total_stages: + success = False + reason = ("invalid stage id %s for the apply, total-stages are %s" + % (stage_id, self.apply_phase.total_stages)) + + elif self.apply_phase.current_stage != stage_id: + success = False + reason = ("stage id %s is not the next stage to be applied, " + "next-stage = %s" + % (stage_id, self.apply_phase.current_stage)) + + else: + self.apply_phase.apply(stage_id+1) + + else: + if stage_id is None: + success = False + reason = "apply not supported during an abort" + else: + success = False + reason = ("apply of stage id %s not supported during an abort" + % stage_id) + + return success, reason + + def _abort(self, stage_id=None): + """ + Strategy Abort + """ + if STRATEGY_PHASE.APPLY == self._current_phase: + if stage_id is not None: + if not self.apply_phase.is_inprogress() or \ + stage_id != self.apply_phase.current_stage: + reason = "apply not inprogress for stage id %s" % stage_id + return False, reason + + if self._state in [STRATEGY_STATE.APPLYING, STRATEGY_STATE.APPLY_FAILED, + STRATEGY_STATE.APPLY_TIMEOUT]: + self._state = STRATEGY_STATE.ABORTING + abort_phase = self.apply_phase.abort() + if not abort_phase: + abort_phase = StrategyPhase(STRATEGY_PHASE.ABORT) + abort_phase.strategy = self + self._phase[STRATEGY_PHASE.ABORT] = abort_phase + + # In the case of a single stage apply, if we are not currently + # applying anything, we need to go to the aborted state now. + if self.apply_phase.current_stage == self.apply_phase.stop_at_stage: + self._state = STRATEGY_STATE.ABORTED + self.abort_complete(STRATEGY_RESULT.ABORTED, "") + + elif STRATEGY_STATE.APPLIED != self._state: + self._state = STRATEGY_STATE.ABORTED + + else: + reason = "apply not inprogress" + return False, reason + else: + reason = "apply not inprogress" + return False, reason + + return True, '' + + def _handle_event(self, event, event_data=None): + """ + Strategy Handle Event + """ + handled = False + + if STRATEGY_STATE.BUILDING == self._state: + if STRATEGY_PHASE.BUILD == self._current_phase: + handled = self.build_phase.handle_event(event, event_data) + + elif STRATEGY_STATE.APPLYING == self._state: + if STRATEGY_PHASE.APPLY == self._current_phase: + handled = self.apply_phase.handle_event(event, event_data) + + elif STRATEGY_STATE.ABORTING == self._state: + if STRATEGY_PHASE.APPLY == self._current_phase: + handled = self.apply_phase.handle_event(event, event_data) + + elif STRATEGY_PHASE.ABORT == self._current_phase: + handled = self.abort_phase.handle_event(event, event_data) + + return handled + + def phase_save(self): + """ + Strategy Phase Save + """ + self.save() + + def phase_extend_timeout(self, phase): + """ + Strategy Phase Extend Timeout + """ + phase.refresh_timeouts() + + def phase_complete(self, phase, phase_result, phase_result_reason=None): + """ + Strategy Phase Complete + """ + self.save() + + result, result_reason = \ + strategy_result_update(STRATEGY_RESULT.INITIAL, '', + phase_result, phase_result_reason) + + if STRATEGY_STATE.BUILDING == self._state: + if self._phase[STRATEGY_PHASE.BUILD] == phase: + if phase.is_success() or phase.is_degraded(): + self._state = STRATEGY_STATE.READY_TO_APPLY + self.build_complete(result, result_reason) + + elif phase.is_failed(): + self._state = STRATEGY_STATE.BUILD_FAILED + self.build_complete(result, result_reason) + + elif phase.is_timed_out(): + self._state = STRATEGY_STATE.BUILD_TIMEOUT + self.build_complete(result, result_reason) + + elif STRATEGY_STATE.APPLYING == self._state: + if self._phase[STRATEGY_PHASE.APPLY] == phase: + if phase.is_success() or phase.is_degraded(): + self._state = STRATEGY_STATE.APPLIED + self.apply_complete(result, result_reason) + + elif phase.is_failed(): + self._state = STRATEGY_STATE.APPLY_FAILED + self.apply_complete(result, result_reason) + self._abort() + self._current_phase = STRATEGY_PHASE.ABORT + self.abort_phase.apply() + + elif phase.is_timed_out(): + self._state = STRATEGY_STATE.APPLY_TIMEOUT + self.apply_complete(result, result_reason) + self._abort() + self._current_phase = STRATEGY_PHASE.ABORT + self.abort_phase.apply() + + elif STRATEGY_STATE.ABORTING == self._state: + if self._phase[STRATEGY_PHASE.APPLY] == phase: + if phase.is_success() or phase.is_degraded(): + self._state = STRATEGY_STATE.APPLIED + self.apply_complete(result, result_reason) + + elif phase.is_failed(): + self._state = STRATEGY_STATE.APPLY_FAILED + self.apply_complete(result, result_reason) + + elif phase.is_timed_out(): + self._state = STRATEGY_STATE.APPLY_TIMEOUT + self.apply_complete(result, result_reason) + + self._current_phase = STRATEGY_PHASE.ABORT + self.abort_phase.apply() + + elif self._phase[STRATEGY_PHASE.ABORT] == phase: + if phase.is_success() or phase.is_degraded(): + self._state = STRATEGY_STATE.ABORTED + self.abort_complete(result, result_reason) + + elif phase.is_failed(): + self._state = STRATEGY_STATE.ABORT_FAILED + self.abort_complete(result, result_reason) + + elif phase.is_timed_out(): + self._state = STRATEGY_STATE.ABORT_TIMEOUT + self.abort_complete(result, result_reason) + + self.save() + + def refresh_timeouts(self): + """ + Strategy Refresh Timeouts + """ + self.build_phase.refresh_timeouts() + self.apply_phase.refresh_timeouts() + self.abort_phase.refresh_timeouts() + + def save(self): + """ + Strategy Save (can be overridden by child class) + """ + pass + + def build(self): + """ + Strategy Build (can be overridden by child class) + """ + self._build() + self.save() + + def build_complete(self, result, result_reason): + """ + Strategy Build Complete (can be overridden by child class) + """ + self.save() + return result, result_reason + + def apply(self, stage_id): + """ + Strategy Apply (can be overridden by child class) + """ + success, reason = self._apply(stage_id) + self.save() + return success, reason + + def apply_complete(self, result, result_reason): + """ + Strategy Apply Complete (can be overridden by child class) + """ + self.save() + return result, result_reason + + def abort(self, stage_id): + """ + Strategy Abort (can be overridden by child class) + """ + success, reason = self._abort(stage_id) + self.save() + return success, reason + + def abort_complete(self, result, result_reason): + """ + Strategy Abort Complete (can be overridden by child class) + """ + self.save() + return result, result_reason + + def handle_event(self, event, event_data=None): + """ + Strategy Handle Event (can be overridden by child class) + """ + return self._handle_event(event, event_data) + + def from_dict(self, data, build_phase=None, apply_phase=None, abort_phase=None): + """ + Initializes a strategy object using the given dictionary + """ + Strategy.__init__(self, data['uuid'], data['name'], data['state'], + data['current_phase'], build_phase, apply_phase, + abort_phase) + return self + + def as_dict(self): + """ + Represent the strategy as a dictionary + """ + data = dict() + data['uuid'] = self.uuid + data['name'] = self.name + data['state'] = self.state + data['current_phase'] = self._current_phase + if self.build_phase.name == self._current_phase: + data['current_phase_completion_percentage'] \ + = self.build_phase.completion_percentage + elif self.apply_phase.name == self._current_phase: + data['current_phase_completion_percentage'] \ + = self.apply_phase.completion_percentage + elif self.abort_phase.name == self._current_phase: + data['current_phase_completion_percentage'] \ + = self.abort_phase.completion_percentage + else: + data['current_phase_completion_percentage'] = 0 + data['build_phase'] = self.build_phase.as_dict() + data['apply_phase'] = self.apply_phase.as_dict() + data['abort_phase'] = self.abort_phase.as_dict() + return data + + def as_json(self): + """ + Represent the strategy as json + """ + return json.dumps(self.as_dict()) diff --git a/nfv/nfv-common/nfv_common/strategy/_strategy_defs.py b/nfv/nfv-common/nfv_common/strategy/_strategy_defs.py new file mode 100755 index 00000000..73a31768 --- /dev/null +++ b/nfv/nfv-common/nfv_common/strategy/_strategy_defs.py @@ -0,0 +1,65 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import six + +from nfv_common.helpers import Constant, Constants, Singleton + + +@six.add_metaclass(Singleton) +class StrategyApplyTypes(Constants): + """ + Strategy - Apply Type Constants + """ + SERIAL = Constant('serial') + PARALLEL = Constant('parallel') + IGNORE = Constant('ignore') + + +@six.add_metaclass(Singleton) +class StrategyAlarmRestrictionTypes(Constants): + """ + Strategy - Alarm Restriction Type Constants + """ + STRICT = Constant('strict') + RELAXED = Constant('relaxed') + + +@six.add_metaclass(Singleton) +class StrategyPhases(Constants): + """ + Strategy - Phase Constants + """ + INITIAL = Constant('initial') + BUILD = Constant('build') + APPLY = Constant('apply') + ABORT = Constant('abort') + + +@six.add_metaclass(Singleton) +class StrategyStates(Constants): + """ + Strategy - State Constants + """ + INITIAL = Constant('initial') + BUILDING = Constant('building') + BUILD_FAILED = Constant('build-failed') + BUILD_TIMEOUT = Constant('build-timeout') + READY_TO_APPLY = Constant('ready-to-apply') + APPLYING = Constant('applying') + APPLY_FAILED = Constant('apply-failed') + APPLY_TIMEOUT = Constant('apply-timeout') + APPLIED = Constant('applied') + ABORTING = Constant('aborting') + ABORT_FAILED = Constant('abort-failed') + ABORT_TIMEOUT = Constant('abort-timeout') + ABORTED = Constant('aborted') + + +# Constant Instantiation +STRATEGY_APPLY_TYPE = StrategyApplyTypes() +STRATEGY_ALARM_RESTRICTION_TYPES = StrategyAlarmRestrictionTypes() +STRATEGY_PHASE = StrategyPhases() +STRATEGY_STATE = StrategyStates() diff --git a/nfv/nfv-common/nfv_common/strategy/_strategy_phase.py b/nfv/nfv-common/nfv_common/strategy/_strategy_phase.py new file mode 100755 index 00000000..4eb13a36 --- /dev/null +++ b/nfv/nfv-common/nfv_common/strategy/_strategy_phase.py @@ -0,0 +1,614 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import weakref + +from datetime import datetime + +from nfv_common import timers +from nfv_common import debug +from nfv_common.helpers import coroutine + +from _strategy_defs import STRATEGY_PHASE +from _strategy_result import STRATEGY_PHASE_RESULT, STRATEGY_STAGE_RESULT +from _strategy_result import STRATEGY_STEP_RESULT +from _strategy_result import strategy_phase_result_update + +DLOG = debug.debug_get_logger('nfv_common.strategy.phase') + + +class StrategyPhase(object): + """ + Strategy Phase + """ + def __init__(self, name): + self._name = name + self._current_stage = 0 + self._stop_at_stage = 0 + self._stage_timer_id = None + self._stages = list() + self._result = STRATEGY_PHASE_RESULT.INITIAL + self._result_reason = '' + self._timer_id = None + self._timeout_in_secs = 0 + self._inprogress = False + self._strategy_reference = None + self._start_date_time = '' + self._end_date_time = '' + + def __del__(self): + self._cleanup() + + @property + def name(self): + """ + Returns the name of the strategy phase + """ + return self._name + + @property + def strategy(self): + """ + Returns the strategy this phase is a member of + """ + if self._strategy_reference is not None: + return self._strategy_reference() + return None + + @strategy.setter + def strategy(self, strategy_value): + """ + Set the strategy that this phase is a member of + """ + self._strategy_reference = weakref.ref(strategy_value) + + @property + def current_stage(self): + """ + Return the current stage + """ + return self._current_stage + + @property + def stop_at_stage(self): + """ + Return the stage to stop at + """ + return self._stop_at_stage + + @property + def total_stages(self): + """ + Returns the number of stages + """ + return len(self._stages) + + @property + def stages(self): + """ + Returns the stages for this phase + """ + return self._stages + + @property + def timeout_in_secs(self): + """ + Returns the maximum amount of time to wait for completion + """ + return self._timeout_in_secs + + @property + def result(self): + """ + Returns the result of the strategy phase + """ + return self._result + + @result.setter + def result(self, result): + """ + Updates the result of the strategy phase + """ + self._result = result + + @property + def result_reason(self): + """ + Returns the reason for the result of the strategy phase + """ + return self._result_reason + + @result_reason.setter + def result_reason(self, reason): + """ + Updates the reason for the result of the strategy phase + """ + self._result_reason = reason + + @property + def start_date_time(self): + """ + Returns the start date-time of the strategy phase + """ + return self._start_date_time + + @start_date_time.setter + def start_date_time(self, date_time_str): + """ + Updates the start date-time of the strategy phase + """ + self._start_date_time = date_time_str + + @property + def end_date_time(self): + """ + Returns the end date-time of the strategy phase + """ + return self._end_date_time + + @end_date_time.setter + def end_date_time(self, date_time_str): + """ + Updates the end date-time of the strategy phase + """ + self._end_date_time = date_time_str + + @property + def completion_percentage(self): + """ + Returns the percentage completed + """ + completed_steps = 0 + total_steps = 0 + + if self._inprogress: + for stage in self._stages: + for step in stage.steps: + if step.result in [STRATEGY_STEP_RESULT.SUCCESS, + STRATEGY_STEP_RESULT.DEGRADED, + STRATEGY_STEP_RESULT.FAILED, + STRATEGY_STEP_RESULT.TIMED_OUT, + STRATEGY_STEP_RESULT.ABORTED]: + completed_steps += 1 + total_steps += 1 + + if 0 == total_steps: + return 100 + + return int((completed_steps * 100) / total_steps) + + def is_inprogress(self): + """ + Returns true if the phase is inprogress + """ + if self._inprogress: + stage = self._stages[self._current_stage] + return stage.is_inprogress() + return False + + def is_degraded(self): + """ + Returns true if the phase is degraded + """ + return STRATEGY_PHASE_RESULT.DEGRADED == self._result + + def is_failed(self): + """ + Returns true if the phase has failed + """ + return STRATEGY_PHASE_RESULT.FAILED == self._result + + def is_timed_out(self): + """ + Returns true if the phase has timed out + """ + return STRATEGY_PHASE_RESULT.TIMED_OUT == self._result + + def is_aborted(self): + """ + Returns true if the phase is aborted + """ + return STRATEGY_PHASE_RESULT.ABORTED == self._result + + def is_success(self): + """ + Returns true if the phase completed successfully + """ + return STRATEGY_PHASE_RESULT.SUCCESS == self._result + + def add_stage(self, stage): + """ + Add a stage to this strategy phase + """ + stage.id = len(self._stages) + stage.phase = self + self._stages.append(stage) + + def _save(self): + """ + Phase Save + """ + if self.strategy is not None: + self.strategy.phase_save() + else: + DLOG.info("Strategy reference is invalid for phase (%s)." % self._name) + + def _cleanup(self): + """ + Phase Cleanup + """ + DLOG.info("Phase (%s) cleanup called" % self._name) + + if self._timer_id is not None: + timers.timers_delete_timer(self._timer_id) + self._timer_id = None + + if self._stage_timer_id is not None: + timers.timers_delete_timer(self._stage_timer_id) + self._stage_timer_id = None + + def _abort(self): + """ + Phase Abort + """ + abort_list = list() + + if STRATEGY_PHASE_RESULT.INITIAL == self._result: + self._result = STRATEGY_PHASE_RESULT.ABORTED + self._result_reason = '' + + elif self._inprogress: + self._result = STRATEGY_PHASE_RESULT.ABORTING + self._result_reason = '' + + if 0 < len(self._stages): + if self._current_stage < len(self._stages): + for idx in range(self._current_stage, -1, -1): + stage = self._stages[idx] + abort_stages = stage.abort() + if abort_stages: + abort_list += abort_stages + + DLOG.info("Phase (%s) abort stage (%s)." + % (self._name, stage.name)) + + DLOG.info("Phase (%s) abort." % self._name) + return abort_list + + @coroutine + def _timeout(self): + """ + Phase Timeout + """ + (yield) + DLOG.info("Phase (%s) timed out, timeout_in_secs=%s." + % (self._name, self._timeout_in_secs)) + + if not self._inprogress: + DLOG.info("Phase timeout timer fired, but phase %s is not inprogress." + % self.name) + return + + self._result = STRATEGY_PHASE_RESULT.TIMED_OUT + self._result_reason = 'timeout' + self._complete(self._result, self._result_reason) + + def _complete(self, result, reason): + """ + Phase Internal Complete + """ + self._inprogress = False + self._cleanup() + self._save() + self._end_date_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + return self.complete(result, reason) + + def _apply(self): + """ + Phase Apply + """ + if not self._inprogress: + if 0 == self._current_stage: + self._cleanup() + self._current_stage = 0 + self._inprogress = True + self._result = STRATEGY_PHASE_RESULT.INPROGRESS + self._result_reason = '' + self._start_date_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + else: + DLOG.debug("Phase (%s) not inprogress." % self._name) + return self._result, self._result_reason + + if self._timer_id is None: + timeout_in_secs = 0 + for idx in range(self._current_stage, self._stop_at_stage, 1): + stage = self._stages[idx] + timeout_in_secs += stage.timeout_in_secs + + if 0 < timeout_in_secs: + self._timeout_in_secs = timeout_in_secs + 1 + self._timer_id = timers.timers_create_timer( + self._name, self._timeout_in_secs, self._timeout_in_secs, + self._timeout) + + for idx in range(self._current_stage, self._stop_at_stage, 1): + stage = self._stages[idx] + if self._stage_timer_id is not None: + timers.timers_delete_timer(self._stage_timer_id) + self._stage_timer_id = None + + DLOG.info("Phase %s running %s stage." % (self._name, stage.name)) + + stage_result, stage_result_reason = stage.apply() + self._current_stage = idx + + if STRATEGY_STAGE_RESULT.WAIT == stage_result: + if 0 < stage.timeout_in_secs: + self._stage_timer_id = timers.timers_create_timer( + stage.name, stage.timeout_in_secs, stage.timeout_in_secs, + self._stage_timeout) + + DLOG.debug("Phase (%s) is waiting for stage (%s) to complete, " + "timeout_in_secs=%s." % (self._name, stage.name, + stage.timeout_in_secs)) + self._save() + return STRATEGY_PHASE_RESULT.WAIT, '' + + else: + DLOG.debug("Phase (%s) stage (%s) complete, result=%s, reason=%s." + % (self._name, stage.name, stage_result, + stage_result_reason)) + + self._result, self._result_reason = \ + strategy_phase_result_update(self._result, + self._result_reason, + stage_result, stage_result_reason) + + if STRATEGY_PHASE_RESULT.FAILED == self._result \ + or STRATEGY_PHASE_RESULT.ABORTED == self._result \ + or STRATEGY_PHASE_RESULT.TIMED_OUT == self._result: + return self._complete(self._result, self._result_reason) + else: + self._save() + + else: + # Check if this is an intermediate stop or the phase has been completed + if self._stop_at_stage == len(self._stages): + # Check for a phase with no stages + if 0 == self._current_stage: + self._result = STRATEGY_PHASE_RESULT.SUCCESS + self._result_reason = '' + + DLOG.debug("Phase (%s) done running, result=%s, reason=%s." + % (self._name, self._result, self._result_reason)) + return self._complete(self._result, self._result_reason) + else: + self._cleanup() + self._save() + return self._result, self._result_reason + + def stage_complete(self, stage_result, stage_result_reason=None): + """ + Strategy Stage Complete + """ + stage = self._stages[self._current_stage] + DLOG.debug("Phase (%s) stage (%s) complete, result=%s, reason=%s." + % (self._name, stage.name, stage_result, stage_result_reason)) + + self._result, self._result_reason = \ + strategy_phase_result_update(self._result, self._result_reason, + stage_result, stage_result_reason) + + if STRATEGY_PHASE_RESULT.ABORTING == self._result: + self._result = STRATEGY_PHASE_RESULT.ABORTED + self._result_reason = '' + self._complete(self._result, self._result_reason) + + elif STRATEGY_PHASE_RESULT.FAILED == self._result \ + or STRATEGY_STAGE_RESULT.ABORTED == self._result \ + or STRATEGY_STAGE_RESULT.TIMED_OUT == self._result: + self._complete(self._result, self._result_reason) + + else: + self._current_stage += 1 + self._apply() + + @coroutine + def _stage_timeout(self): + """ + Strategy Stage Timeout + """ + (yield) + if len(self._stages) <= self._current_stage: + DLOG.error("Stage timeout timer fired, but current stage is invalid, " + "current_stage=%i." % self._current_stage) + return + + if not self._inprogress: + DLOG.info("Stage timeout timer fired, but phase %s is not inprogress, " + "current_stage=%i." % (self.name, self._current_stage)) + return + + stage = self._stages[self._current_stage] + DLOG.info("Phase (%s) stage (%s) timed out, timeout_in_secs=%s." + % (self._name, stage.name, stage.timeout_in_secs)) + + stage_result, stage_result_reason = stage.timeout() + + if STRATEGY_STAGE_RESULT.TIMED_OUT == stage_result: + if STRATEGY_PHASE_RESULT.ABORTING == self._result: + self._result = STRATEGY_PHASE_RESULT.ABORTED + self._result_reason = stage_result_reason + else: + self._result = STRATEGY_PHASE_RESULT.TIMED_OUT + self._result_reason = stage_result_reason + + self._complete(self._result, self._result_reason) + else: + self.stage_complete(stage_result, stage_result_reason) + + def stage_extend_timeout(self): + """ + Strategy Stage Extend Timeout + """ + if self.strategy is not None: + self.strategy.phase_extend_timeout(self) + else: + self.refresh_timeouts() + + def stage_save(self): + """ + Strategy Stage Save + """ + self._save() + + def refresh_timeouts(self): + """ + Phase Refresh Timeouts + """ + if not self._inprogress: + # No need to refresh phase timer, phase not started + return + + if self._timer_id is not None: + timers.timers_delete_timer(self._timer_id) + self._timer_id = None + + # Calculate overall phase timeout + timeout_in_secs = 0 + for idx in range(self._current_stage, self._stop_at_stage, 1): + stage = self._stages[idx] + timeout_in_secs += stage.timeout_in_secs + + if 0 == timeout_in_secs: + # No need to refresh phase timer, phase not inprogress + return + + self._timeout_in_secs = timeout_in_secs + 1 + + # Re-start phase timer + self._timer_id = timers.timers_create_timer(self._name, + self._timeout_in_secs, + self._timeout_in_secs, + self._timeout) + + DLOG.verbose("Started overall strategy phase timer, timeout_in_sec=%s" + % self._timeout_in_secs) + + if self._stage_timer_id is not None: + timers.timers_delete_timer(self._stage_timer_id) + self._stage_timer_id = None + + if len(self._stages) <= self._current_stage: + # No need to refresh strategy stage timer, no current stage being + # applied + return + + # Re-start stage timer + stage = self._stages[self._current_stage] + if 0 < stage.timeout_in_secs: + self._stage_timer_id = timers.timers_create_timer( + stage.name, stage.timeout_in_secs, stage.timeout_in_secs, + self._stage_timeout) + + DLOG.verbose("Started strategy stage timer, timeout_in_sec=%s" + % stage.timeout_in_secs) + + stage.refresh_timeouts() + + def abort(self): + """ + Phase Abort (can be overridden by child class) + """ + abort_list = self._abort() + abort_phase = StrategyPhase(STRATEGY_PHASE.ABORT) + for abort_stage in abort_list: + abort_phase.add_stage(abort_stage) + return abort_phase + + def apply(self, stop_at_stage=None): + """ + Phase Apply (can be overridden by child class) + """ + if stop_at_stage is None: + self._stop_at_stage = len(self._stages) + elif 0 <= stop_at_stage <= len(self._stages): + self._stop_at_stage = stop_at_stage + return self._apply() + + def complete(self, result, reason): + """ + Phase Complete (can be overridden by child class) + """ + DLOG.debug("Strategy Phase (%s) complete." % self._name) + if self.strategy is not None: + self.strategy.phase_complete(self, result, reason) + else: + DLOG.info("Strategy reference is invalid for phase (%s)." % self._name) + return self._result, self._result_reason + + def handle_event(self, event, event_data=None): + """ + Phase Handle Event (can be overridden by child class) + """ + DLOG.debug("Strategy Phase (%s) handle event (%s)." % (self._name, event)) + handled = False + + if self._inprogress: + stage = self._stages[self._current_stage] + handled = stage.handle_event(event, event_data) + + else: + DLOG.debug("Phase (%s) not inprogress." % self._name) + + return handled + + def from_dict(self, data, stages=None): + """ + Initializes a strategy phase object using the given dictionary + """ + StrategyPhase.__init__(self, data['name']) + self._inprogress = data['inprogress'] + self._current_stage = data['current_stage'] + self._stop_at_stage = data['stop_at_stage'] + self._result = data['result'] + self._result_reason = data['result_reason'] + self._start_date_time = data['start_date_time'] + self._end_date_time = data['end_date_time'] + + if stages is not None: + for stage in stages: + self.add_stage(stage) + + if self._inprogress and 0 < len(self._stages): + if 0 == self._current_stage: + stage = self._stages[self._current_stage] + if not stage.is_inprogress(): + self._inprogress = False + self._result = STRATEGY_PHASE_RESULT.INITIAL + self._result_reason = '' + + return self + + def as_dict(self): + """ + Represent the strategy phase as a dictionary + """ + data = dict() + data['name'] = self.name + data['timeout'] = self._timeout_in_secs + data['inprogress'] = self._inprogress + data['completion_percentage'] = self.completion_percentage + data['current_stage'] = self._current_stage + data['stop_at_stage'] = self._stop_at_stage + data['total_stages'] = len(self._stages) + data['stages'] = list() + for stage in self._stages: + data['stages'].append(stage.as_dict()) + data['result'] = self._result + data['result_reason'] = self._result_reason + data['start_date_time'] = self._start_date_time + data['end_date_time'] = self._end_date_time + return data diff --git a/nfv/nfv-common/nfv_common/strategy/_strategy_result.py b/nfv/nfv-common/nfv_common/strategy/_strategy_result.py new file mode 100755 index 00000000..0ddfde70 --- /dev/null +++ b/nfv/nfv-common/nfv_common/strategy/_strategy_result.py @@ -0,0 +1,337 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import six + +from nfv_common.helpers import Constant, Singleton + + +@six.add_metaclass(Singleton) +class StrategyResult(object): + """ + Strategy Result - Constants + """ + INITIAL = Constant('initial') + INPROGRESS = Constant('inprogress') + WAIT = Constant('wait') + SUCCESS = Constant('success') + DEGRADED = Constant('degraded') + FAILED = Constant('failed') + TIMED_OUT = Constant('timed-out') + ABORTING = Constant('aborted') + ABORTED = Constant('aborted') + + +@six.add_metaclass(Singleton) +class StrategyPhaseResult(object): + """ + Strategy Phase Result - Constants + """ + INITIAL = Constant('initial') + INPROGRESS = Constant('inprogress') + WAIT = Constant('wait') + SUCCESS = Constant('success') + DEGRADED = Constant('degraded') + FAILED = Constant('failed') + TIMED_OUT = Constant('timed-out') + ABORTING = Constant('aborted') + ABORTED = Constant('aborted') + + +@six.add_metaclass(Singleton) +class StrategyStageResult(object): + """ + Strategy Stage Result - Constants + """ + INITIAL = Constant('initial') + INPROGRESS = Constant('inprogress') + WAIT = Constant('wait') + SUCCESS = Constant('success') + DEGRADED = Constant('degraded') + FAILED = Constant('failed') + TIMED_OUT = Constant('timed-out') + ABORTING = Constant('aborted') + ABORTED = Constant('aborted') + + +@six.add_metaclass(Singleton) +class StrategyStepResult(object): + """ + Strategy Step Result - Constants + """ + INITIAL = Constant('initial') + INPROGRESS = Constant('inprogress') + WAIT = Constant('wait') + SUCCESS = Constant('success') + DEGRADED = Constant('degraded') + FAILED = Constant('failed') + TIMED_OUT = Constant('timed-out') + ABORTING = Constant('aborted') + ABORTED = Constant('aborted') + + +def strategy_result_update(strategy_result, strategy_result_reason, phase_result, + phase_result_reason): + """ + Update Strategy Stage Result given a strategy phase result + """ + if STRATEGY_RESULT.WAIT == strategy_result: + # Nothing to update + return strategy_result, strategy_result_reason + + if STRATEGY_RESULT.INITIAL == strategy_result: + + if STRATEGY_PHASE_RESULT.ABORTING == phase_result: + return STRATEGY_RESULT.ABORTING, phase_result_reason + + elif STRATEGY_PHASE_RESULT.ABORTED == phase_result: + return STRATEGY_RESULT.ABORTED, phase_result_reason + + elif STRATEGY_PHASE_RESULT.TIMED_OUT == phase_result: + return STRATEGY_RESULT.TIMED_OUT, phase_result_reason + + elif STRATEGY_PHASE_RESULT.FAILED == phase_result: + return STRATEGY_RESULT.FAILED, phase_result_reason + + elif STRATEGY_PHASE_RESULT.DEGRADED == phase_result: + return STRATEGY_RESULT.DEGRADED, phase_result_reason + + elif STRATEGY_PHASE_RESULT.SUCCESS == phase_result: + return STRATEGY_RESULT.SUCCESS, phase_result_reason + + elif STRATEGY_PHASE_RESULT.INPROGRESS == phase_result: + return STRATEGY_RESULT.INPROGRESS, phase_result_reason + + elif STRATEGY_RESULT.INPROGRESS == strategy_result: + + if STRATEGY_PHASE_RESULT.ABORTING == phase_result: + return STRATEGY_RESULT.ABORTING, phase_result_reason + + elif STRATEGY_PHASE_RESULT.ABORTED == phase_result: + return STRATEGY_RESULT.ABORTED, phase_result_reason + + elif STRATEGY_PHASE_RESULT.TIMED_OUT == phase_result: + return STRATEGY_RESULT.TIMED_OUT, phase_result_reason + + elif STRATEGY_PHASE_RESULT.FAILED == phase_result: + return STRATEGY_RESULT.FAILED, phase_result_reason + + elif STRATEGY_PHASE_RESULT.DEGRADED == phase_result: + return STRATEGY_RESULT.DEGRADED, phase_result_reason + + elif STRATEGY_PHASE_RESULT.SUCCESS == phase_result: + return STRATEGY_RESULT.SUCCESS, phase_result_reason + + elif STRATEGY_RESULT.SUCCESS == strategy_result: + + if STRATEGY_PHASE_RESULT.ABORTING == phase_result: + return STRATEGY_RESULT.ABORTING, phase_result_reason + + elif STRATEGY_PHASE_RESULT.ABORTED == phase_result: + return STRATEGY_RESULT.ABORTED, phase_result_reason + + elif STRATEGY_PHASE_RESULT.TIMED_OUT == phase_result: + return STRATEGY_RESULT.TIMED_OUT, phase_result_reason + + elif STRATEGY_PHASE_RESULT.FAILED == phase_result: + return STRATEGY_RESULT.FAILED, phase_result_reason + + elif STRATEGY_PHASE_RESULT.DEGRADED == phase_result: + return STRATEGY_RESULT.DEGRADED, phase_result_reason + + elif STRATEGY_STAGE_RESULT.DEGRADED == strategy_result: + + if STRATEGY_PHASE_RESULT.ABORTING == phase_result: + return STRATEGY_RESULT.ABORTING, phase_result_reason + + elif STRATEGY_PHASE_RESULT.ABORTED == phase_result: + return STRATEGY_RESULT.ABORTED, phase_result_reason + + elif STRATEGY_PHASE_RESULT.TIMED_OUT == phase_result: + return STRATEGY_RESULT.TIMED_OUT, phase_result_reason + + elif STRATEGY_PHASE_RESULT.FAILED == phase_result: + return STRATEGY_RESULT.FAILED, phase_result_reason + + return strategy_result, strategy_result_reason + + +def strategy_phase_result_update(phase_result, phase_result_reason, stage_result, + stage_result_reason): + """ + Update Strategy Phase Result given a strategy stage result + """ + if STRATEGY_PHASE_RESULT.WAIT == phase_result: + # Nothing to update + return phase_result, phase_result_reason + + if STRATEGY_PHASE_RESULT.INITIAL == phase_result: + + if STRATEGY_STAGE_RESULT.ABORTING == stage_result: + return STRATEGY_PHASE_RESULT.ABORTING, stage_result_reason + + elif STRATEGY_STAGE_RESULT.ABORTED == stage_result: + return STRATEGY_PHASE_RESULT.ABORTED, stage_result_reason + + elif STRATEGY_STAGE_RESULT.TIMED_OUT == stage_result: + return STRATEGY_PHASE_RESULT.TIMED_OUT, stage_result_reason + + elif STRATEGY_STAGE_RESULT.FAILED == stage_result: + return STRATEGY_PHASE_RESULT.FAILED, stage_result_reason + + elif STRATEGY_STAGE_RESULT.DEGRADED == stage_result: + return STRATEGY_PHASE_RESULT.DEGRADED, stage_result_reason + + elif STRATEGY_STAGE_RESULT.SUCCESS == stage_result: + return STRATEGY_PHASE_RESULT.SUCCESS, stage_result_reason + + elif STRATEGY_STAGE_RESULT.INPROGRESS == stage_result: + return STRATEGY_PHASE_RESULT.INPROGRESS, stage_result_reason + + elif STRATEGY_PHASE_RESULT.INPROGRESS == phase_result: + + if STRATEGY_STAGE_RESULT.ABORTING == stage_result: + return STRATEGY_PHASE_RESULT.ABORTING, stage_result_reason + + elif STRATEGY_STAGE_RESULT.ABORTED == stage_result: + return STRATEGY_PHASE_RESULT.ABORTED, stage_result_reason + + elif STRATEGY_STAGE_RESULT.TIMED_OUT == stage_result: + return STRATEGY_PHASE_RESULT.TIMED_OUT, stage_result_reason + + elif STRATEGY_STAGE_RESULT.FAILED == stage_result: + return STRATEGY_PHASE_RESULT.FAILED, stage_result_reason + + elif STRATEGY_STAGE_RESULT.DEGRADED == stage_result: + return STRATEGY_PHASE_RESULT.DEGRADED, stage_result_reason + + elif STRATEGY_STAGE_RESULT.SUCCESS == stage_result: + return STRATEGY_PHASE_RESULT.SUCCESS, stage_result_reason + + elif STRATEGY_PHASE_RESULT.SUCCESS == phase_result: + + if STRATEGY_STAGE_RESULT.ABORTING == stage_result: + return STRATEGY_PHASE_RESULT.ABORTING, stage_result_reason + + elif STRATEGY_STAGE_RESULT.ABORTED == stage_result: + return STRATEGY_PHASE_RESULT.ABORTED, stage_result_reason + + elif STRATEGY_STAGE_RESULT.TIMED_OUT == stage_result: + return STRATEGY_PHASE_RESULT.TIMED_OUT, stage_result_reason + + elif STRATEGY_STAGE_RESULT.FAILED == stage_result: + return STRATEGY_PHASE_RESULT.FAILED, stage_result_reason + + elif STRATEGY_STAGE_RESULT.DEGRADED == stage_result: + return STRATEGY_PHASE_RESULT.DEGRADED, stage_result_reason + + elif STRATEGY_PHASE_RESULT.DEGRADED == phase_result: + + if STRATEGY_STAGE_RESULT.ABORTING == stage_result: + return STRATEGY_PHASE_RESULT.ABORTING, stage_result_reason + + elif STRATEGY_STAGE_RESULT.ABORTED == stage_result: + return STRATEGY_PHASE_RESULT.ABORTED, stage_result_reason + + elif STRATEGY_STAGE_RESULT.TIMED_OUT == stage_result: + return STRATEGY_PHASE_RESULT.TIMED_OUT, stage_result_reason + + elif STRATEGY_STAGE_RESULT.FAILED == stage_result: + return STRATEGY_PHASE_RESULT.FAILED, stage_result_reason + + return phase_result, phase_result_reason + + +def strategy_stage_result_update(stage_result, stage_result_reason, step_result, + step_result_reason): + """ + Update Strategy Stage Result given a strategy step result + """ + if STRATEGY_STAGE_RESULT.WAIT == stage_result: + # Nothing to update + return stage_result, stage_result_reason + + if STRATEGY_STAGE_RESULT.INITIAL == stage_result: + + if STRATEGY_STEP_RESULT.ABORTING == step_result: + return STRATEGY_STAGE_RESULT.ABORTING, step_result_reason + + elif STRATEGY_STEP_RESULT.ABORTED == step_result: + return STRATEGY_STAGE_RESULT.ABORTED, step_result_reason + + elif STRATEGY_STEP_RESULT.TIMED_OUT == step_result: + return STRATEGY_STAGE_RESULT.TIMED_OUT, step_result_reason + + elif STRATEGY_STEP_RESULT.FAILED == step_result: + return STRATEGY_STAGE_RESULT.FAILED, step_result_reason + + elif STRATEGY_STEP_RESULT.DEGRADED == step_result: + return STRATEGY_STAGE_RESULT.DEGRADED, step_result_reason + + elif STRATEGY_STEP_RESULT.SUCCESS == step_result: + return STRATEGY_STAGE_RESULT.SUCCESS, step_result_reason + + elif STRATEGY_STEP_RESULT.INPROGRESS == step_result: + return STRATEGY_STAGE_RESULT.INPROGRESS, step_result_reason + + elif STRATEGY_STAGE_RESULT.INPROGRESS == stage_result: + + if STRATEGY_STEP_RESULT.ABORTING == step_result: + return STRATEGY_STAGE_RESULT.ABORTING, step_result_reason + + elif STRATEGY_STEP_RESULT.ABORTED == step_result: + return STRATEGY_STAGE_RESULT.ABORTED, step_result_reason + + elif STRATEGY_STEP_RESULT.TIMED_OUT == step_result: + return STRATEGY_STAGE_RESULT.TIMED_OUT, step_result_reason + + elif STRATEGY_STEP_RESULT.FAILED == step_result: + return STRATEGY_STAGE_RESULT.FAILED, step_result_reason + + elif STRATEGY_STEP_RESULT.DEGRADED == step_result: + return STRATEGY_STAGE_RESULT.DEGRADED, step_result_reason + + elif STRATEGY_STEP_RESULT.SUCCESS == step_result: + return STRATEGY_STAGE_RESULT.SUCCESS, step_result_reason + + elif STRATEGY_STAGE_RESULT.SUCCESS == stage_result: + + if STRATEGY_STEP_RESULT.ABORTING == step_result: + return STRATEGY_STAGE_RESULT.ABORTING, step_result_reason + + elif STRATEGY_STEP_RESULT.ABORTED == step_result: + return STRATEGY_STAGE_RESULT.ABORTED, step_result_reason + + elif STRATEGY_STEP_RESULT.TIMED_OUT == step_result: + return STRATEGY_STAGE_RESULT.TIMED_OUT, step_result_reason + + elif STRATEGY_STEP_RESULT.FAILED == step_result: + return STRATEGY_STAGE_RESULT.FAILED, step_result_reason + + elif STRATEGY_STEP_RESULT.DEGRADED == step_result: + return STRATEGY_STAGE_RESULT.DEGRADED, step_result_reason + + elif STRATEGY_STAGE_RESULT.DEGRADED == stage_result: + + if STRATEGY_STEP_RESULT.ABORTING == step_result: + return STRATEGY_STAGE_RESULT.ABORTING, step_result_reason + + elif STRATEGY_STEP_RESULT.ABORTED == step_result: + return STRATEGY_STAGE_RESULT.ABORTED, step_result_reason + + elif STRATEGY_STEP_RESULT.TIMED_OUT == step_result: + return STRATEGY_STAGE_RESULT.TIMED_OUT, step_result_reason + + elif STRATEGY_STEP_RESULT.FAILED == step_result: + return STRATEGY_STAGE_RESULT.FAILED, step_result_reason + + return stage_result, stage_result_reason + + +# Constant Instantiation +STRATEGY_RESULT = StrategyResult() +STRATEGY_PHASE_RESULT = StrategyPhaseResult() +STRATEGY_STAGE_RESULT = StrategyStageResult() +STRATEGY_STEP_RESULT = StrategyStepResult() diff --git a/nfv/nfv-common/nfv_common/strategy/_strategy_stage.py b/nfv/nfv-common/nfv_common/strategy/_strategy_stage.py new file mode 100755 index 00000000..b3c056f0 --- /dev/null +++ b/nfv/nfv-common/nfv_common/strategy/_strategy_stage.py @@ -0,0 +1,594 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import weakref + +from datetime import datetime + +from nfv_common import timers +from nfv_common import debug +from nfv_common.helpers import coroutine + +from _strategy_result import STRATEGY_STAGE_RESULT, STRATEGY_STEP_RESULT +from _strategy_result import strategy_stage_result_update + +DLOG = debug.debug_get_logger('nfv_common.strategy.stage') + + +class StrategyStage(object): + """ + Strategy Stage + """ + def __init__(self, name): + self._id = 0 + self._name = name + self._current_step = 0 + self._step_timer_id = None + self._steps = list() + self._result = STRATEGY_STAGE_RESULT.INITIAL + self._result_reason = '' + self._timer_id = None + self._timeout_in_secs = 0 + self._inprogress = False + self._phase_reference = None + self._start_date_time = '' + self._end_date_time = '' + + def __del__(self): + self._cleanup() + + @property + def name(self): + """ + Returns the name of the stage + """ + return self._name + + @property + def id(self): + """ + Returns the id of the stage + """ + return self._id + + @id.setter + def id(self, value): + """ + Sets the id of the step + """ + self._id = value + + @property + def timeout_in_secs(self): + """ + Returns the maximum amount of time to wait for completion + """ + return self._timeout_in_secs + + @property + def result(self): + """ + Returns the result of the stage + """ + return self._result + + @result.setter + def result(self, result): + """ + Updates the result of the stage + """ + self._result = result + + @property + def result_reason(self): + """ + Returns the reason for the result of the stage + """ + return self._result_reason + + @result_reason.setter + def result_reason(self, reason): + """ + Updates the reason for the result of the stage + """ + self._result_reason = reason + + @property + def start_date_time(self): + """ + Returns the start date-time of the stage + """ + return self._start_date_time + + @start_date_time.setter + def start_date_time(self, date_time_str): + """ + Updates the start date-time of the stage + """ + self._start_date_time = date_time_str + + @property + def end_date_time(self): + """ + Returns the end date-time of the stage + """ + return self._end_date_time + + @end_date_time.setter + def end_date_time(self, date_time_str): + """ + Updates the end date-time of the stage + """ + self._end_date_time = date_time_str + + @property + def strategy(self): + """ + Returns the strategy this stage is a member of + """ + if self.phase is not None: + return self.phase.strategy + return None + + @property + def phase(self): + """ + Returns the phase this stage is a member of + """ + if self._phase_reference is not None: + return self._phase_reference() + return None + + @phase.setter + def phase(self, phase_value): + """ + Set the phase that this stage is a member of + """ + self._phase_reference = weakref.ref(phase_value) + + @property + def steps(self): + """ + Returns the steps for this stage + """ + return self._steps + + def is_inprogress(self): + """ + Returns if the stage is inprogress or not + """ + return self._inprogress + + def is_failed(self): + """ + Return true if this stage has failed + """ + return STRATEGY_STAGE_RESULT.FAILED == self._result + + def timed_out(self): + """ + Return true if this stage has timed out + """ + return STRATEGY_STAGE_RESULT.TIMED_OUT == self._result + + def aborted(self): + """ + Return true if this stage was aborted + """ + return STRATEGY_STAGE_RESULT.ABORTED == self._result + + def add_step(self, step): + """ + Add a step to this stage + """ + step.id = len(self._steps) + step.stage = self + self._steps.append(step) + + self._timeout_in_secs = 0 + for step in self._steps: + self._timeout_in_secs += step.timeout_in_secs + + if 0 < self._timeout_in_secs: + self._timeout_in_secs += 1 + + def _save(self): + """ + Stage Save + """ + import os + import inspect + + if self.phase is not None: + self.phase.stage_save() + else: + caller = inspect.currentframe().f_back + _, filename = os.path.split(caller.f_code.co_filename) + DLOG.info("Traceback1: %s %s" % (filename, caller.f_lineno)) + + caller = inspect.currentframe().f_back.f_back + _, filename = os.path.split(caller.f_code.co_filename) + DLOG.info("Traceback2: %s %s" % (filename, caller.f_lineno)) + + caller = inspect.currentframe().f_back.f_back.f_back + _, filename = os.path.split(caller.f_code.co_filename) + DLOG.info("Traceback3: %s %s" % (filename, caller.f_lineno)) + + caller = inspect.currentframe().f_back.f_back.f_back.f_back + _, filename = os.path.split(caller.f_code.co_filename) + DLOG.info("Traceback4: %s %s" % (filename, caller.f_lineno)) + + caller = inspect.currentframe().f_back.f_back.f_back.f_back.f_back + _, filename = os.path.split(caller.f_code.co_filename) + DLOG.info("Traceback5: %s %s" % (filename, caller.f_lineno)) + + DLOG.info("Strategy Phase reference is invalid for stage (%s)." + % self._name) + + def _cleanup(self): + """ + Stage Cleanup + """ + DLOG.info("Stage (%s) cleanup called" % self._name) + + if self._timer_id is not None: + timers.timers_delete_timer(self._timer_id) + self._timer_id = None + + if self._step_timer_id is not None: + timers.timers_delete_timer(self._step_timer_id) + self._step_timer_id = None + + def _abort(self): + """ + Stage Abort + """ + abort_list = list() + + if STRATEGY_STAGE_RESULT.INITIAL == self._result: + self._result = STRATEGY_STAGE_RESULT.ABORTED + self._result_reason = '' + + elif self._inprogress: + self._result = STRATEGY_STAGE_RESULT.ABORTING + self._result_reason = '' + + if 0 < len(self._steps): + if self._current_step < len(self._steps): + for idx in range(self._current_step, -1, -1): + step = self._steps[idx] + abort_steps = step.abort() + if abort_steps: + abort_list += abort_steps + + DLOG.info("Stage (%s) abort step (%s)." + % (self._name, step.name)) + + DLOG.info("Stage (%s) abort." % self._name) + return abort_list + + @coroutine + def _timeout(self): + """ + Stage Timeout + """ + (yield) + DLOG.info("Stage (%s) timed out, timeout_in_secs=%s." + % (self._name, self._timeout_in_secs)) + + if not self._inprogress: + DLOG.info("Stage timeout timer fired, but stage %s is not inprogress." + % self.name) + return + + self._result = STRATEGY_STAGE_RESULT.TIMED_OUT + self._result_reason = 'timeout' + self._complete(self._result, self._result_reason) + + def _complete(self, result, reason): + """ + Stage Internal Complete + """ + self._inprogress = False + self._cleanup() + self._save() + self._end_date_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + return self.complete(result, reason) + + def _apply(self): + """ + Stage Apply + """ + if not self._inprogress: + if 0 == self._current_step: + self._cleanup() + self._inprogress = True + self._result = STRATEGY_STAGE_RESULT.INPROGRESS + self._result_reason = '' + self._start_date_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + if 0 < self.timeout_in_secs: + self._timer_id = timers.timers_create_timer( + self._name, self._timeout_in_secs, self._timeout_in_secs, + self._timeout) + else: + DLOG.debug("Stage (%s) not inprogress." % self._name) + return self._result, self._result_reason + + for idx in range(self._current_step, len(self._steps), 1): + step = self._steps[idx] + if self._step_timer_id is not None: + timers.timers_delete_timer(self._step_timer_id) + self._step_timer_id = None + + step.start_date_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + step.result, step.result_reason = step.apply() + self._current_step = idx + + if STRATEGY_STEP_RESULT.WAIT == step.result: + if 0 < step.timeout_in_secs: + self._step_timer_id = timers.timers_create_timer( + step.name, step.timeout_in_secs, step.timeout_in_secs, + self._step_timeout) + + DLOG.debug("Stage (%s) is waiting for step (%s) to complete, " + "timeout_in_secs=%s." % (self.name, step.name, + step.timeout_in_secs)) + + self._save() + return STRATEGY_STAGE_RESULT.WAIT, '' + + else: + DLOG.debug("Stage (%s) step (%s) complete, result=%s, reason=%s." + % (self._name, step.name, step.result, + step.result_reason)) + + step.end_date_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + self._result, self._result_reason = \ + strategy_stage_result_update(self._result, + self._result_reason, + step.result, step.result_reason) + + if STRATEGY_STAGE_RESULT.FAILED == self._result or \ + STRATEGY_STAGE_RESULT.ABORTED == self._result or \ + STRATEGY_STAGE_RESULT.TIMED_OUT == self._result: + return self._complete(self._result, self._result_reason) + else: + self._save() + + else: + # Check for a stage with no steps + if 0 == self._current_step: + self._result = STRATEGY_STAGE_RESULT.SUCCESS + self._result_reason = '' + + DLOG.debug("Stage (%s) done running, result=%s, reason=%s." + % (self._name, self._result, self._result_reason)) + return self._complete(self._result, self._result_reason) + + def step_complete(self, step_result, step_result_reason=None): + """ + Stage Step Complete + """ + step = self._steps[self._current_step] + + DLOG.debug("Stage (%s) step (%s) complete, step_result=%s, step_reason=%s." + % (self._name, step.name, step_result, step_result_reason)) + + step.result, step.result_reason = \ + step.complete(step_result, step_result_reason) + + step.end_date_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + if step_result != step.result: + DLOG.debug("Stage (%s) step (%s) complete, result updated, " + "was_result=%s, now_result=%s." % (self._name, step.name, + step_result, step.result)) + + self._result, self._result_reason = \ + strategy_stage_result_update(self._result, self._result_reason, + step.result, step.result_reason) + + if STRATEGY_STAGE_RESULT.ABORTING == self._result: + self._result = STRATEGY_STAGE_RESULT.ABORTED + self._result_reason = '' + self._complete(self._result, self._result_reason) + + elif STRATEGY_STAGE_RESULT.FAILED == self._result or \ + STRATEGY_STAGE_RESULT.ABORTED == self._result or \ + STRATEGY_STAGE_RESULT.TIMED_OUT == self._result: + self._complete(self._result, self._result_reason) + + else: + self._current_step += 1 + self._apply() + + @coroutine + def _step_timeout(self): + """ + Stage Step Timeout + """ + (yield) + if len(self._steps) <= self._current_step: + DLOG.error("Step timeout timer fired, but current step is invalid, " + "current_step=%i." % self._current_step) + return + + if not self._inprogress: + DLOG.info("Step timeout timer fired, but stage %s is not inprogress, " + "current_step=%i." % (self.name, self._current_step)) + return + + step = self._steps[self._current_step] + DLOG.info("Stage (%s) step (%s) timed out, timeout_in_secs=%s." + % (self._name, step.name, step.timeout_in_secs)) + + step.result, step.result_reason = step.timeout() + + if STRATEGY_STEP_RESULT.TIMED_OUT == step.result: + step.end_date_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + if STRATEGY_STAGE_RESULT.ABORTING == self._result: + self._result = STRATEGY_STAGE_RESULT.ABORTED + self._result_reason = '' + else: + self._result = STRATEGY_STAGE_RESULT.TIMED_OUT + self._result_reason = step.result_reason + + self._complete(self._result, self._result_reason) + + else: + self.step_complete(step.result, step.result_reason) + + def step_extend_timeout(self): + """ + Stage Step Extend Timeout + """ + if self.phase is not None: + self.phase.stage_extend_timeout() + else: + self.refresh_timeouts() + + def refresh_timeouts(self): + """ + Stage Refresh Timeouts + """ + if not self.is_inprogress(): + # No need to refresh stage timer, stage not started + return + + if self._timer_id is not None: + timers.timers_delete_timer(self._timer_id) + self._timer_id = None + + # Calculate overall stage timeout + self._timeout_in_secs = 0 + for step in self._steps: + self._timeout_in_secs += step.timeout_in_secs + + if 0 < self._timeout_in_secs: + self._timeout_in_secs += 1 + + # Re-start stage timer + self._timer_id = timers.timers_create_timer(self._name, + self._timeout_in_secs, + self._timeout_in_secs, + self._timeout) + + DLOG.verbose("Started overall strategy stage timer, timeout_in_sec=%s" + % self._timeout_in_secs) + + if self._step_timer_id is not None: + timers.timers_delete_timer(self._step_timer_id) + self._step_timer_id = None + + if len(self._steps) <= self._current_step: + # No need to refresh step timer, no current step being applied + return + + # Re-start step timer + step = self._steps[self._current_step] + if 0 < step.timeout_in_secs: + self._step_timer_id = timers.timers_create_timer( + step.name, step.timeout_in_secs, step.timeout_in_secs, + self._step_timeout) + + DLOG.verbose("Started strategy step timer, timeout_in_sec=%s" + % step.timeout_in_secs) + + def abort(self): + """ + Stage Abort (can be overridden by child class) + """ + abort_list = self._abort() + if abort_list: + abort_stage = StrategyStage(self.name) + for abort_step in abort_list: + abort_stage.add_step(abort_step) + return [abort_stage] + return [] + + def apply(self): + """ + Stage Apply (can be overridden by child class) + """ + return self._apply() + + def complete(self, result, result_reason): + """ + Stage Complete (can be overridden by child class) + """ + DLOG.debug("Strategy Stage (%s) complete." % self._name) + if self.phase is not None: + self.phase.stage_complete(result, result_reason) + else: + DLOG.info("Strategy Phase reference is invalid for stage (%s)." + % self._name) + return self._result, self._result_reason + + def handle_event(self, event, event_data=None): + """ + Stage Handle Event (can be overridden by child class) + """ + DLOG.debug("Stage (%s) handle event (%s)." % (self._name, event)) + handled = False + + if self._inprogress: + step = self._steps[self._current_step] + handled = step.handle_event(event, event_data) + + return handled + + def from_dict(self, data, steps=None): + """ + Initializes a strategy stage object using the given dictionary + """ + StrategyStage.__init__(self, data['name']) + self._inprogress = data['inprogress'] + self._current_step = data['current_step'] + self._result = data['result'] + self._result_reason = data['result_reason'] + self._start_date_time = data['start_date_time'] + self._end_date_time = data['end_date_time'] + + if steps is not None: + for step in steps: + self.add_step(step) + + if self._inprogress and 0 < len(self._steps): + if 0 == self._current_step: + step = self._steps[self._current_step] + if STRATEGY_STEP_RESULT.INITIAL == step.result: + self._inprogress = False + self._result = STRATEGY_STAGE_RESULT.INITIAL + self._result_reason = '' + + elif len(self._steps) > self._current_step: + step = self._steps[self._current_step] + if step.result not in [STRATEGY_STEP_RESULT.INITIAL, + STRATEGY_STAGE_RESULT.INPROGRESS, + STRATEGY_STAGE_RESULT.WAIT]: + self._current_step += 1 + + return self + + def as_dict(self): + """ + Represent the strategy stage as a dictionary + """ + data = dict() + data['id'] = self._id + data['name'] = self._name + data['timeout'] = self._timeout_in_secs + data['inprogress'] = self._inprogress + data['current_step'] = self._current_step + data['total_steps'] = len(self._steps) + data['steps'] = list() + for step in self._steps: + data['steps'].append(step.as_dict()) + data['result'] = self._result + data['result_reason'] = self._result_reason + data['start_date_time'] = self._start_date_time + data['end_date_time'] = self._end_date_time + return data diff --git a/nfv/nfv-common/nfv_common/strategy/_strategy_step.py b/nfv/nfv-common/nfv_common/strategy/_strategy_step.py new file mode 100755 index 00000000..a17f2bfa --- /dev/null +++ b/nfv/nfv-common/nfv_common/strategy/_strategy_step.py @@ -0,0 +1,237 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import weakref + +from nfv_common import debug + +from _strategy_result import STRATEGY_STEP_RESULT + +DLOG = debug.debug_get_logger('nfv_common.strategy.step') + + +class StrategyStep(object): + """ + Strategy Step + """ + def __init__(self, name, force_pass=False, timeout_in_secs=0, max_retries=1): + self._id = 0 + self._name = name + self._force_pass = force_pass + self._timeout_in_secs = timeout_in_secs + self._max_retries = max_retries + self._result = STRATEGY_STEP_RESULT.INITIAL + self._result_reason = '' + self._stage_reference = None + self._start_date_time = '' + self._end_date_time = '' + + @property + def name(self): + """ + Returns the name of the step + """ + return self._name + + @property + def id(self): + """ + Returns the id of the step + """ + return self._id + + @id.setter + def id(self, value): + """ + Sets the id of the step + """ + self._id = value + + @property + def force_pass(self): + """ + Returns the true if force_pass has been set, otherwise false + """ + return self._force_pass + + @property + def max_retries(self): + """ + Returns the maximum retry attempts for step to be completed + """ + return self._max_retries + + @property + def timeout_in_secs(self): + """ + Returns the maximum amount of time to wait for completion + """ + return self._timeout_in_secs + + @property + def result(self): + """ + Returns the result of the step + """ + return self._result + + @result.setter + def result(self, result): + """ + Updates the result of the step + """ + self._result = result + + @property + def result_reason(self): + """ + Returns the reason for the result of the step + """ + return self._result_reason + + @result_reason.setter + def result_reason(self, reason): + """ + Updates the reason for the result of the step + """ + self._result_reason = reason + + @property + def start_date_time(self): + """ + Returns the start date-time of the step + """ + return self._start_date_time + + @start_date_time.setter + def start_date_time(self, date_time_str): + """ + Updates the start date-time of the step + """ + self._start_date_time = date_time_str + + @property + def end_date_time(self): + """ + Returns the end date-time of the step + """ + return self._end_date_time + + @end_date_time.setter + def end_date_time(self, date_time_str): + """ + Updates the end date-time of the step + """ + self._end_date_time = date_time_str + + @property + def strategy(self): + """ + Returns the strategy this step is a member of + """ + if self.phase is not None: + return self.phase.strategy + return None + + @property + def phase(self): + """ + Returns the phase this step is a member of + """ + if self.stage is not None: + return self.stage.phase + return None + + @property + def stage(self): + """ + Returns the stage this step is a member of + """ + if self._stage_reference is not None: + return self._stage_reference() + return None + + @stage.setter + def stage(self, stage_value): + """ + Set the stage that this step is a member of + """ + self._stage_reference = weakref.ref(stage_value) + + def extend_timeout(self, timeout_in_secs): + """ + Allow the step timeout to be extended + """ + DLOG.verbose("Extending strategy step timeout for %s to %s." + % (self._name, timeout_in_secs)) + self._timeout_in_secs = timeout_in_secs + if self._stage_reference is not None: + self.stage.step_extend_timeout() + + def abort(self): + """ + Strategy Step Abort (can be overridden by child class) + """ + DLOG.info("Default strategy step abort for %s." % self._name) + return [] + + def apply(self): + """ + Strategy Step Apply (expected to be overridden by child class) + """ + DLOG.verbose("Default strategy step apply for %s." % self._name) + return STRATEGY_STEP_RESULT.SUCCESS, '' + + def complete(self, result, result_reason): + """ + Strategy Step Completed (can be overridden by child class) + """ + DLOG.verbose("Default strategy step complete for %s, result=%s, " + "reason=%s." % (self._name, result, result_reason)) + return result, result_reason + + def timeout(self): + """ + Strategy Step Timeout (can be overridden by child class) + """ + DLOG.verbose("Default strategy step timeout for %s, timeout=%s secs." + % (self._name, self._timeout_in_secs)) + return STRATEGY_STEP_RESULT.TIMED_OUT, '' + + def handle_event(self, event, event_data=None): + """ + Strategy Step Handle Event (expected to be overridden by child class) + """ + DLOG.verbose("Default strategy step handle event for %s." + % self._name) + return False + + def from_dict(self, data): + """ + Returns a strategy step object initialized using the given dictionary + """ + StrategyStep.__init__(self, data['name'], data['force_pass'], + data['timeout'], data['max_retries']) + self._result = data['result'] + self._result_reason = data['result_reason'] + self._start_date_time = data['start_date_time'] + self._end_date_time = data['end_date_time'] + return self + + def as_dict(self): + """ + Represent the strategy step as a dictionary + """ + data = dict() + data['id'] = self._id + data['name'] = self._name + data['force_pass'] = self._force_pass + data['timeout'] = self._timeout_in_secs + data['max_retries'] = self._max_retries + data['result'] = self._result + data['result_reason'] = self._result_reason + data['start_date_time'] = self._start_date_time + data['end_date_time'] = self._end_date_time + return data diff --git a/nfv/nfv-common/nfv_common/tasks/__init__.py b/nfv/nfv-common/nfv_common/tasks/__init__.py new file mode 100755 index 00000000..4de43fd4 --- /dev/null +++ b/nfv/nfv-common/nfv_common/tasks/__init__.py @@ -0,0 +1,11 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# flake8: noqa +# +from _task import TASK_PRIORITY, Task +from _task_future import TaskFuture +from _task_scheduler import TaskScheduler +from _task_worker_pool import TaskWorkerPool diff --git a/nfv/nfv-common/nfv_common/tasks/_task.py b/nfv/nfv-common/nfv_common/tasks/_task.py new file mode 100755 index 00000000..35f0ac13 --- /dev/null +++ b/nfv/nfv-common/nfv_common/tasks/_task.py @@ -0,0 +1,221 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import six +import collections + +from nfv_common import debug +from nfv_common.helpers import Constants, Constant, Singleton + +from _task_result import TaskResult + +DLOG = debug.debug_get_logger('nfv_common.tasks.task') + + +@six.add_metaclass(Singleton) +class TaskPriority(Constants): + """ + Task Priority Constants + """ + HIGH = 0 + MED = 1 + LOW = 2 + + +# Constant Instantiation +TASK_PRIORITY = TaskPriority() + + +class Task(object): + """ + Task + """ + _READY = Constant('Ready') + _RUNNING = Constant('Running') + _TIMEOUT = Constant('Timeout') + _COMPLETE = Constant('Complete') + _COMPLETED = Constant('Completed') + _ABORTED = Constant('Aborted') + + _id = 1 + + def __init__(self, scheduler, priority, target): + """ + Create a task, scheduler is used to schedule this task and the task + work, the target is a co-routine that is sent task results + """ + self._id = Task._id + self._priority = priority + self._name = target.__name__ + self._scheduler = scheduler + self._started = False + self._target = target + self._work_list = collections.OrderedDict() + DLOG.debug("Task created, id=%s, name=%s." % (self._id, self._name)) + Task._id += 1 + + @property + def id(self): + """ + Returns the unique identifier of the task + """ + return self._id + + @property + def name(self): + """ + Returns the name of the task + """ + return self._name + + @property + def priority(self): + """ + Returns the priority of the task + """ + return self._priority + + def add_timer(self, name, interval_secs): + """ + Add a timer that will fire in so many milliseconds + """ + timer_id = self._scheduler.add_task_timer(name, interval_secs, self) + return timer_id + + def cancel_timer(self, timer_id): + """ + Cancel timer + """ + self._scheduler.cancel_task_timer(timer_id, self) + + def timer_fired(self, timer_id): + """ + Handle timer firing; the timer identifier is sent to + the task's co-routine target + """ + task_result = TaskResult(complete=True, timer_result=True, + result_data=timer_id, + ancillary_result_data=None) + self._target.send(task_result) + self._scheduler.schedule_task(self) + + def add_io_read_wait(self, select_obj): + """ + Add a read selection object to wait on + """ + self._scheduler.add_task_io_read_wait(select_obj, self) + + def cancel_io_read_wait(self, select_obj): + """ + Cancel a read selection object being waited on + """ + self._scheduler.cancel_task_io_read_wait(select_obj, self) + + def add_io_write_wait(self, select_obj): + """ + Add a write selection object to wait on + """ + self._scheduler.add_task_io_write_wait(select_obj, self) + + def cancel_io_write_wait(self, select_obj): + """ + Cancel a write selection object being waited on + """ + self._scheduler.cancel_task_io_write_wait(select_obj, self) + + def io_wait_complete(self, select_obj): + """ + Called when a selection object being waited on has become + readable or writeable; the selection object is sent to + the tasks co-routine target + """ + task_result = TaskResult(complete=True, selobj_result=True, + result_data=select_obj, + ancillary_result_data=None) + self._target.send(task_result) + self._scheduler.schedule_task(self) + + def add_task_work(self, task_work): + """ + Add work to be done by the task + """ + task_work.task_id = self._id + self._work_list[task_work.id] = [Task._READY, task_work] + return task_work + + def task_work_complete(self, task_work): + """ + Task work has been completed, send result to the task target + (results are sent in order the task work was scheduled) + """ + DLOG.verbose("TaskWork complete, name=%s." % task_work.name) + + state, _ = self._work_list[task_work.id] + if Task._TIMEOUT == state: + DLOG.verbose("TaskWork already marked as timed out, ignoring " + "completed result, name=%s." % task_work.name) + self._scheduler.schedule_task(self) + return + + self._work_list[task_work.id] = [Task._COMPLETE, task_work] + + # Following is used to order how the results are sent to the task + # target. It is possible to have many task work outstanding at + # the same time. Results are returned in the order that task + # work was scheduled. + for key, (state, task_work) in self._work_list.items(): + if Task._READY == state: + self._scheduler.schedule_task(self) + break + elif Task._RUNNING == state: + break + elif Task._COMPLETE == state: + self._work_list[task_work.id] = [Task._COMPLETED, task_work] + self._scheduler.schedule_task(self) + if isinstance(task_work.result, Exception): + try: + self._target.throw(task_work.result) + except Exception as e: + if e == task_work.result: + DLOG.info("Task (%s) target did not catch " + "exception, exception=%s." + % (self._name, e)) + else: + raise + else: + task_result = TaskResult( + complete=True, result_data=task_work.result, + ancillary_result_data=task_work.ancillary_result_data) + self._target.send(task_result) + + def task_work_timeout(self, task_work): + """ + Work being done by the task has timed out + """ + DLOG.error("Task(%s) work (%s) timed out, id=%s." + % (self._name, task_work.name, self._id)) + task_result = TaskResult(complete=False, result_data=None, + ancillary_result_data=None) + self._target.send(task_result) + self._work_list[task_work.id] = [Task._TIMEOUT, task_work] + self._scheduler.schedule_task(self) + + def run(self): + """ + Run the task + """ + DLOG.debug("Task(%s) run, id=%s." % (self._name, self._id)) + if not self._started: + self._target.send(None) + self._scheduler.reschedule_task(self) + self._started = True + else: + # Schedule work that is ready + for key, (state, task_work) in self._work_list.items(): + if Task._READY == state: + scheduled = self._scheduler.schedule_task_work(task_work) + if scheduled: + self._work_list[task_work.id] = [Task._RUNNING, + task_work] diff --git a/nfv/nfv-common/nfv_common/tasks/_task_future.py b/nfv/nfv-common/nfv_common/tasks/_task_future.py new file mode 100755 index 00000000..e7828966 --- /dev/null +++ b/nfv/nfv-common/nfv_common/tasks/_task_future.py @@ -0,0 +1,141 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +from nfv_common import debug + +from _task_work import TaskWork + +DLOG = debug.debug_get_logger('nfv_common.tasks.task_future') + + +class TaskFuture(object): + """ + Task Future + """ + def __init__(self, scheduler): + """ + Create a task future + """ + self._scheduler = scheduler + self._result = None + self._timeouts = None + + def set_timeouts(self, timeouts): + """ + Set the timeout values to be used when work is to be done + Parameter timeouts is a dictionary of target and the timeout in seconds + """ + self._timeouts = timeouts + + def work(self, target, *args, **kwargs): + """ + Schedule work in the future + """ + timeout_in_secs = None + if self._timeouts is not None: + # Look for a target specific timeout + module_name = target.__module__.split('.')[-1] + timeout_name = "%s.%s" % (module_name, target.__name__) + timeout_in_secs = self._timeouts.get(timeout_name, None) + if timeout_in_secs is not None: + timeout_in_secs = int(timeout_in_secs) + else: + # Look for a module level timeout + timeout_name = "%s" % module_name + timeout_in_secs = self._timeouts.get(timeout_name, None) + if timeout_in_secs is not None: + timeout_in_secs = int(timeout_in_secs) + + if timeout_in_secs is None: + if kwargs: + timeout_in_secs = kwargs.get('timeout_in_secs', None) + if timeout_in_secs is not None: + del kwargs['timeout_in_secs'] + + if timeout_in_secs is None: + timeout_in_secs = 20 + + elif 0 >= timeout_in_secs: + timeout_in_secs = None # No timeout wanted, wait forever + + if self._scheduler.running_task is not None: + task_work = TaskWork(timeout_in_secs, target, *args, **kwargs) + self._scheduler.running_task.add_task_work(task_work) + self._result = None + return task_work.id + else: + raise LookupError("Running task no longer running") + + def timer(self, name, interval_secs): + """ + Schedule a timer to be fired after so many milliseconds, + callback is a co-routine that is sent the timer identifier + that has fired + """ + if self._scheduler.running_task is not None: + timer_id = self._scheduler.running_task.add_timer(name, + interval_secs) + return timer_id + else: + raise LookupError("Running task no longer running") + + def cancel_timer(self, timer_id): + """ + Cancel a scheduled timer + """ + if self._scheduler.running_task is not None: + self._scheduler.running_task.cancel_timer(timer_id) + else: + raise LookupError("Running task no longer running") + + def io_read_wait(self, select_obj): + """ + Wait on a read selection object + """ + if self._scheduler.running_task is not None: + self._scheduler.running_task.add_io_read_wait(select_obj) + else: + raise LookupError("Running task no longer running") + + def io_read_wait_cancel(self, select_obj): + """ + Cancel a wait on a read selection object + """ + if self._scheduler.running_task is not None: + self._scheduler.running_task.cancel_io_read_wait(select_obj) + else: + raise LookupError("Running task no longer running") + + def io_write_wait(self, select_obj): + """ + Wait on a write selection object + """ + if self._scheduler.running_task is not None: + self._scheduler.running_task.add_io_write_wait(select_obj) + else: + raise LookupError("Running task no longer running") + + def io_write_wait_cancel(self, select_obj): + """ + Cancel a wait on a write selection object + """ + if self._scheduler.running_task is not None: + self._scheduler.running_task.cancel_io_write_wait(select_obj) + else: + raise LookupError("Running task no longer running") + + @property + def result(self): + """ + Returns the result of a future + """ + return self._result + + @result.setter + def result(self, result): + """ + Set the result of a future + """ + self._result = result diff --git a/nfv/nfv-common/nfv_common/tasks/_task_result.py b/nfv/nfv-common/nfv_common/tasks/_task_result.py new file mode 100755 index 00000000..e5ff6e7b --- /dev/null +++ b/nfv/nfv-common/nfv_common/tasks/_task_result.py @@ -0,0 +1,64 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +from nfv_common import debug + +DLOG = debug.debug_get_logger('nfv_common.tasks.task_result') + + +class TaskResult(object): + """ + Task Result + """ + def __init__(self, complete=False, aborted=False, timer_result=False, + selobj_result=False, result_data=None, + ancillary_result_data=None): + """ + Create a task result + """ + self._complete = complete + self._aborted = aborted + self._timer_result = timer_result + self._selobj_result = selobj_result + self._result_data = result_data + self._ancillary_result_data = ancillary_result_data + + @property + def ancillary_data(self): + """ + Returns the ancillary result data + """ + return self._ancillary_result_data + + @property + def data(self): + """ + Returns the result data + """ + return self._result_data + + def is_complete(self): + """ + Indicates if the task result has been completed + """ + return self._complete + + def is_aborted(self): + """ + Indicates if the task result has been aborted + """ + return self._aborted + + def is_timer(self): + """ + Indicates if the task result data is a timer identifier + """ + return self._timer_result + + def is_selobj(self): + """ + Indicates if the task result data is a selection object + """ + return self._selobj_result diff --git a/nfv/nfv-common/nfv_common/tasks/_task_scheduler.py b/nfv/nfv-common/nfv_common/tasks/_task_scheduler.py new file mode 100755 index 00000000..8f225b78 --- /dev/null +++ b/nfv/nfv-common/nfv_common/tasks/_task_scheduler.py @@ -0,0 +1,396 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import inspect +import collections + +from nfv_common import debug +from nfv_common import timers +from nfv_common import selobj +from nfv_common import selectable +from nfv_common import histogram +from nfv_common.helpers import coroutine + +from _task import Task, TASK_PRIORITY +from _task_future import TaskFuture + +DLOG = debug.debug_get_logger('nfv_common.tasks.task_scheduler') + + +class TaskScheduler(object): + """ + Task Scheduler + """ + def __init__(self, name, task_worker_pool): + """ + Create a task scheduler + """ + self._name = name + self._task_worker_pool = task_worker_pool + self._workers_selobj = dict() + self._workers_timer = dict() + self._tasks = dict() + self._task_timers = dict() + self._task_work_timers = dict() + self._task_read_selobjs = dict() + self._task_write_selobjs = dict() + self._running_task = None + self._tasks_scheduled = False + self._wait_queue = collections.deque() + self._ready_queue = list() + self._ready_dequeues = list() + for _ in TASK_PRIORITY: + self._ready_queue.append(collections.deque()) + self._ready_dequeues.append(0) + self._run_queue = selectable.MultiprocessQueue() + selobj.selobj_add_read_obj(self._run_queue.selobj, self.run_tasks) + + @property + def name(self): + """ + Returns the name of the scheduler + """ + return self._name + + @property + def running_task(self): + """ + Returns the running task + """ + return self._running_task + + def add_task(self, priority, target, *args, **kwargs): + """ + Add a task to the task scheduler + """ + if inspect.isgeneratorfunction(target): + future = TaskFuture(self) + task = Task(self, priority, target(future, *args, **kwargs)) + DLOG.debug("Pool %s: Add Task, name=%s." + % (self._task_worker_pool.name, task.name)) + self.schedule_task(task) + result = task.id + else: + result = target(*args, **kwargs) + return result + + def delete_task(self, task): + """ + Delete a task from the task scheduler + """ + DLOG.debug("Pool %s: Delete Task, name=%s." + % (self._task_worker_pool.name, task.name)) + for timer_id, timer_owner in self._task_timers.items(): + if timer_owner.id == task.id: + timers.timers_delete_timer(timer_id) + del self._task_timers[timer_id] + + for timer_id, timer_owner in self._task_work_timers.items(): + if timer_owner.task_id == task.id: + timers.timers_delete_timer(timer_id) + del self._task_work_timers[timer_id] + + for select_obj, select_obj_owner in self._task_read_selobjs.items(): + if select_obj_owner.id == task.id: + selobj.selobj_del_read_obj(select_obj) + del self._task_read_selobjs[select_obj] + + for select_obj, select_obj_owner in self._task_write_selobjs.items(): + if select_obj_owner.id == task.id: + selobj.selobj_del_write_obj(select_obj) + del self._task_write_selobjs[select_obj] + + del self._tasks[task.id] + + def add_task_timer(self, name, interval_secs, task): + """ + Add timer for a task + """ + timer_id = timers.timers_create_timer(name, interval_secs, + interval_secs, + self.task_timer_timeout) + self._task_timers[timer_id] = task + return timer_id + + def cancel_task_timer(self, timer_id, task): + timer_owner = self._task_timers.get(timer_id, None) + if timer_owner is not None: + if timer_owner.id == task.id: + timers.timers_delete_timer(timer_id) + del self._task_timers[timer_id] + + @coroutine + def task_timer_timeout(self): + """ + Called when a task timer has fired + """ + while True: + timer_id = (yield) + task = self._task_timers.get(timer_id, None) + if task is None: + break + try: + task.timer_fired(timer_id) + except StopIteration: + self.delete_task(task) + break + + def add_task_io_read_wait(self, select_obj, task): + """ + Add a task read selection object to wait on + """ + selobj.selobj_add_read_obj(select_obj, self.task_io_wait_complete) + self._task_read_selobjs[select_obj] = task + + def cancel_task_io_read_wait(self, select_obj, task): + """ + Cancel a task read selection object being waited on + """ + select_obj_owner = self._task_read_selobjs.get(select_obj, None) + if select_obj_owner is not None: + if select_obj_owner.id == task.id: + selobj.selobj_del_read_obj(select_obj) + del self._task_read_selobjs[select_obj] + + def add_io_write_wait(self, select_obj, task): + """ + Add a task write selection object to wait on + """ + selobj.selobj_add_write_obj(select_obj, self.task_io_wait_complete) + self._task_write_selobjs[select_obj] = task + + def cancel_io_write_wait(self, select_obj, task): + """ + Cancel a task write selection object being waited on + """ + select_obj_owner = self._task_write_selobjs.get(select_obj, None) + if select_obj_owner is not None: + if select_obj_owner.id == task.id: + selobj.selobj_del_write_obj(select_obj) + del self._task_write_selobjs[select_obj] + + @coroutine + def task_io_wait_complete(self): + """ + Called when a task selection object being waited on has become + readable or writeable + """ + while True: + select_obj = (yield) + task = self._task_read_selobjs.get(select_obj, None) + if task is None: + task = self._task_write_selobjs.get(select_obj, None) + if task is None: + break + try: + task.io_wait_complete(select_obj) + except StopIteration: + self.delete_task(task) + break + + def _schedule_next_task(self): + """ + Schedule next task + """ + task_id = None + + if self._ready_dequeues[TASK_PRIORITY.HIGH] >= 60: + self._ready_dequeues[TASK_PRIORITY.HIGH] = 0 + + if self._ready_dequeues[TASK_PRIORITY.MED] >= 60: + self._ready_dequeues[TASK_PRIORITY.MED] = 0 + if 0 < len(self._ready_queue[TASK_PRIORITY.LOW]): + task_id = self._ready_queue[TASK_PRIORITY.LOW].pop() + else: + if 0 < len(self._ready_queue[TASK_PRIORITY.MED]): + task_id = self._ready_queue[TASK_PRIORITY.MED].pop() + + elif 0 < len(self._ready_queue[TASK_PRIORITY.LOW]): + task_id = self._ready_queue[TASK_PRIORITY.LOW].pop() + self._ready_dequeues[TASK_PRIORITY.MED] = 0 + + if task_id is None: + if 0 < len(self._ready_queue[TASK_PRIORITY.HIGH]): + task_id = self._ready_queue[TASK_PRIORITY.HIGH].pop() + self._ready_dequeues[TASK_PRIORITY.HIGH] += 1 + + elif 0 < len(self._ready_queue[TASK_PRIORITY.MED]): + task_id = self._ready_queue[TASK_PRIORITY.MED].pop() + self._ready_dequeues[TASK_PRIORITY.HIGH] = 0 + self._ready_dequeues[TASK_PRIORITY.MED] += 1 + + elif 0 < len(self._ready_queue[TASK_PRIORITY.LOW]): + task_id = self._ready_queue[TASK_PRIORITY.LOW].pop() + self._ready_dequeues[TASK_PRIORITY.HIGH] = 0 + self._ready_dequeues[TASK_PRIORITY.MED] = 0 + self._ready_dequeues[TASK_PRIORITY.LOW] += 1 + + if task_id is not None: + self._run_queue.put(task_id) + self._tasks_scheduled = True + + def _schedule_task(self, task, reschedule=False): + """ + Schedule or Reschedule a task + """ + DLOG.verbose("Pool %s: Scheduling Task, name=%s." + % (self._task_worker_pool.name, task.name)) + self._tasks[task.id] = task + + for pri in TASK_PRIORITY: + if task.id in self._ready_queue[pri]: + break + else: + if reschedule: + self._ready_queue[task.priority].append(task.id) + else: + self._ready_queue[task.priority].appendleft(task.id) + + histogram.add_histogram_data(self._name + + ' [tasks-queue-p%i]' % task.priority, + len(self._ready_queue[task.priority]), + "ready-tasks") + + if not self._tasks_scheduled: + self._schedule_next_task() + + def reschedule_task(self, task): + """ + Reschedule a task + """ + self._schedule_task(task, reschedule=True) + + def schedule_task(self, task): + """ + Schedule a task + """ + self._schedule_task(task) + + def schedule_task_work(self, task_work=None): + """ + Schedule task work to one of the task workers if available + """ + if task_work is not None: + self._wait_queue.appendleft(task_work) + + if 0 == len(self._wait_queue): + return False + + worker = self._task_worker_pool.claim_worker() + if worker is not None: + task_work = self._wait_queue.pop() + + DLOG.verbose("Pool %s: Task worker available to run TaskWork, " + "name=%s." % (self._task_worker_pool.name, + task_work.name)) + + selobj.selobj_add_read_obj(worker.selobj, self.task_work_complete) + self._workers_selobj[worker.selobj] = worker + worker.submit_task_work(task_work) + + if task_work.timeout_in_secs is not None: + timer_id = timers.timers_create_timer(task_work.name, + task_work.timeout_in_secs, + task_work.timeout_in_secs, + self.task_work_timeout) + self._task_work_timers[timer_id] = task_work + self._workers_timer[timer_id] = worker + return True + else: + DLOG.verbose("Pool %s: No task worker available to run TaskWork." + % self._task_worker_pool.name) + return False + + @coroutine + def task_work_complete(self): + """ + A task worker has completed it's assigned work + """ + while True: + select_obj = (yield) + worker = self._workers_selobj.get(select_obj, None) + if worker is not None: + self._task_worker_pool.release_worker(worker) + selobj.selobj_del_read_obj(worker.selobj) + del self._workers_selobj[worker.selobj] + + task_work = worker.get_task_work_result() + if task_work is not None: + for timer_id, timer_owner in self._task_work_timers.items(): + if timer_owner.id == task_work.id: + timers.timers_delete_timer(timer_id) + del self._task_work_timers[timer_id] + del self._workers_timer[timer_id] + + task = self._tasks.get(task_work.task_id, None) + if task is not None: + self._running_task = task + try: + task.task_work_complete(task_work) + except StopIteration: + self.delete_task(task) + + if self._task_worker_pool.available_workers(): + self.schedule_task_work() + + @coroutine + def task_work_timeout(self): + """ + Work being done by the task has timed out + """ + timer_id = (yield) + worker = self._workers_timer.get(timer_id, None) + if worker is not None: + self._task_worker_pool.timeout_worker(worker) + selobj.selobj_del_read_obj(worker.selobj) + del self._workers_selobj[worker.selobj] + del self._workers_timer[timer_id] + + task_work = self._task_work_timers.get(timer_id, None) + if task_work is not None: + task = self._tasks.get(task_work.task_id, None) + if task is not None: + try: + task.task_work_timeout(task_work) + del self._task_work_timers[timer_id] + except StopIteration: + self.delete_task(task) + + if self._task_worker_pool.available_workers(): + self.schedule_task_work() + + @coroutine + def run_tasks(self): + """ + Run tasks that are ready to run + """ + while True: + select_obj = (yield) + if select_obj == self._run_queue.selobj: + self._tasks_scheduled = False + task_id = self._run_queue.get() + if self._tasks: + DLOG.verbose("Pool %s: Total tasks=%s." + % (self._task_worker_pool.name, + len(self._tasks))) + self._running_task = self._tasks.get(task_id, None) + if self._running_task is not None: + try: + DLOG.verbose("Pool %s: Running task, name=%s." + % (self._task_worker_pool.name, + self._running_task.name)) + self._running_task.run() + + except StopIteration: + self.delete_task(self._running_task) + + finally: + self._running_task = None + + self._schedule_next_task() + + else: + DLOG.verbose("Pool %s: No tasks to schedule." + % self._task_worker_pool.name) diff --git a/nfv/nfv-common/nfv_common/tasks/_task_work.py b/nfv/nfv-common/nfv_common/tasks/_task_work.py new file mode 100755 index 00000000..0a35e964 --- /dev/null +++ b/nfv/nfv-common/nfv_common/tasks/_task_work.py @@ -0,0 +1,113 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +from nfv_common import debug +from nfv_common import timers +from nfv_common import exceptions +from nfv_common.helpers import Result + +DLOG = debug.debug_get_logger('nfv_common.tasks.task_work') + + +class TaskWork(object): + """ + Task Work + """ + _id = 0 + + def __init__(self, timeout, target, *args, **kwargs): + """ + Create task work + """ + self._id = TaskWork._id + self._name = target.__name__ + self._task_id = None + self._target = target + self._timeout_in_secs = timeout + self._args = list(args) + self._kwargs = dict(kwargs) + self._result = None + self._ancillary_result_data = None + self._create_timestamp_ms = timers.get_monotonic_timestamp_in_ms() + + DLOG.debug("TaskWork created, id=%s, name=%s, timeout_in_secs=%i." + % (self._id, self._name, self._timeout_in_secs)) + TaskWork._id += 1 + + @property + def name(self): + """ + Returns the name of the task work + """ + return self._name + + @property + def id(self): + """ + Returns the unique identifier of the task work + """ + return self._id + + @property + def task_id(self): + """ + Returns the task identifier that owns this task work + """ + return self._task_id + + @task_id.setter + def task_id(self, task_id): + """ + Set the task identifier that owns this task work + """ + self._task_id = task_id + + @property + def create_timestamp_ms(self): + """ + Returns the creation timestamp in milliseconds + """ + return self._create_timestamp_ms + + @property + def timeout_in_secs(self): + """ + Returns the maximum timeout in seconds that the task work should + take to run + """ + return self._timeout_in_secs + + @property + def ancillary_result_data(self): + """ + Returns the ancillary result data for the task work + """ + return self._ancillary_result_data + + @property + def result(self): + """ + Returns the result of the task work + """ + return self._result + + def run(self): + """ + Runs the task work + """ + DLOG.debug("TaskWork run, id=%s, name=%s." % (self._id, self._name)) + try: + result = self._target(*self._args, **self._kwargs) + if isinstance(result, Result): + self._result = result.result_data + self._ancillary_result_data = result.ancillary_data + else: + self._result = result + + except Exception as e: + if isinstance(e, exceptions.PickleableException): + self._result = e + else: + self._result = Exception(e.__class__.__name__ + ": " + str(e)) diff --git a/nfv/nfv-common/nfv_common/tasks/_task_worker.py b/nfv/nfv-common/nfv_common/tasks/_task_worker.py new file mode 100755 index 00000000..df87655d --- /dev/null +++ b/nfv/nfv-common/nfv_common/tasks/_task_worker.py @@ -0,0 +1,96 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +from nfv_common import thread +from nfv_common import histogram +from nfv_common import timers +from nfv_common import debug + +DLOG = debug.debug_get_logger('nfv_common.tasks.task_worker') + + +class TaskWorker(thread.ThreadWorker): + """ + Task Worker + """ + def __init__(self, name): + super(TaskWorker, self).__init__(name) + + def initialize(self): + """ + Initialize the Task Worker + """ + return + + def finalize(self): + """ + Finalize the Task Worker + """ + return + + def do_work(self, action, work): + """ + Do work given to the Task Worker + """ + if TaskWorkerThread.ACTION_DO_WORK == action: + if work is not None: + work.run() + self.send_result(work) + + +class TaskWorkerThread(thread.Thread): + """ + Task Worker Thread + """ + ACTION_DO_WORK = "thread-do-work" + _id = 1 + + def __init__(self, name): + """ + Create a task worker + """ + self._id = TaskWorkerThread._id + self._name = name + self._worker = TaskWorker(self._name) + super(TaskWorkerThread, self).__init__(self._name, self._worker) + TaskWorkerThread._id += 1 + + @property + def id(self): + """ + Returns a unique identifier for this task worker + """ + return self._id + + @property + def name(self): + """ + Returns the name for this task worker + """ + return self._name + + def submit_task_work(self, task_work): + """ + Submit task work for this task worker to execute + """ + self.send_work(TaskWorkerThread.ACTION_DO_WORK, task_work) + + def get_task_work_result(self): + """ + Returns the result of task work completed + """ + result = self._worker.get_result() + + if hasattr(result.ancillary_result_data, 'execution_time'): + histogram.add_histogram_data( + result.name + ' [worker-execution-time]', + result.ancillary_result_data.execution_time, 'secs') + + now_ms = timers.get_monotonic_timestamp_in_ms() + elapsed_secs = (now_ms - result.create_timestamp_ms) / 1000 + histogram.add_histogram_data(result.name + ' [execution-time]', + elapsed_secs, 'secs') + + return result diff --git a/nfv/nfv-common/nfv_common/tasks/_task_worker_pool.py b/nfv/nfv-common/nfv_common/tasks/_task_worker_pool.py new file mode 100755 index 00000000..78bc559a --- /dev/null +++ b/nfv/nfv-common/nfv_common/tasks/_task_worker_pool.py @@ -0,0 +1,87 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import collections + +from nfv_common import debug + +from _task_worker import TaskWorkerThread + +DLOG = debug.debug_get_logger('nfv_common.tasks.task_worker_pool') + + +class TaskWorkerPool(object): + """ + Task Worker Pool + """ + def __init__(self, pool_name, num_workers=1): + """ + Create Task Worker Pool + """ + self._pool_name = pool_name + self._workers_avail = collections.OrderedDict() + self._workers = list() + + for worker_x in range(num_workers): + worker = TaskWorkerThread("%s-Worker-%s" % (pool_name, worker_x)) + self._workers.append(worker) + + for worker in self._workers: + worker.start() + self._workers_avail[worker.id] = worker + + @property + def name(self): + """ + Returns the pool name + """ + return self._pool_name + + def available_workers(self): + """ + Returns true if there are workers available to do work + """ + if self._workers_avail: + return True + return False + + def claim_worker(self): + """ + Claims a worker, returns a worker if available or None otherwise + """ + if self._workers_avail: + _, worker = self._workers_avail.popitem() + DLOG.verbose("Claim worker %s" % worker.name) + return worker + return None + + def release_worker(self, worker, timeout=False): + """ + Release a worker back into the pool + """ + if worker is not None: + DLOG.verbose("Release worker %s" % worker.name) + self._workers_avail[worker.id] = worker + + def timeout_worker(self, worker): + """ + Timeout a worker + """ + if worker is not None: + DLOG.info("Timeout worker %s" % worker.name) + worker.stop(max_wait_in_seconds=1) + new_worker = TaskWorkerThread(worker.name) + new_worker.start() + self._workers = [x for x in self._workers if x.id == worker.id] + self._workers.append(new_worker) + self._workers_avail[new_worker.id] = new_worker + del worker + + def shutdown(self): + """ + Shutdown the pool of workers + """ + for worker in self._workers: + worker.stop(max_wait_in_seconds=1) diff --git a/nfv/nfv-common/nfv_common/tasks/unit_test.py b/nfv/nfv-common/nfv_common/tasks/unit_test.py new file mode 100755 index 00000000..9e7abc7f --- /dev/null +++ b/nfv/nfv-common/nfv_common/tasks/unit_test.py @@ -0,0 +1,138 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# flake8: noqa +# +import six + +from nfv_common import debug +from nfv_common import config +from nfv_common import selobj +from nfv_common import timers +from nfv_common.helpers import coroutine + +from _task_worker_pool import TaskWorkerPool +from _task_scheduler import TaskScheduler + +DLOG = debug.debug_get_logger('unit_test', debug_level=debug.DEBUG_LEVEL.INFO) + +_test_complete = False +_test_result = None + + +def unit_test(title): + def unit_test_wrapper(func): + def func_wrapper(*args, **kwargs): + try: + global _test_complete, _test_result + _test_complete = False + _test_result = None + six.print_("%-40s: " % title, end='') + result = func(*args, **kwargs) + _test_result = result + while not _test_complete: + selobj.selobj_dispatch(500) + timers.timers_schedule() + if _test_result: + six.print_("PASSED", end='\n') + else: + six.print_("%s FAILED", end='\n') + except Exception as e: + DLOG.exception("%s" % e) + six.print_("%s FAILED", end='\n') + return func_wrapper + return unit_test_wrapper + + +def _task_non_coroutine(arg1): + global _test_complete + _test_complete = True + assert(arg1 == 'arg1') + return True + + +def _task_work_func(arg1, arg2): + assert(arg1 == 'arg1') + assert(arg2 == 'arg2') + return "FUNCTION PASSED" + + +@coroutine +def _task_coroutine_callback(): + global _test_complete, _test_result + result = (yield) + assert(result == "FUNCTION PASSED") + _test_complete = True + _test_result = True + + +def _task_coroutine(future, arg1, callback): + assert(arg1 == 'arg1') + future.work(_task_work_func, 'arg1', 'arg2') + future.result = (yield) + if future.result.is_complete(): + callback.send(future.result.data) + else: + callback.send(None) + + +def _task_coroutine_with_timer(future, arg1, callback): + assert(arg1 == 'arg1') + timer_id = future.timer('timer-test', 2) + start_ms = timers.get_monotonic_timestamp_in_ms() + future.result = (yield) + end_ms = timers.get_monotonic_timestamp_in_ms() + if future.result.is_complete(): + if future.result.is_timer: + if future.result.data == timer_id: + elapsed_secs = (end_ms - start_ms) / 1000 + if 2 < elapsed_secs: + callback.send("FUNCTION PASSED") + return + callback.send(None) + + +class UnitTest(object): + def __init__(self): + self._task_worker_pool = TaskWorkerPool('test-pool', num_workers=1) + self._scheduler = TaskScheduler('test-scheduler', self._task_worker_pool) + + @unit_test('NORMAL_FUNCTION_CALL') + def test_normal_function_call(self): + result = self._scheduler.add_task(_task_non_coroutine, 'arg1') + return result + + @unit_test('CO-ROUTINE_FUNCTION_CALL') + def test_coroutine_function_call(self): + self._scheduler.add_task(_task_coroutine, 'arg1', + callback=_task_coroutine_callback()) + return _test_result + + @unit_test('CO-ROUTINE_FUNCTION_TIMER_CALL') + def test_coroutine_timer_function_call(self): + self._scheduler.add_task(_task_coroutine_with_timer, 'arg1', + callback=_task_coroutine_callback()) + return _test_result + + def run(self): + six.print_("TASKS UNIT TESTS", end='\n') + six.print_("================", end='\n') + self.test_normal_function_call() + self.test_coroutine_function_call() + self.test_coroutine_timer_function_call() + + +if __name__ == '__main__': + + debug.debug_initialize(config.CONF['debug']) + selobj.selobj_initialize() + timers.timers_initialize(500, 1000, 1000) + + unit_test = UnitTest() + unit_test.run() + + timers.timers_finalize() + selobj.selobj_finalize() + debug.debug_finalize() \ No newline at end of file diff --git a/nfv/nfv-common/nfv_common/tcp/__init__.py b/nfv/nfv-common/nfv_common/tcp/__init__.py new file mode 100755 index 00000000..f2824d31 --- /dev/null +++ b/nfv/nfv-common/nfv_common/tcp/__init__.py @@ -0,0 +1,9 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# flake8: noqa +# +from _tcp_connection import TCPConnection +from _tcp_server import TCPServer diff --git a/nfv/nfv-common/nfv_common/tcp/_tcp_connection.py b/nfv/nfv-common/nfv_common/tcp/_tcp_connection.py new file mode 100755 index 00000000..353dd430 --- /dev/null +++ b/nfv/nfv-common/nfv_common/tcp/_tcp_connection.py @@ -0,0 +1,268 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import hmac +import hashlib +import base64 + +import struct +import socket +import select +import errno + +from nfv_common import debug +from nfv_common import timers + +DLOG = debug.debug_get_logger('nfv_common.tcp') + + +class TCPConnection(object): + """ + TCP Connection + """ + AUTH_VECTOR_MAX_SIZE = 64 + + def __init__(self, ip, port, sock=None, blocking=True, owner=None, + auth_key=None): + """ + Create a TCP connection + """ + self._owner = owner + self._auth_key = auth_key + self._ip = ip + self._port = port + if sock is None: + result = None + for family in (socket.AF_INET6, socket.AF_INET): + try: + result = socket.getaddrinfo(ip, None, family, + socket.SOCK_STREAM) + break + except socket.error: + continue + + if result is None or 0 == len(result): + raise ValueError("Unable to get address information for %s." % ip) + + family, sock_type, protocol, canonical_name, socket_address = result[0] + + self._socket = socket.socket(family, socket.SOCK_STREAM) + self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self._socket.bind((ip, int(port))) + else: + self._socket = sock + + self._blocking = blocking + self._socket.setblocking(blocking) + self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) + self._msg_parts = list() + self._msg_len = 0 + self._msg_len_remaining = -1 + + @property + def ip(self): + """ + Returns the ip of the connection + """ + return self._ip + + @property + def port(self): + """ + Returns the port of the connection + """ + return self._port + + @property + def sock(self): + """ + Returns the socket object of the connection + """ + return self._socket + + @property + def selobj(self): + """ + Returns the selection object associated with the connection + """ + return self._socket.fileno() + + def is_shutdown(self): + """ + Returns true if the connection has shutdown + """ + return self._socket is None + + def connect(self, ip, port, timeout_in_secs=None): + """ + Connect to an end-point + """ + try: + if timeout_in_secs is not None: + self._socket.settimeout(timeout_in_secs) + + self._socket.connect((ip, int(port))) + + if self._blocking: + self._socket.settimeout(None) + + self._socket.setblocking(self._blocking) + + except socket.error as e: + DLOG.error("Connect to end-point failed, ip=%s, port=%s, error=%s." + % (ip, port, e)) + self.close() + raise + + def send(self, payload): + """ + Send a message into the TCP connection, assumes the following + messaging format: | length (4-bytes) | string of bytes | + """ + bytes_sent = 0 + + if self._socket is not None: + if self._auth_key is None: + msg = struct.pack('!L', socket.htonl(len(payload))) + msg += payload + else: + auth_vector = hmac.new(self._auth_key, msg=payload, + digestmod=hashlib.sha512).digest() + msg_len = len(auth_vector) + len(payload) + msg = struct.pack('!L', socket.htonl(msg_len)) + msg += auth_vector[:self.AUTH_VECTOR_MAX_SIZE] + msg += payload + + bytes_sent = self._socket.send(bytes(msg)) + return bytes_sent + + def _receive_non_blocking(self): + """ + Receive a message from the TCP connection (non-blocking), assumes the + following messaging format: | length (4-bytes) | string of bytes | + """ + if self._socket is None: + return None + + message = None + self._socket.setblocking(False) + try: + if -1 == self._msg_len_remaining: + if 0 == self._msg_len: + read_len = struct.calcsize('!L') + else: + read_len = struct.calcsize('!L') - self._msg_len + + msg_block = self._socket.recv(read_len) + if 0 == len(msg_block): + DLOG.verbose("Connection closed.") + self.close() + else: + self._msg_parts.append(msg_block) + msg = b"".join(self._msg_parts) + self._msg_len = len(msg_block) + + if struct.calcsize('!L') == len(msg): + self._msg_parts[:] = list() + self._msg_len = socket.ntohl(struct.unpack('!L', msg)[0]) + self._msg_len_remaining = self._msg_len + + else: + msg_block = self._socket.recv(self._msg_len_remaining) + if 0 == len(msg_block): + DLOG.verbose("Connection closed.") + self.close() + else: + self._msg_parts.append(msg_block) + self._msg_len_remaining -= len(msg_block) + if 0 == self._msg_len_remaining: + msg = b"".join(self._msg_parts) + self._msg_parts[:] = list() + self._msg_len = 0 + self._msg_len_remaining = -1 + + if self._auth_key is None: + message = msg + else: + auth_vector = msg[:self.AUTH_VECTOR_MAX_SIZE] + message = msg[self.AUTH_VECTOR_MAX_SIZE:] + expected = hmac.new(self._auth_key, msg=message, + digestmod=hashlib.sha512).digest() + + if auth_vector != expected: + auth_vector_str = base64.b64encode(auth_vector) + expected_str = base64.b64encode(expected) + + DLOG.info("Authorization vector mismatch, msg=%s, " + "auth_vector=%s, expected=%s." + % (message, auth_vector_str, + expected_str)) + message = None + + except socket.timeout as e: + DLOG.info("TCP socket timeout, ip=%s, por=%s, error=%s." + % (self._ip, self._port, e)) + + except socket.error as e: + DLOG.error("TCP socket error, ip=%s, port=%s, error=%s." + % (self._ip, self._port, e)) + self.close() + + finally: + if self._socket is not None: + self._socket.setblocking(self._blocking) + + return message + + def _receive_blocking(self, timeout_in_secs=5): + """ + Receive a message from the TCP connection (blocking) + """ + start_ms = timers.get_monotonic_timestamp_in_ms() + + while self._socket is not None: + read_objs = [self._socket.fileno()] + try: + readable, writeable, in_error \ + = select.select(read_objs, [], [], timeout_in_secs) + + for selobj in readable: + if selobj == self._socket.fileno(): + msg = self._receive_non_blocking() + if msg is not None: + return msg + + except (OSError, socket.error, select.error) as e: + if errno.EINTR != e.args[0]: + pass + + now_ms = timers.get_monotonic_timestamp_in_ms() + secs_expired = (now_ms - start_ms) / 1000 + if timeout_in_secs <= secs_expired: + DLOG.info("Timed out waiting for a message.") + break + else: + timeout_in_secs -= secs_expired + + return None + + def receive(self, blocking=True, timeout_in_secs=5): + """ + Receive a message from the TCP connection + """ + if blocking: + return self._receive_blocking(timeout_in_secs) + else: + return self._receive_non_blocking() + + def close(self): + """ + Close the TCP connection + """ + if self._socket is not None: + if self._owner is not None: + self._owner.closing_connection(self.selobj) + self._socket.close() + self._socket = None diff --git a/nfv/nfv-common/nfv_common/tcp/_tcp_server.py b/nfv/nfv-common/nfv_common/tcp/_tcp_server.py new file mode 100755 index 00000000..9971fb49 --- /dev/null +++ b/nfv/nfv-common/nfv_common/tcp/_tcp_server.py @@ -0,0 +1,118 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +from nfv_common import debug +from nfv_common import selobj +from nfv_common.helpers import coroutine + +from _tcp_connection import TCPConnection + +DLOG = debug.debug_get_logger('nfv_common.tcp') + + +class TCPServer(object): + """ + TCP Server + """ + def __init__(self, ip, port, msg_handler, max_connections=5, auth_key=None): + """ + Create a TCP Server + """ + self._auth_key = auth_key + self._connection = TCPConnection(ip, port) + self._socket = self._connection.sock + self._socket.listen(max_connections) + selobj.selobj_add_read_obj(self._connection.selobj, self.dispatch) + self._client_connections = dict() + self._message_handler = msg_handler + + @coroutine + def selobj_error_callback(self): + while True: + select_obj = (yield) + client_connection = self._client_connections.get(select_obj, None) + if client_connection is not None: + selobj.selobj_del_read_obj(select_obj) + del self._client_connections[select_obj] + DLOG.info("Client connection error, from %s, port=%s." + % (client_connection.ip, client_connection.port)) + selobj.selobj_del_error_callback(select_obj) + + @coroutine + def dispatch(self): + while True: + select_obj = (yield) + if select_obj == self._connection.selobj: + # Client Connect + client_socket, client_address = self._socket.accept() + client_ip = client_address[0] + client_port = client_address[1] + + client_connection = TCPConnection(client_ip, client_port, + client_socket, False, self, + self._auth_key) + selobj.selobj_add_read_obj(client_connection.selobj, + self.dispatch) + selobj.selobj_add_error_callback(client_connection.selobj, + self.selobj_error_callback) + self._client_connections[client_connection.selobj] \ + = client_connection + + DLOG.verbose("Client connected from %s, port=%s." + % (client_ip, client_port)) + else: + # Client Data + client_connection = self._client_connections.get(select_obj, + None) + if client_connection is not None: + msg = client_connection.receive(blocking=False) + if msg is not None: + DLOG.verbose("Message received from %s, port=%s, " + "select_obj=%s." % (client_connection.ip, + client_connection.port, + select_obj)) + self._message_handler(client_connection, msg) + + client_connection = self._client_connections.get(select_obj, + None) + if client_connection is not None: + if client_connection.is_shutdown(): + selobj.selobj_del_read_obj(select_obj) + selobj.selobj_del_error_callback(select_obj) + del self._client_connections[select_obj] + DLOG.verbose("Client connection closed, ip=%s, port=%s, " + "select_obj=%s." % (client_connection.ip, + client_connection.port, + select_obj)) + else: + selobj.selobj_del_read_obj(select_obj) + selobj.selobj_del_error_callback(select_obj) + + def closing_connection(self, select_obj): + """ + Connection is about to be closed + """ + client_connection = self._client_connections.get(select_obj, None) + if client_connection is not None: + selobj.selobj_del_read_obj(select_obj) + selobj.selobj_del_error_callback(select_obj) + del self._client_connections[select_obj] + DLOG.verbose("Client connection closing, ip=%s, port=%s, " + "select_obj=%s." % (client_connection.ip, + client_connection.port, + select_obj)) + + def shutdown(self): + """ + Shutdown the TCP Server + """ + connections = self._client_connections.copy() + for client_connection in connections.itervalues(): + selobj.selobj_del_read_obj(client_connection.selobj) + selobj.selobj_del_error_callback(client_connection.selobj) + client_connection.close() + + selobj.selobj_del_read_obj(self._connection.selobj) + self._connection.close() diff --git a/nfv/nfv-common/nfv_common/tcp/unit_test.py b/nfv/nfv-common/nfv_common/tcp/unit_test.py new file mode 100755 index 00000000..3fad733c --- /dev/null +++ b/nfv/nfv-common/nfv_common/tcp/unit_test.py @@ -0,0 +1,57 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# flake8: noqa +# +import time +import argparse + +from nfv_common import debug +from nfv_common import selobj +from nfv_common import timers + +from _tcp_server import TCPServer +from _tcp_connection import TCPConnection + + +def message_handler(client_connection, msg): + print("Received Message: %s" % msg) + + +if __name__ == '__main__': + + CONF = dict() + CONF['debug'] = dict() + CONF['debug']['config_file'] = '/etc/nfv/vim/debug.ini' + CONF['debug']['handlers'] = 'stdout' + + debug.debug_initialize(CONF['debug']) + selobj.selobj_initialize() + timers.timers_initialize(500, 1000, 1000) + + parser = argparse.ArgumentParser() + parser.add_argument('-s', '--server', help='server-side', + action="store_true") + parser.add_argument('-c', '--client', help='client-side', + action="store_true") + args = parser.parse_args() + + if args.server: + tcp_server = TCPServer('127.0.0.1', '3201', message_handler) + + while True: + selobj.selobj_dispatch(5000) + + else: + tcp_connection = TCPConnection('127.0.0.1', '3202') + tcp_connection.connect('127.0.0.1', '3201') + + while True: + tcp_connection.send("HI") + time.sleep(5) + + timers.timers_finalize() + selobj.selobj_finalize() + debug.debug_finalize() diff --git a/nfv/nfv-common/nfv_common/thread/__init__.py b/nfv/nfv-common/nfv_common/thread/__init__.py new file mode 100755 index 00000000..006cd625 --- /dev/null +++ b/nfv/nfv-common/nfv_common/thread/__init__.py @@ -0,0 +1,9 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# flake8: noqa +# +from _thread import Thread +from _thread_worker import ThreadWorker diff --git a/nfv/nfv-common/nfv_common/thread/_thread.py b/nfv/nfv-common/nfv_common/thread/_thread.py new file mode 100755 index 00000000..517774c3 --- /dev/null +++ b/nfv/nfv-common/nfv_common/thread/_thread.py @@ -0,0 +1,237 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import sys +import ctypes +import signal +from multiprocessing import Process + +from nfv_common import debug +from nfv_common import selobj +from nfv_common import timers +from nfv_common import selectable +from nfv_common.helpers import coroutine + +from _thread_progress_marker import ThreadProgressMarker + +DLOG = debug.debug_get_logger('nfv_common.thread') + + +class ThreadState(object): + """ + Thread State + """ + def __init__(self): + self.stay_on = True + self.debug_reload = False + + +class Thread(object): + """ + Thread + """ + ACTION_DEBUG_CONFIG_RELOAD = "thread-debug-config-reload" + ACTION_STOP = "thread-stop" + + def __init__(self, name, thread_worker, check_interval_in_secs=30): + """ + Create thread + """ + self._name = name + self._work_queue = selectable.MultiprocessQueue() + self._thread_worker = thread_worker + self._progress_marker = ThreadProgressMarker() + self._process = Process(target=_thread_main, + args=(self._name, self._progress_marker, + debug.debug_get_config(), + thread_worker, self._work_queue), + name=self._name) + self._process.daemon = True + self._check_timer_id = None + self._check_interval_in_secs = check_interval_in_secs + self._last_marker_value = None + self._stall_timestamp_ms = None + debug.debug_register_config_change_callback(self.debug_config_change) + + @property + def name(self): + """ + Return the name of the thread + """ + return self._name + + @property + def selobj(self): + """ + Returns the selection object that signals when thread work + is complete + """ + return self._thread_worker.selobj + + @property + def stall_elapsed_secs(self): + """ + Returns the elapsed time in seconds that the thread has been stalled + """ + if self._stall_timestamp_ms is not None: + now = timers.get_monotonic_timestamp_in_ms() + return int((now - self._stall_timestamp_ms) / 1000) + return 0 + + @coroutine + def do_check(self): + """ + Check the Thread for progress + """ + while True: + (yield) + if self._last_marker_value is not None: + if self._last_marker_value == self._progress_marker.value: + if self._stall_timestamp_ms is None: + self._stall_timestamp_ms = \ + timers.get_monotonic_timestamp_in_ms() + + DLOG.error("Thread %s stalled, progress_marker=%s, " + "elapsed_secs=%s." % (self._name, + self._progress_marker.value, + self.stall_elapsed_secs)) + else: + self._stall_timestamp_ms = None + + self._last_marker_value = self._progress_marker.value + + def start(self): + """ + Start the Thread + """ + self._process.start() + if self._check_timer_id is None: + self._check_timer_id = timers.timers_create_timer( + self._name, self._check_interval_in_secs, + self._check_interval_in_secs, self.do_check) + + def stop(self, max_wait_in_seconds): + """ + Stop the Thread + """ + self._work_queue.put([Thread.ACTION_STOP, None]) + self._process.join(max_wait_in_seconds) + if self._process.is_alive(): + self._process.terminate() + if self._check_timer_id is not None: + timers.timers_delete_timer(self._check_timer_id) + self._work_queue.close() + + def debug_config_change(self): + self._work_queue.put([Thread.ACTION_DEBUG_CONFIG_RELOAD, None]) + + def send_work(self, action, work): + """ + Send work to Thread + """ + self._work_queue.put([action, work]) + + def get_result(self): + """ + Get work result + """ + return self._thread_worker.get_result() + + +@coroutine +def _thread_dispatch_work(thread_state, thread_worker, work_queue): + """ + Dispatch thread work + """ + while True: + select_obj = (yield) + if select_obj == work_queue.selobj: + work_entry = work_queue.get() + if work_entry is not None: + action, work = work_entry + + DLOG.verbose("Received work, action=%s." % action) + + if Thread.ACTION_DEBUG_CONFIG_RELOAD == action: + thread_state.debug_reload = True + + elif Thread.ACTION_STOP == action: + thread_state.stay_on = False + + else: + thread_worker.do_work(action, work) + + +def _thread_main(thread_name, progress_marker, debug_config, thread_worker, + work_queue): + """ + Main loop for the thread + """ + from ctypes import util + + PR_SET_PDEATHSIG = 1 + PR_SET_NAME = 15 + PR_SIGKILL = 9 + + libc = ctypes.cdll.LoadLibrary(util.find_library("c")) + result = libc.prctl(PR_SET_NAME, thread_name) + if 0 != result: + DLOG.error("PRCTL set-name failed with error=%s." % result) + sys.exit(200) + + result = libc.prctl(PR_SET_PDEATHSIG, PR_SIGKILL) + if 0 != result: + DLOG.error("PRCTL set-parent-death-signal failed with error=%s." % result) + sys.exit(201) + + signal.signal(signal.SIGTERM, signal.SIG_IGN) + signal.signal(signal.SIGINT, signal.SIG_IGN) + signal.signal(signal.SIGHUP, signal.SIG_IGN) + signal.signal(signal.SIGUSR1, signal.SIG_IGN) + signal.signal(signal.SIGUSR2, signal.SIG_IGN) + + try: + thread_state = ThreadState() + + debug.debug_initialize(debug_config, thread_name=thread_name) + selobj.selobj_initialize() + timers.timers_initialize(thread_worker.tick_interval_in_ms, + thread_worker.tick_max_delay_in_ms, + thread_worker.tick_delay_debounce_in_ms) + + DLOG.debug("Thread %s: initializing." % thread_name) + thread_worker.initialize() + + selobj.selobj_add_read_obj(work_queue.selobj, _thread_dispatch_work, + thread_state, thread_worker, work_queue) + + DLOG.debug("Thread %s: started." % thread_name) + while thread_state.stay_on: + progress_marker.increment() + selobj.selobj_dispatch(thread_worker.tick_interval_in_ms) + timers.timers_schedule() + + if not timers.timers_scheduling_on_time(): + DLOG.info("Thread %s: not scheduling on time" % thread_name) + + if thread_state.debug_reload: + debug.debug_reload_config() + thread_state.debug_reload = False + + except KeyboardInterrupt: + print("Keyboard Interrupt received.") + pass + + except Exception as e: + DLOG.exception("%s" % e) + sys.exit(202) + + finally: + DLOG.info("Thread %s: shutting down." % thread_name) + thread_worker.finalize() + timers.timers_finalize() + selobj.selobj_finalize() + DLOG.info("Thread %s: shutdown." % thread_name) + debug.debug_finalize() diff --git a/nfv/nfv-common/nfv_common/thread/_thread_progress_marker.py b/nfv/nfv-common/nfv_common/thread/_thread_progress_marker.py new file mode 100755 index 00000000..0b2b0e8a --- /dev/null +++ b/nfv/nfv-common/nfv_common/thread/_thread_progress_marker.py @@ -0,0 +1,28 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +from ctypes import c_ulonglong +from multiprocessing import RawValue, Lock + +from nfv_common import debug + +DLOG = debug.debug_get_logger('nfv_common.thread.thread_progress_marker') + + +class ThreadProgressMarker(object): + """ + Thread Progress Marker + """ + def __init__(self, initial_value=0): + self.progress_marker = RawValue(c_ulonglong, initial_value) + self.lock = Lock() + + def increment(self, increment_by=1): + with self.lock: + self.progress_marker.value += increment_by + + @property + def value(self): + return self.progress_marker.value diff --git a/nfv/nfv-common/nfv_common/thread/_thread_worker.py b/nfv/nfv-common/nfv_common/thread/_thread_worker.py new file mode 100755 index 00000000..e527e735 --- /dev/null +++ b/nfv/nfv-common/nfv_common/thread/_thread_worker.py @@ -0,0 +1,82 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +from nfv_common import debug +from nfv_common import selectable + +DLOG = debug.debug_get_logger('nfv_common.thread.thread_worker') + + +class ThreadWorker(object): + """ + Thread Worker + """ + def __init__(self, name, tick_interval_in_ms=500, + tick_max_delay_in_ms=10000, tick_delay_debounce_in_ms=5000): + self._name = name + self._tick_interval_in_ms = tick_interval_in_ms + self._tick_max_delay_in_ms = tick_max_delay_in_ms + self._tick_delay_debounce_in_ms = tick_delay_debounce_in_ms + self._result_queue = selectable.MultiprocessQueue() + + @property + def name(self): + """ + Returns the name of thread worker + """ + return self._name + + @property + def tick_interval_in_ms(self): + return self._tick_interval_in_ms + + @property + def tick_max_delay_in_ms(self): + return self._tick_max_delay_in_ms + + @property + def tick_delay_debounce_in_ms(self): + return self._tick_delay_debounce_in_ms + + @property + def selobj(self): + """ + Returns the selection object that signals when thread work + is complete + """ + return self._result_queue.selobj + + def send_result(self, result): + """ + Send work result + """ + self._result_queue.put(result) + + def get_result(self): + """ + Get work result + """ + return self._result_queue.get() + + def do_work(self, action, work): + """ + Called to do work from thread-main + """ + DLOG.verbose("Default thread worker do_work called for %s." + % self._name) + + def initialize(self): + """ + Called to initialize thread worker from thread-main + """ + DLOG.verbose("Default thread worker initialize called for %s." + % self._name) + + def finalize(self): + """ + Called to finalize thread worker from thread-main + """ + DLOG.verbose("Default thread worker finalize called for %s." + % self._name) diff --git a/nfv/nfv-common/nfv_common/timers/__init__.py b/nfv/nfv-common/nfv_common/timers/__init__.py new file mode 100755 index 00000000..9fe8666b --- /dev/null +++ b/nfv/nfv-common/nfv_common/timers/__init__.py @@ -0,0 +1,14 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# flake8: noqa +# +from _timestamp import get_monotonic_timestamp_in_ms +from _timer_module import interval_timer +from _timer_module import timers_create_timer, timers_delete_timer +from _timer_module import timers_reschedule_timer +from _timer_module import timers_scheduling_on_time, timers_schedule +from _timer_module import timers_register_interval_timers +from _timer_module import timers_initialize, timers_finalize diff --git a/nfv/nfv-common/nfv_common/timers/_timer.py b/nfv/nfv-common/nfv_common/timers/_timer.py new file mode 100755 index 00000000..80f6d83b --- /dev/null +++ b/nfv/nfv-common/nfv_common/timers/_timer.py @@ -0,0 +1,73 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +from nfv_common import debug + +from _timestamp import get_monotonic_timestamp_in_ms + +DLOG = debug.debug_get_logger('nfv_common.timers.timer') + + +class Timer(object): + """ + Timer + """ + _id = 1 + + def __init__(self, timer_name, initial_delay_secs, interval_secs, + callback, *callback_args, **callback_kwargs): + """ + Create timer + """ + self._timer_id = Timer._id + self._timer_name = timer_name + self._interval_secs = interval_secs + self._arm_timestamp = get_monotonic_timestamp_in_ms() + self._callback = callback(*callback_args, **callback_kwargs) + + if initial_delay_secs is None: + self._next_expiry_in_secs = interval_secs + else: + self._next_expiry_in_secs = initial_delay_secs + Timer._id += 1 + + @property + def timer_id(self): + """ + Returns the unique timer identifier + """ + return self._timer_id + + @property + def timer_name(self): + """ + Returns the name of the timer + """ + return self._timer_name + + def reschedule(self, interval_secs): + """ + Reschedule a timer + """ + self._interval_secs = interval_secs + self._next_expiry_in_secs = self._interval_secs + + def callback(self, now_ms): + """ + Execute the callback associated with this timer if enough + time has elapsed + """ + rearm = True + secs_expired = (now_ms - self._arm_timestamp) / 1000 + if secs_expired > self._next_expiry_in_secs: + DLOG.verbose("Timer %s with timer id %s fired." % (self._timer_name, + self._timer_id)) + try: + self._callback.send(self._timer_id) + self._arm_timestamp = get_monotonic_timestamp_in_ms() + self._next_expiry_in_secs = self._interval_secs + except StopIteration: + rearm = False + return rearm diff --git a/nfv/nfv-common/nfv_common/timers/_timer_module.py b/nfv/nfv-common/nfv_common/timers/_timer_module.py new file mode 100755 index 00000000..ccf9eb10 --- /dev/null +++ b/nfv/nfv-common/nfv_common/timers/_timer_module.py @@ -0,0 +1,124 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import functools + +from nfv_common import debug + +from _timer import Timer +from _timer_scheduler import TimerScheduler + +DLOG = debug.debug_get_logger('nfv_common.timers.timer_module') + +_scheduler = None +_interval_timers = dict() + + +def interval_timer(name, initial_delay_secs, interval_secs): + """ + Decorator function used to create an interval timer, note decorators + are called at load time + """ + def timer_wrap(func): + def timer_wrapper(*args, **kwargs): + target = func(*args, **kwargs) + target.send(None) + return target + + functools.update_wrapper(timer_wrapper, func) + if timer_wrapper not in _interval_timers: + _interval_timers[timer_wrapper] = (name, initial_delay_secs, + interval_secs, timer_wrapper) + return timer_wrapper + return timer_wrap + + +def timers_create_timer(name, initial_delay_secs, interval_secs, + callback, *callback_args, **callback_kwargs): + """ + Create a timer + """ + global _scheduler + + timer = Timer(name, initial_delay_secs, interval_secs, + callback, *callback_args, **callback_kwargs) + _scheduler.add_timer(timer) + DLOG.debug("Timer %s created, name=%s." % (timer.timer_id, name)) + return timer.timer_id + + +def timers_delete_timer(timer_id): + """ + Delete a timer + """ + global _scheduler + + _scheduler.delete_timer(timer_id) + DLOG.debug("Timer %s deleted." % timer_id) + + +def timers_reschedule_timer(timer_id, interval_secs): + """ + Reschedule a timer at a different interval + """ + global _scheduler + + _scheduler.reschedule_timer(timer_id, interval_secs) + DLOG.debug("Timer %s rescheduled every %s seconds." % (timer_id, + interval_secs)) + + +def timers_scheduling_on_time(): + """ + Determine if we are scheduling timers on time + """ + global _scheduler + + return _scheduler.scheduling_on_time + + +def timers_schedule(): + """ + Schedule timers + """ + global _scheduler + + _scheduler.schedule() + + +def timers_register_interval_timers(interval_timers): + """ + Register the given interval timers + """ + for timer_func in interval_timers: + name, initial_delay_secs, interval_secs, func \ + = _interval_timers[timer_func] + + timers_create_timer(name, initial_delay_secs, interval_secs, func) + + +def timers_initialize(scheduler_interval_ms, scheduler_max_delay_ms, + scheduler_delay_debounce_ms): + """ + Initializes the timer package + """ + global _scheduler + + if _scheduler is not None: + del _scheduler + + _scheduler = TimerScheduler(scheduler_interval_ms, + scheduler_max_delay_ms, + scheduler_delay_debounce_ms) # flake8: noqa + + +def timers_finalize(): + """ + Finalizes the timer package + """ + global _scheduler + + if _scheduler is not None: + del _scheduler diff --git a/nfv/nfv-common/nfv_common/timers/_timer_scheduler.py b/nfv/nfv-common/nfv_common/timers/_timer_scheduler.py new file mode 100755 index 00000000..975d7c09 --- /dev/null +++ b/nfv/nfv-common/nfv_common/timers/_timer_scheduler.py @@ -0,0 +1,117 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +from nfv_common import debug +from nfv_common import histogram + +from _timestamp import get_monotonic_timestamp_in_ms + +DLOG = debug.debug_get_logger('nfv_common.timers.timer_scheduler') + + +class TimerScheduler(object): + """ + Timer Scheduler + """ + def __init__(self, scheduler_interval_ms, scheduler_max_delay_ms, + scheduler_delay_debounce_ms): + """ + Create a timer scheduler + """ + self._scheduler_interval_ms = scheduler_interval_ms + self._scheduler_max_delay_ms = scheduler_max_delay_ms + self._scheduler_delay_debounce_ms = scheduler_delay_debounce_ms + self._scheduler_timestamp_ms = 0 + self._scheduler_delay_timestamp_ms = 0 + self._timers = [] + self._scheduling_on_time = True + self._timers_to_delete = [] + self._scheduling_timers = False + + @property + def scheduling_on_time(self): + """ + Determine if timers are being scheduled on time + """ + return self._scheduling_on_time + + def schedule(self): + """ + Schedule timers + """ + now_ms = get_monotonic_timestamp_in_ms() + ms_expired = now_ms - self._scheduler_timestamp_ms + + if ms_expired < self._scheduler_interval_ms: + DLOG.verbose("Not enough time has elapsed to schedule timers, " + "ms_expired=%d ms." % ms_expired) + return + + if 0 != self._scheduler_timestamp_ms: + if ms_expired >= self._scheduler_max_delay_ms: + if self._scheduling_on_time: + self._scheduling_on_time = False + DLOG.info("Not scheduling on time, elapsed=%d ms." + % ms_expired) + + self._scheduler_delay_timestamp_ms \ + = get_monotonic_timestamp_in_ms() + else: + if not self.scheduling_on_time: + ms_expired = now_ms - self._scheduler_delay_timestamp_ms + if ms_expired > self._scheduler_delay_debounce_ms: + self._scheduling_on_time = True + DLOG.info("Now scheduling on time.") + + self._scheduler_timestamp_ms = now_ms + self._scheduling_timers = True + overall_start_ms = get_monotonic_timestamp_in_ms() + try: + DLOG.verbose('Scheduling timers.') + for timer in self._timers: + start_ms = get_monotonic_timestamp_in_ms() + rearm = timer.callback(now_ms) + elapsed_ms = get_monotonic_timestamp_in_ms() - start_ms + histogram.add_histogram_data("timer callback: " + timer.timer_name, + elapsed_ms / 100, "decisecond") + if not rearm and timer.timer_id not in self._timers_to_delete: + self._timers_to_delete.append(timer.timer_id) + finally: + self._scheduling_timers = False + + # Cleanup pending timers to be deleted. + self._timers[:] = [timer for timer in self._timers + if timer.timer_id not in self._timers_to_delete] + # Cleanup list of timers to delete + del self._timers_to_delete[:] + + elapsed_ms = get_monotonic_timestamp_in_ms() - overall_start_ms + histogram.add_histogram_data("timer overall time per dispatch: ", + elapsed_ms / 100, "decisecond") + + def add_timer(self, timer): + """ + Add a timer + """ + self._timers.append(timer) + + def delete_timer(self, timer_id): + """ + Delete a timer + """ + if self._scheduling_timers: + self._timers_to_delete.append(timer_id) + else: + self._timers[:] = [timer for timer in self._timers + if timer_id != timer.timer_id] + + def reschedule_timer(self, timer_id, interval_secs): + """ + Reschedule a timer + """ + existing_timer = next((timer for timer in self._timers + if timer_id == timer.timer_id), None) + if existing_timer is not None: + existing_timer.reschedule(interval_secs) diff --git a/nfv/nfv-common/nfv_common/timers/_timestamp.py b/nfv/nfv-common/nfv_common/timers/_timestamp.py new file mode 100755 index 00000000..8c0c0bf0 --- /dev/null +++ b/nfv/nfv-common/nfv_common/timers/_timestamp.py @@ -0,0 +1,41 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import os +import ctypes + +from nfv_common import debug + +DLOG = debug.debug_get_logger('nfv_common.timers.timestamp') + +CLOCK_MONOTONIC_RAW = 4 # from + + +class timespec(ctypes.Structure): + """ + Timespec C Type + """ + _fields_ = [('tv_sec', ctypes.c_long), ('tv_nsec', ctypes.c_long)] + + +try: + librt = ctypes.CDLL('librt.so.1', use_errno=True) + clock_gettime = librt.clock_gettime + clock_gettime.argtypes = [ctypes.c_int, ctypes.POINTER(timespec)] +except Exception: + raise OSError("Could not load librt.so library") + + +def get_monotonic_timestamp_in_ms(): + """ + Returns the timestamp in milliseconds + """ + t = timespec() + if 0 != clock_gettime(CLOCK_MONOTONIC_RAW, ctypes.pointer(t)): + errno_ = ctypes.get_errno() + raise OSError(errno_, os.strerror(errno_)) + timestamp_ms = (t.tv_sec * 1e+3) + (t.tv_nsec * 1e-6) + DLOG.verbose("Monotonic timestamp fetched is %s." % timestamp_ms) + return timestamp_ms diff --git a/nfv/nfv-common/nfv_common/validate.py b/nfv/nfv-common/nfv_common/validate.py new file mode 100755 index 00000000..6b498d74 --- /dev/null +++ b/nfv/nfv-common/nfv_common/validate.py @@ -0,0 +1,57 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import uuid + + +def valid_uuid_str(uuid_str, version=4): + """ + Returns true if uuid string given is a valid uuid + """ + try: + uuid.UUID(uuid_str, version=version) + + except ValueError: + return False + + return True + + +def valid_uuid_hex(uuid_hex_str, version=4): + """ + Returns true if uuid hex string given is a valid uuid + """ + try: + uuid_value = uuid.UUID(uuid_hex_str, version=version) + + except ValueError: + return False + + # Verify that the uuid_hex_str was not converted into a valid uuid. This + # is possible when the uuid_hex_str is a valid hex string but not a valid + # uuid. The uuid.UUID constructor will auto correct. + return uuid_value.hex == uuid_hex_str + + +def valid_bool(boolean_str): + """ + Returns true if string given is a valid boolean + """ + if boolean_str.lower() in ['true', '1', 'false', '0']: + return True + return False + + +def valid_integer(integer_str): + """ + Returns true if string given is a valid integer + """ + try: + int(integer_str) + + except ValueError: + return False + + return True diff --git a/nfv/nfv-common/setup.py b/nfv/nfv-common/setup.py new file mode 100755 index 00000000..d63d79a2 --- /dev/null +++ b/nfv/nfv-common/setup.py @@ -0,0 +1,20 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# flake8: noqa +# +from setuptools import setup, find_packages + +setup( + name='windriver-nfv_common-plugins', + description='Wind River NFV Plugins', + version='1.0.0', + license='Apache-2.0', + platforms=['any'], + provides='nfv_plugins', + packages=find_packages(), + package_data={'nfv_common.forensic': ['config/*']}, + include_package_data=True, +) diff --git a/nfv/nfv-debug-tools/histogram_analysis/Histogram.py b/nfv/nfv-debug-tools/histogram_analysis/Histogram.py new file mode 100755 index 00000000..b8b82e36 --- /dev/null +++ b/nfv/nfv-debug-tools/histogram_analysis/Histogram.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python +################################################################################ +# +# Copyright (c) 2017 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +################################################################################ +# +# Description: Un-gzips nfv-vim log files within the same directory and generates +# CSVs based on the processes found related to histogram data, +# naming each CSV after the process found in the log files whose +# log data it is storing. Each CSV file contains 3 columns of +# data: TIMESTAMP, average execution time, hits per sample +# This script is meant to be used with the plotter.py data visualization +# script. +# +# Behaviour : The script runs without any input arguments, copies all nfv-vim +# log files found, gzipped or not, and places them into a directory +# called logs/ where it un-gzips any zipped files (this retains the +# original gzipped files). After this it begins parsing each nfv-vim +# log file found, starting from the highest numbered e.g. nfv-vim.log.20 +# down to the lowest nfv-vim.log.1 or nfv-vim.log. This script expects +# the highest number of digits following .log. to be two i.e [0-9][0-9] +# CSV files will be stored in a new directory called csv/ +# Timestamps for each sample are written to the CSV, followed by the +# average execution time which is each execution time in the sample, +# multiplied by its respective number of hits during that sample, and +# then all of these values summed together, divided by the sum comprising +# the total number of hits for that particular sample. The third column +# is simply the total number of hits for that same (across all execution +# times). +# +# Place this script in a directory containing the gzipped or ungzipped logs you would +# like to generate CSV files for. +# +# Run the script with ./Histogram.py +# +################################################################################ + +import os +import glob +from subprocess import call +from collections import defaultdict + +dir = os.path.dirname(__file__) +csvDir = os.path.join(dir,'csv/') +logDir = os.path.join(dir,'logs/') + +if not os.path.exists(csvDir): + os.makedirs(csvDir) + +if not os.path.exists(logDir): + os.makedirs(logDir) + +call("cp nfv-vim.log nfv-vim.log.[0-9] nfv-vim.log.[0-9][0-9] nfv-vim.log.[0-9].gz nfv-vim.log.[0-9][0-9].gz logs/", shell=True) +call("gunzip logs/nfv-vim.log.[0-9].gz logs/nfv-vim.log.[0-9][0-9].gz", shell=True) + +class Parser: + def __init__(self): + self.proc="" # Name of process being read + self.timestamp="" # Timestamp found on line stating process name + self.write=False # Flag indicating data has yet to be written + self.stored=False # Flag indicating that there is new data stored + self.length=0 # Time duration of process + self.instanceCount=0 # Number of hits for the particular duration + self.rollingCount=0 # Sum of the hits for each duration parsed within the sample + self.total=0 # Specific duration multiplied by number of hits for that duration + self.avg=0 # Average execution time of process + self.unit="" # Unit execution time was recorded in + self.csvs=defaultdict(list) # Stores unique processes in a dict of lists + + # Resets variables when a new process begins to be read in logs + def reset(self): + self.length=0 + self.avg=0 + self.instanceCount=0 + self.rollingCount=0 + self.total=0 + self.proc="" + self.unit="" + self.write=False + self.stored=False + + # Adds log data for a process to the csvs dictionary + def add(self,proc,total,timestamp,rollingCount): + if rollingCount != 0: + avg=total/float(rollingCount) + else: + avg=0 + self.csvs[proc].append(timestamp+","+str(avg)+","+str(rollingCount)+",") + self.reset() + + def main(self): + # Sorts the log files to read them in descending order + sorted_files = glob.glob(logDir+"nfv-vim.log*") + sorted_files.sort(reverse=True) + for logFile in sorted_files: + with open(logFile,"r+") as f: + cfgLines = f.read().splitlines() + for line in cfgLines: + if "Histogram" in line: + if self.write or self.stored: + self.add(self.proc,self.total,self.timestamp,self.rollingCount) + self.write=True + self.proc=line.partition("Histogram: ")[2] + self.proc=("".join(self.proc.split())).rstrip(':') + self.timestamp=line.split()[0] + elif "histogram.py" in line: + line=line.split() + self.length=int(line[8]) + self.unit=line[9] + self.instanceCount=int(line[10]) + if "decisecond" in self.unit: + self.length*=100 + elif "secs" in self.unit: + self.length*=1000 + self.total=self.total+self.instanceCount*self.length + self.rollingCount+=self.instanceCount + self.stored=True + f.close() + if self.write or self.stored: + self.add(self.proc,self.total,self.timestamp,self.rollingCount) + + for process in self.csvs: + with open(os.path.join(csvDir,process+".csv"),'w+') as csvOut: + for line in self.csvs[process]: + csvOut.write(line+"\n") + csvOut.close() + +process=Parser() +process.main() +print "\nComplete\n" \ No newline at end of file diff --git a/nfv/nfv-debug-tools/histogram_analysis/plotter.py b/nfv/nfv-debug-tools/histogram_analysis/plotter.py new file mode 100755 index 00000000..3794b6f6 --- /dev/null +++ b/nfv/nfv-debug-tools/histogram_analysis/plotter.py @@ -0,0 +1,495 @@ +#!/usr/bin/env python +################################################################################ +# +# Copyright (c) 2017 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +################################################################################ +# +# Description: Graphs nfv-vim histogram data from CSV files to html page and saves +# saves it locally. +# +# Behaviour : The script takes in arguments from the command line such as specific +# process names, or the name of a grouping of processes, and graphs +# them in a local html file using plotly. The x-axis displays +# datestamps corresponding to when the sample in the csv file was taken, +# and the y-axis displays either the average execution time of the +# processes during each sample, or the difference in total hits that +# process experienced from one sample period to the previous sample +# period. Both average execution times and the delta hit count can +# be displayed on the same graph using two y-axes. The CSV files must +# be generated prior to running this script by running Histogram.sh. +# A config called logplot.cfg will be generated the first time this +# script is run and will automatically populate itself with all processes +# listed in the csv/ directory. Change the N to a Y in the right column +# of the config file to have that process graphed when running this script +# via config settings. Groupings of processes can also be made under the +# groups section by following the same N/Y format as above. When a group +# name is specified all processes listed under that group name will be +# graphed if they have a Y in their right-column. +# +# To run this script ensure that plotly is installed. +# To do this enter: sudo pip install plotly +# +# +# If no arguments are entered when running this script it will default to running +# the proceses in logplot.cfg with a Y in their rightmost column, and will display +# the average execution time on the y-axis for all available samples. +# +################################################################################ + +import os +import csv +import sys +import time +import plotly +import plotly.graph_objs as go +from plotly.graph_objs import Scatter, Layout +from plotly import tools +from glob import iglob +import commands + +dir = os.path.dirname(__file__) +fig = plotly.graph_objs.graph_objs.Figure +pth = os.path.join(dir,'csv/') + +execTime=False # Indicates if average execution time is to be graphed or not +default=False # Indicates no commands were entered and to run with default settings (run config with -t option) +oneAxis=False # Causes the generated graph to have two y-axes sharing an x-axis with both avg execution time and hits being graphed +config=False # Indicates whether to pull process names from logplot.cfg or not +hits=False # Indicates if the delta of hits between samples is to be graphed +markers=False +lines=False +timestamp=[] +dateRange=[] +warnings=[] +procs=[] +group=[] +graphName="" +plotType="" + +def helpMessage(): + print "\n"+"-"*120 + print "NFV-VIM Histogram Graphing Script\n" + print "This script is meant to graph average execution times and the delta of hits between sample periods for processes in nfv-vim logs.\n" + print "Usage:\n" + print(" -c ... runs from the logplot.cfg (c)onfig file. All processes in the first list with a Y\n" + " in the far-right column will be included in the generated graph.\n") + print(" -d ... command used to specify a (d)ate range within which you would like to see log data.\n" + " The format is YYYY/MM/DD-YYYY/MM/DD with the lower bound on the left, and the upper\n" + " bound on the right. The range is up to and including the bounds. To have a bound simply\n" + " cover all datestamps before or after a bound, omit the undefined bound. Only one bound can be\n" + " unspecified in this way.\n" + " e.g. -d 2016/12/01-2016/12/12\n" + " -d -2016/12/12 To use all logs prior to and including 2016/12/12\n" + " -d 2016/12/01- To use all logs after and including 2016/12/01\n") + print(" -t ... used to indicate that you would like the graph to display average execution (t)imes\n" + " along the y-axis.\n") + print(" -h ... used to indicate that you would like the graph to display the dela of (h)its between\n" + " sample periods on the y-axis.\n") + print " -l ... used to set the graph to be a line graph. (Can be used with -m as well)\n" + print " -m ... used to set the graph to be a scatterplot. (Can be used with -l as well)\n" + print(" -lm ... used to set the graph to be a scatterplot with connecting lines. This same effect can also be\n" + " achieved by using -l -m\n") + print(" -n ... used to (n)ame the file that will be generated by this script. Files can be found in the\n" + " Graphs/ directory, found inside the directory containing this script. Do not include spaces in\n" + " the file name. If no name is specified, the name will default to the timestamp from when the\n" + " script was run.\n" + " e.g. 01-24-2017.html\n") + print(" -oneaxis ... used to generate a graph with two Y-axes sharing an x-axis. Average execution time's y-axis is\n" + " on the right, and delta Hits per sample's y-axis is on the left. Used to look for correlations. The \n" + " name of the process being graphed will have _time or _hits appended to it so you can tell which\n" + " y-axis to relate it to. Only works if both -h and -t flags are used. Can be used for multiple processes.\n" + " e.g. -h -t -oneaxis --p process1 process2\n") + print(" --g ... will run the script for processes specified in logplot.cfg under the (G)roups heading.\n" + " All processes listed under the named group's heading will be included in the graph.\n" + " Space-delimit the groups to be included. This must be the last command entered.\n" + " e.g. --g group1 group2\n") + print(" --p ... follow this with a space-delimited list of (p)rocesses you would like to graph together.\n" + " This must be the last command entered.\n") + print(" --update ... This will update the master list of process at the beginning of the logplot.cfg file:\n" + " Processes not currently listed in the master list will be added and their run status set to N.\n\n") + print "Note: If neither the -t nor -h tag is used, the script will default to display the average execution time on the y-axis.\n\n" + print "Examples:\n" + print("./plotter.py -c -d 2016/12/3-2016/12/10 -t -n ConfigOutput_Dec_3-10 ... This will graph all processes with a Y in their\n" + " right-most column in the config file, using logs\n" + " with a timestamp between Dec 3rd and 10th 2016,\n" + " and will display their average execution time in\n" + " the y-axis. The file will be called\n" + " ConfigOutput_Dec_3-10.html") + print("./plotter.py -h -t --g group1 ... This will generate two graphs, one with the delta of hits\n" + " on the y-axis, and the other with the average execution time\n" + " in the y-axis, for processes listed under group1 in\n" + " logplot.cfg.\n" + " period will be displayed on the y-axis.\n") + print("./plotter.py ... This will run the default settings, which are to run\n" + " for the processes enabled in the master list in\n" + " the config file, to use log information for all dates\n" + " available, to show average execution time on the y-axis,\n" + " and to name the file with the current day's datestamp.") + print "-"*120 + + +# Appends new processes found via CSV filenames to the master process list in logplot.cfg if there are not already present. +# If logplot.cfg has not been generated yet, this will create it and add process names found in filenames in ./csv +def updater(configExists=True): + procs=[] + existingProcs=[] + newProcs=[] + position=0 # Tracks position of the end of the master process list so new processes can be added above it. + + os.chdir(pth) + for name in iglob("*.csv"): + procs.append(str(name)[:-4]) + os.chdir("..") + if not configExists: + f=open(os.path.join(dir,'logplot.cfg'),"w") + for p in procs: + f.write(p+" "*(59-len(p))+"N\n") + f.write("#"*20+"END OF PROCESS LIST"+"#"*21+"\n\n") + f.write("#"*27+"GROUPS"+"#"*27+"\n") + f.write("#GroupSTART\n") + f.write("GroupName=ExampleGroupName1\n") + f.write("ExampleProcessName1"+" "*40+"N\n") + f.write("ExampleProcessName2"+" "*40+"N\n") + f.write("#GroupEND\n") + f.write("-"*60+"\n") + f.write("GroupName=ExampleGroupName2\n") + f.write("ExampleProcessName3"+" "*40+"N\n") + f.write("ExampleProcessName4"+" "*40+"N\n") + f.write("#GroupEND\n") + f.write("#"*20+"END OF GROUPS"+"#"*27) + f.close() + else: + with open(os.path.join(dir,'logplot.cfg'),"r+") as f: + cfgLines = f.read().splitlines() + for cfgProc in cfgLines: + if "#END" in cfgProc: + break + existingProcs.append(cfgProc.split()[0]) + position+=1 + for p in procs: + if p not in existingProcs: + newProcs.append(p+" "*(59-len(p))+"N") + procs=cfgLines[:position]+newProcs+cfgLines[position:] + f.seek(0) + f.write("\n".join(procs)) + f.truncate() + f.close() + + +# Appends process names found in the specified group to the list of processes to be graphed. +def gCommand(groups): + procs=[] + f=open(os.path.join(dir,'logplot.cfg'),"r") + cfgLines=f.read().splitlines() + + for g in groups: + groupFound=False + finishedGroup=False + + for i in xrange(len(cfgLines)): + liNum=i + if str("GroupName="+g) == cfgLines[i].strip(): + groupFound=True + linum=i + while not finishedGroup: + liNum+=1 + if "GroupEND" in cfgLines[liNum]: + finishedGroup=True + else: + cfgLine=cfgLines[liNum].split() + if cfgLine[1]=="Y": + procs.append(cfgLine[0]) + else: + break + else: + if not groupFound: + warnings.append("WARNING: The following group could not be found: %s\n\t\t Please check your logplot.cfg file for the intended group name."%(g,)) + + f.close() + return procs + + +# Appends processes explicitly named by the user to the list of processes to be run. +# If the process name specified using the --p command does not match the name of any processes taken from .csv filenames, the user is given +# a list of known processes containing the name they entered. If they enter one of the provided names, it will be added to the list. If the +# user enters "s", the process in question will be skipped and the script will continue. If they user enters "q" the script will exit. +def pCommand(pList): + procList=[] + for i in xrange(len(pList)): + csvFile=str(pList[i])+".csv" + procName=str(pList[i]) + isFile=False + + if os.path.isfile(os.path.join(pth,csvFile)): + isFile = True + procList.append(pList[i]) + else: + while(not isFile): + print "\nFiles containing keyword: %s"%(str(procName)) + csvFile=str(procName)+".csv" + for root, directories, filenames in os.walk(pth): + for filename in filenames: + if procName.lower() in filename.lower(): + if (str(procName)+".csv") == str(filename): + isFile=True + procList.append(str(procName).strip()) + break + else: + print " "+filename[:-4] + else: + procName = str(raw_input("\nEnter the corrected process name, q to quit, or s to skip: ")).strip() + if procName=="s": + isFile=True + break + elif procName=="q": + sys.exit() + return procList + + +# Stores the average execution time, or delta hit count data into into a plotly graph obj, and restricts sample to be within a certain +# date range if specified. If plots is 1, one graph will be generated. If plots is 2, two graphs will be generated with one above the other. +def storeGraphData(procs, dateRange=[], execTime=False, hits=False, plots=1): + graphData={} + prevHitTotal=0 + timeList=[[] for p in xrange(len(procs))] + dateList=[[] for p in xrange(len(procs))] + hitList=[[] for p in xrange(len(procs))] + if dateRange: + for i in xrange(len(procs)): + csvFile = str(procs[i])+".csv" + with open(os.path.join(pth,csvFile), 'rb') as f: + reader = csv.reader(f, delimiter=',', quoting=csv.QUOTE_NONE) + for ts, at, h, n in reader: + t = ts.split("T") + date=''.join(x for x in t[0].split('-')) + if (int(date) >= int(dateRange[0])) and (int(date) <= int(dateRange[1])): + timeList[i].append(at) + dateList[i].append(str(ts[0:10:1]+" "+ts[11:])) + hitList[i].append(int(h)-prevHitTotal) + prevHitTotal=int(h) + f.close() + hitList[i][0]=None + graphData['trace'+str(i)] = go.Scatter( + x = dateList[i], + y = timeList[i] if execTime else hitList[i], + mode = plotType, + name = (procs[i] if not oneAxis else (procs[i]+"_"+("time" if execTime else "hits"))) + ) + if plots==1: + fig.append_trace(graphData['trace'+str(i)], 1, 1) + elif plots==2: + fig.append_trace(graphData['trace'+str(i)], 2, 1) + + else: + for i in xrange(len(procs)): + csvFile = str(procs[i])+".csv" + with open(os.path.join(pth,csvFile), 'rb') as f: + reader = csv.reader(f, delimiter=',', quoting=csv.QUOTE_NONE) + for ts, at, h, n in reader: + timeList[i].append(at) + dateList[i].append(str(ts[0:10:1]+" "+ts[11:])) + hitList[i].append(int(h)-prevHitTotal) + prevHitTotal=int(h) + f.close() + hitList[i][0]=None + graphData['trace'+str(i)] = go.Scatter( + x = dateList[i], + y = timeList[i] if execTime else hitList[i], + mode = plotType, + name = (procs[i] if not oneAxis else (procs[i]+"_"+("time" if execTime else "hits"))) + ) + if plots==1: + fig.append_trace(graphData['trace'+str(i)], 1, 1) + elif plots==2: + fig.append_trace(graphData['trace'+str(i)], 2, 1) + + +# Formats the graph by adding axis titles, changing font sizes, setting there to be two separate graphs or two graphs sharing an x-axis etc. +def formatGraph(two, oneAxis): + fig['layout'].update(showlegend=True) + if two: + if oneAxis: + fig['layout']['xaxis1'].update(title='Timestamp',titlefont=dict(size=20, color='#4d4d4d')) + fig['layout']['yaxis1'].update(title='Hits Per Sample',titlefont=dict(size=20, color='#4d4d4d')) + fig['layout']['yaxis2'].update(title='Average Execution Time (milliseconds)',anchor='x',overlaying='y',side='right',position=1,titlefont=dict(size=20, color='#4d4d4d')) + else: + fig['layout']['xaxis1'].update(title='Timestamp',titlefont=dict(size=20, color='#4d4d4d')) + fig['layout']['yaxis1'].update(title='Average Execution Time (milliseconds)',titlefont=dict(size=20, color='#4d4d4d')) + fig['layout']['xaxis2'].update(title='Timestamp',titlefont=dict(size=20, color='#4d4d4d')) + fig['layout']['yaxis2'].update(title='Hits Per Sample',titlefont=dict(size=20, color='#4d4d4d')) + fig['layout'].update(title=graphName, titlefont=dict(size=26)) + else: + fig['layout'].update( + title=graphName, + xaxis=dict( + title="Timestamp", + titlefont=dict( + family='Courier New, monospace', + size=18, + color='#4d4d4d' + ) + ), + yaxis=dict( + title="Average Execution Time (milliseconds)" if execTime else "Hits Per Sample", + titlefont=dict( + family='Courier New, monospace', + size=18, + color='#4d4d4d' + ) + ) + ) + + +# Sets the name of the saved html file. +def setFilename(graphName): + validName=False + if not os.path.exists("Graphs/"): + os.makedirs("Graphs/") + os.chdir(os.path.join(dir,'Graphs/')) + if not graphName: + graphName=time.strftime("%m-%d-%Y") + if os.path.exists(str(graphName+".html")): + n=1 + while(not validName): + if os.path.exists(str(graphName+"("+str(n)+").html")): + n+=1 + else: + graphName=graphName+"("+str(n)+")" + validName=True + return graphName + + + +print "Welcome to plotter, type --help for information" +# Checks that plotly is installed, otherwise graphs cannot be generated. +plotCheck=commands.getstatusoutput("pip list | grep plotly") +if plotCheck[0]==0: + if "plotly" not in plotCheck[1]: + print "\n\tWARNING: Plotly is not installed on your system.\n\tPlease install it with: sudo pip install plotly\n" + sys.exit() +# Checks to see if logplot.cfg already exists, creates it if not. +if not os.path.isfile(os.path.join(dir,'logplot.cfg')): + print "Generating logplot.cfg" + updater(False) + print "logplot.cfg created." +if not os.path.isdir('./csv'): + print "\n\tWARNING: ./csv directory is missing. Please run Histogram.sh or make sure directory has not been renamed.\n" + sys.exit() + +command = sys.argv # Takes arguments from the command line + +if len(command)==1: + print "Running with default settings." + default = True +else: + for i in xrange(1,len(command)): + if command[i] == "-c": # Use config file + config=True + elif command[i] == "--g": # Groups + for j in xrange(i+1,len(command)): + group.append(command[j]) + procs=gCommand(group) + break + elif command[i] == "-t": # Average execution time + execTime=True + elif command[i] == "-h": # Delta hits between samples + hits=True + elif command[i] == "-l": # Graph with lines + lines=True + elif command[i] == "-m": # Graph with markers (scatter) + markers=True + elif command[i] == "-lm": # Graph with lines and markers + lines=True + markers=True + elif command[i] == "-d": # Date range + dateRange=command[i+1].split('-') + if dateRange[0]: + lower=dateRange[0].split("/") + dateRange[0]=lower[0]+lower[1].zfill(2)+lower[2].zfill(2) + else: + dateRange[0]="0"*8 + if dateRange[1]: + upper=dateRange[1].split("/") + dateRange[1]=upper[0]+upper[1].zfill(2)+upper[2].zfill(2) + else: + dateRange[1]="9"*8 + i+=1 + elif command[i] == "-n": # Name of file to be generated + graphName=command[i+1] + i+=1 + elif command[i] == "-oneaxis": # Have hit and time data displayed on same graph + oneAxis=True + elif (command[i] == "--help") or (command[i] == "--h"): # Print help message and exit script + helpMessage() + sys.exit() + elif command[i] == "--p": # User-specified processes + for j in xrange(i+1,len(command)): + procs.append(command[j]) + procs=pCommand(procs) + break + elif command[i] == "--update": + print "Updating..." + updater() + print "Update complete." + sys.exit() + +# If neither average execution time nor delta hit count are specified to be shown, default to showing average execution time. +if (not execTime) and (not hits): + execTime = True + +# Default settings can be changed as desired. +if default: + config=True + execTime=True + +if (lines and markers): + plotType="lines+markers" +elif lines: + plotType="lines" +else: + plotType="markers" + +if config: + f=open(os.path.join(dir,'logplot.cfg'),"r") + procList=f.read().splitlines() + for p in procList: + if "#END" in p: + break + cfgLine=p.split() + if cfgLine[1]=="Y": + csvFile=cfgLine[0]+".csv" + if os.path.exists(os.path.join(pth,csvFile)): + procs.append(cfgLine[0]) + else: + warnings.append("WARNING: %s does not exist."%(csvFile,)) + f.close() + +# If both average execution time and delta hits are specified to be shown, generate two graphs if -oneaxis wasn't specified. +# If only one of execution time and delta hits was specified, generate one graph. +if procs: + if (execTime and hits): + if(not oneAxis): + fig = tools.make_subplots(rows=2, cols=1) + storeGraphData(procs, dateRange, execTime, False, 1) + storeGraphData(procs, dateRange, False, hits, 2) + else: + fig = tools.make_subplots(rows=1, cols=1) + storeGraphData(procs, dateRange, False, hits, 1) + storeGraphData(procs, dateRange, execTime, False, 1) + else: + fig = tools.make_subplots(rows=1, cols=1) + storeGraphData(procs, dateRange, execTime, hits) + + formatGraph((execTime and hits), oneAxis) + + # Generates the plot + plotly.offline.plot(fig, filename=setFilename(graphName)+".html") +else: + warnings.append("NO GRAPH GENERATED BECAUSE NO VALID GROUP OR PROCESS NAME SPECIFIED.") + +# If any warnings occured, print them +if warnings: + print "\n\t"+("\n\t").join(warnings)+"\n" diff --git a/nfv/nfv-docs/Makefile b/nfv/nfv-docs/Makefile new file mode 100755 index 00000000..e84f7d0f --- /dev/null +++ b/nfv/nfv-docs/Makefile @@ -0,0 +1,26 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +DOC_BUILDER = python ./doc_builder.py + +BUILD_DIR = ./build + +.PHONY: html clean help + +html: + $(DOC_BUILDER) -b html $(BUILD_DIR)/html + @echo + @echo "Build finished. Build output is in $(BUILD_DIR)/html." + +package: + tar -pczf vim_documentation.tar.gz $(BUILD_DIR) + +clean: + rm -rf $(BUILD_DIR)/* + +help: + @echo "Please use make where is one of" + @echo " html to make standalone HTML files" + @echo " package to tar and gzip documentation" diff --git a/nfv/nfv-docs/doc_builder.py b/nfv/nfv-docs/doc_builder.py new file mode 100755 index 00000000..e1df5366 --- /dev/null +++ b/nfv/nfv-docs/doc_builder.py @@ -0,0 +1,68 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +# The following installs are required: +# pip install pyyaml +# pip install jinja2 +# +import os +import six +import yaml +import shutil +import pprint +import argparse +from jinja2 import Environment, FileSystemLoader + + +CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) +DOC_SRC_DIR = CURRENT_DIR + '/documentation' +HTML_SRC_DIR = CURRENT_DIR + '/html_layout' + + +def build_html_doc(build_dir, document_data): + """ + Build HTML Documentation + """ + shutil.copytree(HTML_SRC_DIR + '/css', build_dir + '/css') + shutil.copytree(HTML_SRC_DIR + '/images', build_dir + '/images') + shutil.copytree(HTML_SRC_DIR + '/javascript', build_dir + '/javascript') + + j2_env = Environment(loader=FileSystemLoader(HTML_SRC_DIR + '/templates'), + trim_blocks=True) + + index_template = j2_env.get_template('index.html') + index_html = index_template.render(document_data) + + with open(build_dir + '/index.html', "w") as f: + six.print_(index_html, file=f) + + for toc_entry in document_data['table_of_contents']: + toc_entry_data = yaml.load(open(DOC_SRC_DIR + '/' + toc_entry['link'] + + '.yaml')) + toc_entry_data['page_link'] = toc_entry['link'] + + page_content_template = j2_env.get_template('page_content.html') + toc_entry_html = page_content_template.render(toc_entry_data) + six.print_(toc_entry_html, file=f) + + +if __name__ == '__main__': + """ + Document Builder + """ + parser = argparse.ArgumentParser() + parser.add_argument('-b', '--builder', help='type of build') + parser.add_argument('build_dir', help='build directory') + args = parser.parse_args() + + if not os.path.exists(args.build_dir): + os.makedirs(args.build_dir) + + document_data = yaml.load(open(DOC_SRC_DIR + '/document.yaml')) + + if 'html' == args.builder: + build_html_doc(args.build_dir, document_data) + else: + print "No builder selected, do nothing." \ No newline at end of file diff --git a/nfv/nfv-docs/documentation/document.yaml b/nfv/nfv-docs/documentation/document.yaml new file mode 100755 index 00000000..513ff480 --- /dev/null +++ b/nfv/nfv-docs/documentation/document.yaml @@ -0,0 +1,20 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +title: VIM Documentation +title_expanded: Virtual Infrastructure Manager Documentation + +table_of_contents: + - name: Software Image Management + link: software_image_management + + - name: Virtualised Network Resource + link: virtualised_network_resource + + - name: Virtualised Storage Resource + link: virtualised_storage_resource + + - name: Virtualised Compute Resource + link: virtualised_compute_resource diff --git a/nfv/nfv-docs/documentation/software_image_management.yaml b/nfv/nfv-docs/documentation/software_image_management.yaml new file mode 100755 index 00000000..868ec2e2 --- /dev/null +++ b/nfv/nfv-docs/documentation/software_image_management.yaml @@ -0,0 +1,302 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +page_title: Software Image Management +page_description: > + These interfaces allows an authorized consumer functional block to manage + the VNF software images in the VIM. + +query_response: &alias_query_response + - name: uuid + type: xsd:string + description: UUID of the image. + - name: name + type: xsd:string + description: Name of the image. + - name: description + type: xsd:string + description: > + Description of the image. Defaults to the empty string. + - name: container_format + type: xsd:string + description: > + Format of container. One of ami, ari, aki, bare, ovf, ova. + - name: disk_format + type: xsd:string + description: > + Format of disk. One of ami, ari, aki, vhd, vmdk, raw, qcow2, vdi, + iso. + - name: minimum_disk_size + type: xsd:string + description: Minimum size of disk in GB. + - name: minimum_memory_size + type: xsd:string + description: Minimum size of memory in MB. + - name: visibility + type: xsd:string + description: > + Visibility of the image. One of private, public, shared. + - name: protected + type: xsd:string + description: Determines if image is deletable. + - name: availability_status + type: xsd:string + description: Status of the image. + - name: action + type: xsd:string + description: Current action being performed on the image. + +api_group_images: &alias_api_group_images + name: Images + api_listing: + - id: add_image + name: Add Image + description: Allows adding a new software image to the image repository. + method: POST + urls: + - /v1/images + request: + - name: name + optional: "no" + type: xsd:string + description: Name of the image. + - name: description + optional: "yes" + type: xsd:string + description: > + Description of the image. Defaults to the empty string. + - name: container_format + optional: "no" + type: xsd:string + description: > + Format of container. One of ami, ari, aki, bare, ovf, ova. + - name: disk_format + optional: "no" + type: xsd:string + description: > + Format of disk. One of ami, ari, aki, vhd, vmdk, raw, qcow2, vdi, + iso. + - name: minimum_disk_size + optional: "yes" + type: xsd:string + description: Minimum size of disk in GB. Defaults to no minimum. + - name: minimum_memory_size + optional: "yes" + type: xsd:string + description: Minimum size of memory in MB. Defaults to no minimum. + - name: visibility + optional: "yes" + type: xsd:string + description: > + Visibility of the image. One of private, public, shared. Defaults + to 'public'. + - name: protected + optional: "yes" + type: xsd:boolean + description: Determines if image is deletable. Defaults to 'no'. + - name: properties + optional: "yes" + type: xsd:string + description: List of properties to be associated with the image. + - name: image_data_ref + optional: "no" + type: xsd:string + description: > + URL of where the image data is located. URL can be located on the + local disk (URL starts with file://) or located on a HTTP server + (URL starts with http://). + response: *alias_query_response + normal_codes: + - name: Created + value: 201 + error_codes: + - name: Bad Request + value: 400 + - name: Internal Server Error + value: 500 + examples: + - title: Add a software image where the image data is on local disk + description: > + This example demonstrates how to add a software image where the + image data is on the local disk to the image repository using curl + commands. + request: > + curl -i -X POST -H 'Content-Type: application/json' + -H 'Accept: application/json' + http://localhost:4545/v1/images + -d "{\"name\": \"cirros\", \"container_format\": \"bare\", + \"disk_format\": \"qcow2\", \"image_data_ref\": + \"file://scratch/cirros-0.3.0-x86_64-disk.img\"}" + response: > + {"description": "", "container_format": "bare", + "disk_format": "qcow2", "minimum_disk_size": 0, + "visibility": "public", "name": "cirros", "protected": "no", + "availability_status": ["available"], "action": "", + "minimum_memory_size": 0, + "uuid": "9a770e3f-e67d-482d-b592-092226fdd119"} + - title: Add a software image where the image data is on a HTTP server + description: > + This example demonstrates how to add a software image where the + image data is on a remote http server to the image repository using + curl commands. + request: > + curl -i -X POST -H 'Content-Type: application/json' + -H 'Accept: application/json' http://localhost:4545/v1/images + -d "{\"name\": \"cirros\", \"container_format\": \"bare\", + \"disk_format\": \"qcow2\", \"image_data_ref\": + \"http://192.168.204.15:4545/file/cirros-0.3.0-x86_64-disk.img\"}" + response: > + {"description": "", "container_format": "bare", + "disk_format": "qcow2", "minimum_disk_size": 0, + "visibility": "public", "name": "cirros", + "protected": "no", "availability_status": ["available"], + "action": "", "minimum_memory_size": 0, + "uuid": "4ebfeb7c-c783-4c2d-a1cd-1558f06c6dba"} + + - id: update_image + name: Update Image + description: Allows updating a software image in the image repository. + method: PUT + urls: + - /v1/images/{image_uuid} + request: + - name: description + optional: "yes" + type: xsd:string + description: > + Description of the image. + - name: minimum_disk_size + optional: "yes" + type: xsd:string + description: Minimum size of disk in GB. + - name: minimum_memory_size + optional: "yes" + type: xsd:string + description: Minimum size of memory in MB. + - name: visibility + optional: "yes" + type: xsd:string + description: > + Visibility of the image. One of private, public, shared. + - name: protected + optional: "yes" + type: xsd:boolean + description: Determines if image is deletable. + response: *alias_query_response + normal_codes: + - name: Okay + value: 200 + error_codes: + - name: Bad Request + value: 400 + - name: Not Found + value: 404 + - name: Internal Server Error + value: 500 + examples: + - title: Update a software image + description: > + This example demonstrates how to update attributes of a software + image in the image repository using curl commands. + request: > + curl -i -X PUT -H 'Content-Type: application/json' + -H 'Accept: application/json' + http://localhost:4545/v1/images/9a770e3f-e67d-482d-b592-092226fdd119 + -d "{\"description\": \"new cirros image\", + \"minimum_disk_size\": \"1\", \"minimum_memory_size\": \"512\", + \"visibility\": \"private\", \"protected\": false}" + response: > + {"description": "new cirros image", "container_format": "bare", + "disk_format": "qcow2", "minimum_disk_size": 1, + "visibility": "private", "name": "cirros", "protected": "yes", + "availability_status": ["available"], "action": "", + "minimum_memory_size": 512, + "uuid": "9a770e3f-e67d-482d-b592-092226fdd119"} + + - id: delete_image + name: Delete Image + description: Allows deleting a software image from the image repository. + method: DELETE + urls: + - /v1/images/{image_uuid} + normal_codes: + - name: No Content + value: 204 + error_codes: + - name: Bad Request + value: 400 + - name: Not Found + value: 404 + - name: Internal Server Error + value: 500 + examples: + - title: Delete a software image + description: > + This example demonstrates how to delete a particular software image + from the image repository using curl commands. + request: > + curl -i -X DELETE -H 'Content-Type: application/json' + -H 'Accept: application/json' + http://localhost:4545/v1/images/9a770e3f-e67d-482d-b592-092226fdd119 + response: > + No response is returned. + + - id: query_images + name: Query Image(s) + description: > + Allows querying the information of all the software images or a single + software image from the image repository. + method: GET + urls: + - /v1/images + - /v1/images/{image_uuid} + response: *alias_query_response + normal_codes: + - name: Okay + value: 200 + error_codes: + - name: Not Found + value: 404 + - name: Internal Server Error + value: 500 + examples: + - title: Query all software images + description: > + This example demonstrates how to query all software images from the + image repository using curl commands. + request: > + curl -i -X GET -H 'Content-Type: application/json' + -H 'Accept: application/json' http://localhost:4545/v1/images + response: > + [{"description": "new cirros image", "container_format": "bare", + "disk_format": "qcow2", "minimum_disk_size": 1, + "visibility": "private", "name": "cirros", "protected": "yes", + "availability_status": ["available"], "action": "", + "minimum_memory_size": 512, + "uuid": "9a770e3f-e67d-482d-b592-092226fdd119"}, + {"description": "", "container_format": "bare", + "disk_format": "qcow2", "minimum_disk_size": 1, + "visibility": "public", "name": "vim-test-image", "protected": "no", + "availability_status": ["available"], "action": "", + "minimum_memory_size": 512, + "uuid": "def2d2c8-7411-45a0-a9e1-fc343c641b07"}] + - title: Query a software image + description: > + This example demonstrates how to query a particular software image + from the image repository using curl commands. + request: > + curl -i -X GET -H 'Content-Type: application/json' + -H 'Accept: application/json' + http://localhost:4545/v1/images/def2d2c8-7411-45a0-a9e1-fc343c641b07 + response: > + {"description": "", "container_format": "bare", + "disk_format": "qcow2", "minimum_disk_size": 1, + "visibility": "public", "name": "vim-test-image", + "protected": "no", "availability_status": ["available"], + "action": "", "minimum_memory_size": 512, + "uuid": "def2d2c8-7411-45a0-a9e1-fc343c641b07"} + +api_group_list: + - *alias_api_group_images diff --git a/nfv/nfv-docs/documentation/virtualised_compute_resource.yaml b/nfv/nfv-docs/documentation/virtualised_compute_resource.yaml new file mode 100755 index 00000000..67641b85 --- /dev/null +++ b/nfv/nfv-docs/documentation/virtualised_compute_resource.yaml @@ -0,0 +1,6 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +page_title: Virtualised Compute Resource diff --git a/nfv/nfv-docs/documentation/virtualised_network_resource.yaml b/nfv/nfv-docs/documentation/virtualised_network_resource.yaml new file mode 100755 index 00000000..ce4cedcb --- /dev/null +++ b/nfv/nfv-docs/documentation/virtualised_network_resource.yaml @@ -0,0 +1,295 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +page_title: Virtualised Network Resource +page_description: > + These interfaces allows an authorized consumer functional block to manage + virtualized network resources in the VIM. + +api_networks: &api_networks_grouping + name: Networks + data_model: network_model.svg + api_listing: + - id: allocate_network + name: Allocate Network + description: Allows for the instantiation and assignment of a network. + method: POST + urls: + - /v1/virtualised_resources/networks + request: + - name: network_resource_id + optional: "no" + type: string + description: Name of the network. + - name: reservation_id + optional: "yes" + type: string + description: Unique identifier representing a resource reservation. + - name: network_resource_type + optional: "no" + type: string + description: > + Type of virtualised network resource being allocated. One of + network, subnet. + - name: type_network_data + optional: "yes" + type: NetworkType + description: > + The network data about the particular network resource to create. + - name: type_subnet_data + optional: "yes" + type: NetworkSubnetType + description: > + The subnet data about the particular sub-network resource to + create. + - name: meta_data + optional: "yes" + type: key-value pairs + description: List of key-value pairs. + response: + - name: operation_result + type: string + description: Result of the request, indicating pass or failed. + - name: network_data + type: NetworkResourceType + description: > + The network data about the particular network resource that + was created. + - name: subnet_data + type: NetworkSubnetResourceType + description: > + The subnet data about the particular sub-network resource that + was created. + - name: message + type: string + description: > + Additional information about the allocation request result. + normal_codes: + - name: Created + value: 201 + error_codes: + - name: Bad Request + value: 400 + - name: Internal Server Error + value: 500 + examples: + - title: Allocate a network + description: > + This example demonstrates how to allocate a network with subnets. + request: > + cat > network_ipv4_subnets_request.txt + {"network_resource_id": "network-ipv4-subnets", "network_resource_type": "network", + "type_network_data": { + "type_of_network": "vlan", "type_of_segment": "351", "wrs_physical_network": "physnet-x", "is_shared": true, + "layer3_attributes": [ + {"ip_version": "4", "is_dhcp_enabled": true, "gateway_ip": "192.168.12.1", "wrs_subnet_ip": "192.168.12.0", + "wrs_subnet_prefix": 24} + ]}} + + curl -i -X POST -H 'Content-Type: application/json' + -H 'Accept: application/json' + http://localhost:4545/v1/virtualised_resources/networks + -d @network_ipv4_subnets_request.txt + response: > + {"operation_result": null, "message": null, + "network_data": { + "network_attributes": { + "is_shared": true, "type_of_network": "vlan", "type_of_segment": "351", + "layer3_attributes": [ + {"is_dhcp_enabled": true, "meta_data": null, "wrs_subnet_ip": "192.168.12.0", "gateway_ip": "192.168.12.1", + "ip_version": "4", "wrs_subnet_prefix": 24} + ], + "sharing_criteria": null, "bandwidth": 0, "meta_data": null, "network_qos": []}, + "resource_id": "network-ipv4-subnets"}} + + - id: update_network + name: Update Network + description: Allows updating the information for a network. + method: PATCH + urls: + - /v1/virtualised_resources/networks + request: + - name: network_resource_id + optional: "no" + type: string + description: Name of the network. + - name: update_network_data + optional: "yes" + type: NetworkType + description: > + The network data to use to update a particular network resource. + - name: update_subnet_data + optional: "yes" + type: NetworkSubnetType + description: > + The subnet data to use to update a particular sub-network resource. + - name: meta_data + optional: "yes" + type: key-value pairs + description: List of key-value pairs. + response: + - name: operation_result + type: string + description: Result of the request, indicating pass or failed. + - name: network_resource_id + type: string + description: Name of the network. + - name: network_data + type: NetworkResourceType + description: > + The network data about the particular network resource that + was updated. + - name: subnet_data + type: NetworkSubnetResourceType + description: > + The subnet data about the particular sub-network resource that + was updated. + - name: message + type: string + description: Additional information about the update request result. + normal_codes: + - name: Okay + value: 200 + error_codes: + - name: Bad Request + value: 400 + - name: Internal Server Error + value: 500 + examples: + - title: Update a network + description: > + This example demonstrates how to update a network with subnets. + request: > + cat > network_update_ipv4_ipv6_subnets_request.txt + {"network_resource_id": "network-ipv4-subnets", + "update_network_data": { + "layer3_attributes": [ + {"ip_version": "4", "is_dhcp_enabled": true, "wrs_subnet_ip": "192.168.14.0", "wrs_subnet_prefix": "24"}, + {"ip_version": "6", "is_dhcp_enabled": false, "gateway_ip": "3FFE::1", "wrs_subnet_ip": "3FFE::", + "wrs_subnet_prefix": "16"}, + {"ip_version": "4", "is_dhcp_enabled": false, "gateway_ip": "192.168.15.1", "wrs_subnet_ip": "192.168.15.0", + "wrs_subnet_prefix": "24"} + ]}} + + curl -i -X PATCH -H 'Content-Type: application/json' + -H 'Accept: application/json' + http://localhost:4545/v1/virtualised_resources/networks + -d @network_update_ipv4_ipv6_subnets_request.txt + response: > + {"operation_result": null, "message": null, + "network_data": { + "network_attributes": { + "is_shared": false, "type_of_network": "vlan", "type_of_segment": "351", + "layer3_attributes": [ + {"network_id": "143fab9e-b4d2-4239-867b-d52e254d6d86", "is_dhcp_enabled": false, "meta_data": null, + "wrs_subnet_ip": "192.168.15.0", "gateway_ip": "192.168.15.1", + "ip_version": "4", "wrs_subnet_prefix": 24}, + {"network_id": "143fab9e-b4d2-4239-867b-d52e254d6d86", "is_dhcp_enabled": false, "meta_data": null, + "wrs_subnet_ip": "3ffe::", "gateway_ip": "3FFE::1", "ip_version": "6", "wrs_subnet_prefix": 16}, + {"network_id": "143fab9e-b4d2-4239-867b-d52e254d6d86", "is_dhcp_enabled": true, "meta_data": null, + "wrs_subnet_ip": "192.168.14.0", "gateway_ip": "192.168.14.1", "ip_version": "4", "wrs_subnet_prefix": 24} + ], + "sharing_criteria": null, "bandwidth": 0, "meta_data": null, "network_qos": []}, + "resource_id": "network-ipv4-subnets"}} + + - id: delete_network + name: Delete Network + description: Allows deleting a network. + method: DELETE + urls: + - /v1/virtualised_resources/networks + request: + - name: network_resource_ids + optional: "no" + type: string list + description: List of network resource identifiers to be deleted. + response: + - name: operation_result + type: string + description: Result of the request, indicating pass or failed. + - name: network_resource_ids + type: list of strings + description: > + List of network resource identifiers that have been deleted. + - name: message + type: string + description: Additional information about the delete request result. + normal_codes: + - name: Okay + value: 200 + error_codes: + - name: Bad Request + value: 400 + - name: Internal Server Error + value: 500 + examples: + - title: Delete a network + description: > + This example demonstrates how to delete a network. + request: > + cat > network_delete_request.txt + {"network_resource_ids": ["network-ipv4-subnets"]} + + curl -i -X DELETE -H 'Content-Type: application/json' + -H 'Accept: application/json' + http://localhost:4545/v1/virtualised_resources/networks + -d @network_delete_request.txt + response: > + {"operation_result": null, "network_resource_ids": + ["network-ipv4-subnets"], "message": null} + + - id: get_network + name: Get Network(s) + description: > + Allows querying the information of all the networks or a + a single network. + method: GET + urls: + - /v1/virtualised_resources/networks + - /v1/virtualised_resources/networks/{resource_id} + response: + - name: operation_result + type: string + description: Result of the request, indicating pass or failed. + - name: query_result + type: NetworkResourceType(s) + description: A list of network data that was request. + - name: message + type: string + description: Additional information about the query request result. + normal_codes: + - name: Okay + value: 200 + error_codes: + - name: Bad Request + value: 400 + - name: Internal Server Error + value: 500 + examples: + - title: Query a network + description: > + This example demonstrates how to query a network. + request: > + curl -i -X GET -H 'Content-Type: application/json' + -H 'Accept: application/json' + http://localhost:4545/v1/virtualised_resources/networks/network-ipv4-subnets + response: > + {"operation_result": null, "message": null, + "query_result": [ + {"network_attributes": { + "is_shared": false, "type_of_network": "vlan", "type_of_segment": "351", + "layer3_attributes": [ + {"network_id": "143fab9e-b4d2-4239-867b-d52e254d6d86", "is_dhcp_enabled": false, "meta_data": null, + "wrs_subnet_ip": "192.168.15.0", "gateway_ip": "192.168.15.1", "ip_version": "4", "wrs_subnet_prefix": 24}, + {"network_id": "143fab9e-b4d2-4239-867b-d52e254d6d86", "is_dhcp_enabled": false, "meta_data": null, + "wrs_subnet_ip": "3ffe::", "gateway_ip": "3FFE::1", "ip_version": "6", "wrs_subnet_prefix": 16}, + {"network_id": "143fab9e-b4d2-4239-867b-d52e254d6d86", "is_dhcp_enabled": true, "meta_data": null, + "wrs_subnet_ip": "192.168.14.0", "gateway_ip": "192.168.14.1", "ip_version": "4", "wrs_subnet_prefix": 24} + ], + "sharing_criteria": null, "bandwidth": 0, "meta_data": null, "network_qos": []}, + "resource_id": "network-ipv4-subnets"}]} + +api_group_list: + - *api_networks_grouping diff --git a/nfv/nfv-docs/documentation/virtualised_storage_resource.yaml b/nfv/nfv-docs/documentation/virtualised_storage_resource.yaml new file mode 100755 index 00000000..ab6ad83d --- /dev/null +++ b/nfv/nfv-docs/documentation/virtualised_storage_resource.yaml @@ -0,0 +1,6 @@ +# +# Copyright (c) 2015-2016 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +page_title: Virtualised Storage Resource \ No newline at end of file diff --git a/nfv/nfv-docs/html_layout/conf.py b/nfv/nfv-docs/html_layout/conf.py new file mode 100644 index 00000000..a03f9435 --- /dev/null +++ b/nfv/nfv-docs/html_layout/conf.py @@ -0,0 +1,354 @@ +# -*- coding: utf-8 -*- +# +# VIM documentation build configuration file, created by +# sphinx-quickstart on Tue Mar 31 19:18:06 2015. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os +import shlex + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'VIM' +copyright = u'2015, WindRiver' +author = u'WindRiver' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = 'v1.0' +# The full version, including alpha/beta/rc tags. +release = 'v1.0' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = [] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'alabaster' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' +#html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# Now only 'ja' uses this config value +#html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +#html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'VIMdoc' + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', + +# Latex figure (float) alignment +#'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'VIM.tex', u'VIM Documentation', + u'WindRiver', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'vim', u'VIM Documentation', + [author], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'VIM', u'VIM Documentation', + author, 'VIM', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False + + +# -- Options for Epub output ---------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = project +epub_author = author +epub_publisher = author +epub_copyright = copyright + +# The basename for the epub file. It defaults to the project name. +#epub_basename = project + +# The HTML theme for the epub output. Since the default themes are not optimized +# for small screen space, using the same theme for HTML and epub output is +# usually not wise. This defaults to 'epub', a theme designed to save visual +# space. +#epub_theme = 'epub' + +# The language of the text. It defaults to the language option +# or 'en' if the language is not set. +#epub_language = '' + +# The scheme of the identifier. Typical schemes are ISBN or URL. +#epub_scheme = '' + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +#epub_identifier = '' + +# A unique identification for the text. +#epub_uid = '' + +# A tuple containing the cover image and cover page html template filenames. +#epub_cover = () + +# A sequence of (type, uri, title) tuples for the guide element of content.opf. +#epub_guide = () + +# HTML files that should be inserted before the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_pre_files = [] + +# HTML files shat should be inserted after the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_post_files = [] + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ['search.html'] + +# The depth of the table of contents in toc.ncx. +#epub_tocdepth = 3 + +# Allow duplicate toc entries. +#epub_tocdup = True + +# Choose between 'default' and 'includehidden'. +#epub_tocscope = 'default' + +# Fix unsupported image types using the Pillow. +#epub_fix_images = False + +# Scale large images. +#epub_max_image_width = 0 + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#epub_show_urls = 'inline' + +# If false, no index is generated. +#epub_use_index = True diff --git a/nfv/nfv-docs/html_layout/css/bootstrap.css b/nfv/nfv-docs/html_layout/css/bootstrap.css new file mode 100755 index 00000000..ba6fac53 --- /dev/null +++ b/nfv/nfv-docs/html_layout/css/bootstrap.css @@ -0,0 +1,6566 @@ +/*! + * Bootstrap v3.3.2 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ + +/*! normalize.css v3.0.2 | MIT License | git.io/normalize */ +html { + font-family: sans-serif; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +body { + margin: 0; +} +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section, +summary { + display: block; +} +audio, +canvas, +progress, +video { + display: inline-block; + vertical-align: baseline; +} +audio:not([controls]) { + display: none; + height: 0; +} +[hidden], +template { + display: none; +} +a { + background-color: transparent; +} +a:active, +a:hover { + outline: 0; +} +abbr[title] { + border-bottom: 1px dotted; +} +b, +strong { + font-weight: bold; +} +dfn { + font-style: italic; +} +h1 { + margin: .67em 0; + font-size: 2em; +} +mark { + color: #000; + background: #ff0; +} +small { + font-size: 80%; +} +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} +sup { + top: -.5em; +} +sub { + bottom: -.25em; +} +img { + border: 0; +} +svg:not(:root) { + overflow: hidden; +} +figure { + margin: 1em 40px; +} +hr { + height: 0; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} +pre { + overflow: auto; +} +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} +button, +input, +optgroup, +select, +textarea { + margin: 0; + font: inherit; + color: inherit; +} +button { + overflow: visible; +} +button, +select { + text-transform: none; +} +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; +} +button[disabled], +html input[disabled] { + cursor: default; +} +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} +input { + line-height: normal; +} +input[type="checkbox"], +input[type="radio"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 0; +} +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} +input[type="search"] { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield; +} +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} +fieldset { + padding: .35em .625em .75em; + margin: 0 2px; + border: 1px solid #c0c0c0; +} +legend { + padding: 0; + border: 0; +} +textarea { + overflow: auto; +} +optgroup { + font-weight: bold; +} +table { + border-spacing: 0; + border-collapse: collapse; +} +td, +th { + padding: 0; +} +/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ +@media print { + *, + *:before, + *:after { + color: #000 !important; + text-shadow: none !important; + background: transparent !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + a[href]:after { + content: " (" attr(href) ")"; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + a[href^="#"]:after, + a[href^="javascript:"]:after { + content: ""; + } + pre, + blockquote { + border: 1px solid #999; + + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + img { + max-width: 100% !important; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } + select { + background: #fff !important; + } + .navbar { + display: none; + } + .btn > .caret, + .dropup > .btn > .caret { + border-top-color: #000 !important; + } + .label { + border: 1px solid #000; + } + .table { + border-collapse: collapse !important; + } + .table td, + .table th { + background-color: #fff !important; + } + .table-bordered th, + .table-bordered td { + border: 1px solid #ddd !important; + } +} +@font-face { + font-family: 'Glyphicons Halflings'; + + src: url('../fonts/glyphicons-halflings-regular.eot'); + src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); +} +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: normal; + line-height: 1; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.glyphicon-asterisk:before { + content: "\2a"; +} +.glyphicon-plus:before { + content: "\2b"; +} +.glyphicon-euro:before, +.glyphicon-eur:before { + content: "\20ac"; +} +.glyphicon-minus:before { + content: "\2212"; +} +.glyphicon-cloud:before { + content: "\2601"; +} +.glyphicon-envelope:before { + content: "\2709"; +} +.glyphicon-pencil:before { + content: "\270f"; +} +.glyphicon-glass:before { + content: "\e001"; +} +.glyphicon-music:before { + content: "\e002"; +} +.glyphicon-search:before { + content: "\e003"; +} +.glyphicon-heart:before { + content: "\e005"; +} +.glyphicon-star:before { + content: "\e006"; +} +.glyphicon-star-empty:before { + content: "\e007"; +} +.glyphicon-user:before { + content: "\e008"; +} +.glyphicon-film:before { + content: "\e009"; +} +.glyphicon-th-large:before { + content: "\e010"; +} +.glyphicon-th:before { + content: "\e011"; +} +.glyphicon-th-list:before { + content: "\e012"; +} +.glyphicon-ok:before { + content: "\e013"; +} +.glyphicon-remove:before { + content: "\e014"; +} +.glyphicon-zoom-in:before { + content: "\e015"; +} +.glyphicon-zoom-out:before { + content: "\e016"; +} +.glyphicon-off:before { + content: "\e017"; +} +.glyphicon-signal:before { + content: "\e018"; +} +.glyphicon-cog:before { + content: "\e019"; +} +.glyphicon-trash:before { + content: "\e020"; +} +.glyphicon-home:before { + content: "\e021"; +} +.glyphicon-file:before { + content: "\e022"; +} +.glyphicon-time:before { + content: "\e023"; +} +.glyphicon-road:before { + content: "\e024"; +} +.glyphicon-download-alt:before { + content: "\e025"; +} +.glyphicon-download:before { + content: "\e026"; +} +.glyphicon-upload:before { + content: "\e027"; +} +.glyphicon-inbox:before { + content: "\e028"; +} +.glyphicon-play-circle:before { + content: "\e029"; +} +.glyphicon-repeat:before { + content: "\e030"; +} +.glyphicon-refresh:before { + content: "\e031"; +} +.glyphicon-list-alt:before { + content: "\e032"; +} +.glyphicon-lock:before { + content: "\e033"; +} +.glyphicon-flag:before { + content: "\e034"; +} +.glyphicon-headphones:before { + content: "\e035"; +} +.glyphicon-volume-off:before { + content: "\e036"; +} +.glyphicon-volume-down:before { + content: "\e037"; +} +.glyphicon-volume-up:before { + content: "\e038"; +} +.glyphicon-qrcode:before { + content: "\e039"; +} +.glyphicon-barcode:before { + content: "\e040"; +} +.glyphicon-tag:before { + content: "\e041"; +} +.glyphicon-tags:before { + content: "\e042"; +} +.glyphicon-book:before { + content: "\e043"; +} +.glyphicon-bookmark:before { + content: "\e044"; +} +.glyphicon-print:before { + content: "\e045"; +} +.glyphicon-camera:before { + content: "\e046"; +} +.glyphicon-font:before { + content: "\e047"; +} +.glyphicon-bold:before { + content: "\e048"; +} +.glyphicon-italic:before { + content: "\e049"; +} +.glyphicon-text-height:before { + content: "\e050"; +} +.glyphicon-text-width:before { + content: "\e051"; +} +.glyphicon-align-left:before { + content: "\e052"; +} +.glyphicon-align-center:before { + content: "\e053"; +} +.glyphicon-align-right:before { + content: "\e054"; +} +.glyphicon-align-justify:before { + content: "\e055"; +} +.glyphicon-list:before { + content: "\e056"; +} +.glyphicon-indent-left:before { + content: "\e057"; +} +.glyphicon-indent-right:before { + content: "\e058"; +} +.glyphicon-facetime-video:before { + content: "\e059"; +} +.glyphicon-picture:before { + content: "\e060"; +} +.glyphicon-map-marker:before { + content: "\e062"; +} +.glyphicon-adjust:before { + content: "\e063"; +} +.glyphicon-tint:before { + content: "\e064"; +} +.glyphicon-edit:before { + content: "\e065"; +} +.glyphicon-share:before { + content: "\e066"; +} +.glyphicon-check:before { + content: "\e067"; +} +.glyphicon-move:before { + content: "\e068"; +} +.glyphicon-step-backward:before { + content: "\e069"; +} +.glyphicon-fast-backward:before { + content: "\e070"; +} +.glyphicon-backward:before { + content: "\e071"; +} +.glyphicon-play:before { + content: "\e072"; +} +.glyphicon-pause:before { + content: "\e073"; +} +.glyphicon-stop:before { + content: "\e074"; +} +.glyphicon-forward:before { + content: "\e075"; +} +.glyphicon-fast-forward:before { + content: "\e076"; +} +.glyphicon-step-forward:before { + content: "\e077"; +} +.glyphicon-eject:before { + content: "\e078"; +} +.glyphicon-chevron-left:before { + content: "\e079"; +} +.glyphicon-chevron-right:before { + content: "\e080"; +} +.glyphicon-plus-sign:before { + content: "\e081"; +} +.glyphicon-minus-sign:before { + content: "\e082"; +} +.glyphicon-remove-sign:before { + content: "\e083"; +} +.glyphicon-ok-sign:before { + content: "\e084"; +} +.glyphicon-question-sign:before { + content: "\e085"; +} +.glyphicon-info-sign:before { + content: "\e086"; +} +.glyphicon-screenshot:before { + content: "\e087"; +} +.glyphicon-remove-circle:before { + content: "\e088"; +} +.glyphicon-ok-circle:before { + content: "\e089"; +} +.glyphicon-ban-circle:before { + content: "\e090"; +} +.glyphicon-arrow-left:before { + content: "\e091"; +} +.glyphicon-arrow-right:before { + content: "\e092"; +} +.glyphicon-arrow-up:before { + content: "\e093"; +} +.glyphicon-arrow-down:before { + content: "\e094"; +} +.glyphicon-share-alt:before { + content: "\e095"; +} +.glyphicon-resize-full:before { + content: "\e096"; +} +.glyphicon-resize-small:before { + content: "\e097"; +} +.glyphicon-exclamation-sign:before { + content: "\e101"; +} +.glyphicon-gift:before { + content: "\e102"; +} +.glyphicon-leaf:before { + content: "\e103"; +} +.glyphicon-fire:before { + content: "\e104"; +} +.glyphicon-eye-open:before { + content: "\e105"; +} +.glyphicon-eye-close:before { + content: "\e106"; +} +.glyphicon-warning-sign:before { + content: "\e107"; +} +.glyphicon-plane:before { + content: "\e108"; +} +.glyphicon-calendar:before { + content: "\e109"; +} +.glyphicon-random:before { + content: "\e110"; +} +.glyphicon-comment:before { + content: "\e111"; +} +.glyphicon-magnet:before { + content: "\e112"; +} +.glyphicon-chevron-up:before { + content: "\e113"; +} +.glyphicon-chevron-down:before { + content: "\e114"; +} +.glyphicon-retweet:before { + content: "\e115"; +} +.glyphicon-shopping-cart:before { + content: "\e116"; +} +.glyphicon-folder-close:before { + content: "\e117"; +} +.glyphicon-folder-open:before { + content: "\e118"; +} +.glyphicon-resize-vertical:before { + content: "\e119"; +} +.glyphicon-resize-horizontal:before { + content: "\e120"; +} +.glyphicon-hdd:before { + content: "\e121"; +} +.glyphicon-bullhorn:before { + content: "\e122"; +} +.glyphicon-bell:before { + content: "\e123"; +} +.glyphicon-certificate:before { + content: "\e124"; +} +.glyphicon-thumbs-up:before { + content: "\e125"; +} +.glyphicon-thumbs-down:before { + content: "\e126"; +} +.glyphicon-hand-right:before { + content: "\e127"; +} +.glyphicon-hand-left:before { + content: "\e128"; +} +.glyphicon-hand-up:before { + content: "\e129"; +} +.glyphicon-hand-down:before { + content: "\e130"; +} +.glyphicon-circle-arrow-right:before { + content: "\e131"; +} +.glyphicon-circle-arrow-left:before { + content: "\e132"; +} +.glyphicon-circle-arrow-up:before { + content: "\e133"; +} +.glyphicon-circle-arrow-down:before { + content: "\e134"; +} +.glyphicon-globe:before { + content: "\e135"; +} +.glyphicon-wrench:before { + content: "\e136"; +} +.glyphicon-tasks:before { + content: "\e137"; +} +.glyphicon-filter:before { + content: "\e138"; +} +.glyphicon-briefcase:before { + content: "\e139"; +} +.glyphicon-fullscreen:before { + content: "\e140"; +} +.glyphicon-dashboard:before { + content: "\e141"; +} +.glyphicon-paperclip:before { + content: "\e142"; +} +.glyphicon-heart-empty:before { + content: "\e143"; +} +.glyphicon-link:before { + content: "\e144"; +} +.glyphicon-phone:before { + content: "\e145"; +} +.glyphicon-pushpin:before { + content: "\e146"; +} +.glyphicon-usd:before { + content: "\e148"; +} +.glyphicon-gbp:before { + content: "\e149"; +} +.glyphicon-sort:before { + content: "\e150"; +} +.glyphicon-sort-by-alphabet:before { + content: "\e151"; +} +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152"; +} +.glyphicon-sort-by-order:before { + content: "\e153"; +} +.glyphicon-sort-by-order-alt:before { + content: "\e154"; +} +.glyphicon-sort-by-attributes:before { + content: "\e155"; +} +.glyphicon-sort-by-attributes-alt:before { + content: "\e156"; +} +.glyphicon-unchecked:before { + content: "\e157"; +} +.glyphicon-expand:before { + content: "\e158"; +} +.glyphicon-collapse-down:before { + content: "\e159"; +} +.glyphicon-collapse-up:before { + content: "\e160"; +} +.glyphicon-log-in:before { + content: "\e161"; +} +.glyphicon-flash:before { + content: "\e162"; +} +.glyphicon-log-out:before { + content: "\e163"; +} +.glyphicon-new-window:before { + content: "\e164"; +} +.glyphicon-record:before { + content: "\e165"; +} +.glyphicon-save:before { + content: "\e166"; +} +.glyphicon-open:before { + content: "\e167"; +} +.glyphicon-saved:before { + content: "\e168"; +} +.glyphicon-import:before { + content: "\e169"; +} +.glyphicon-export:before { + content: "\e170"; +} +.glyphicon-send:before { + content: "\e171"; +} +.glyphicon-floppy-disk:before { + content: "\e172"; +} +.glyphicon-floppy-saved:before { + content: "\e173"; +} +.glyphicon-floppy-remove:before { + content: "\e174"; +} +.glyphicon-floppy-save:before { + content: "\e175"; +} +.glyphicon-floppy-open:before { + content: "\e176"; +} +.glyphicon-credit-card:before { + content: "\e177"; +} +.glyphicon-transfer:before { + content: "\e178"; +} +.glyphicon-cutlery:before { + content: "\e179"; +} +.glyphicon-header:before { + content: "\e180"; +} +.glyphicon-compressed:before { + content: "\e181"; +} +.glyphicon-earphone:before { + content: "\e182"; +} +.glyphicon-phone-alt:before { + content: "\e183"; +} +.glyphicon-tower:before { + content: "\e184"; +} +.glyphicon-stats:before { + content: "\e185"; +} +.glyphicon-sd-video:before { + content: "\e186"; +} +.glyphicon-hd-video:before { + content: "\e187"; +} +.glyphicon-subtitles:before { + content: "\e188"; +} +.glyphicon-sound-stereo:before { + content: "\e189"; +} +.glyphicon-sound-dolby:before { + content: "\e190"; +} +.glyphicon-sound-5-1:before { + content: "\e191"; +} +.glyphicon-sound-6-1:before { + content: "\e192"; +} +.glyphicon-sound-7-1:before { + content: "\e193"; +} +.glyphicon-copyright-mark:before { + content: "\e194"; +} +.glyphicon-registration-mark:before { + content: "\e195"; +} +.glyphicon-cloud-download:before { + content: "\e197"; +} +.glyphicon-cloud-upload:before { + content: "\e198"; +} +.glyphicon-tree-conifer:before { + content: "\e199"; +} +.glyphicon-tree-deciduous:before { + content: "\e200"; +} +.glyphicon-cd:before { + content: "\e201"; +} +.glyphicon-save-file:before { + content: "\e202"; +} +.glyphicon-open-file:before { + content: "\e203"; +} +.glyphicon-level-up:before { + content: "\e204"; +} +.glyphicon-copy:before { + content: "\e205"; +} +.glyphicon-paste:before { + content: "\e206"; +} +.glyphicon-alert:before { + content: "\e209"; +} +.glyphicon-equalizer:before { + content: "\e210"; +} +.glyphicon-king:before { + content: "\e211"; +} +.glyphicon-queen:before { + content: "\e212"; +} +.glyphicon-pawn:before { + content: "\e213"; +} +.glyphicon-bishop:before { + content: "\e214"; +} +.glyphicon-knight:before { + content: "\e215"; +} +.glyphicon-baby-formula:before { + content: "\e216"; +} +.glyphicon-tent:before { + content: "\26fa"; +} +.glyphicon-blackboard:before { + content: "\e218"; +} +.glyphicon-bed:before { + content: "\e219"; +} +.glyphicon-apple:before { + content: "\f8ff"; +} +.glyphicon-erase:before { + content: "\e221"; +} +.glyphicon-hourglass:before { + content: "\231b"; +} +.glyphicon-lamp:before { + content: "\e223"; +} +.glyphicon-duplicate:before { + content: "\e224"; +} +.glyphicon-piggy-bank:before { + content: "\e225"; +} +.glyphicon-scissors:before { + content: "\e226"; +} +.glyphicon-bitcoin:before { + content: "\e227"; +} +.glyphicon-yen:before { + content: "\00a5"; +} +.glyphicon-ruble:before { + content: "\20bd"; +} +.glyphicon-scale:before { + content: "\e230"; +} +.glyphicon-ice-lolly:before { + content: "\e231"; +} +.glyphicon-ice-lolly-tasted:before { + content: "\e232"; +} +.glyphicon-education:before { + content: "\e233"; +} +.glyphicon-option-horizontal:before { + content: "\e234"; +} +.glyphicon-option-vertical:before { + content: "\e235"; +} +.glyphicon-menu-hamburger:before { + content: "\e236"; +} +.glyphicon-modal-window:before { + content: "\e237"; +} +.glyphicon-oil:before { + content: "\e238"; +} +.glyphicon-grain:before { + content: "\e239"; +} +.glyphicon-sunglasses:before { + content: "\e240"; +} +.glyphicon-text-size:before { + content: "\e241"; +} +.glyphicon-text-color:before { + content: "\e242"; +} +.glyphicon-text-background:before { + content: "\e243"; +} +.glyphicon-object-align-top:before { + content: "\e244"; +} +.glyphicon-object-align-bottom:before { + content: "\e245"; +} +.glyphicon-object-align-horizontal:before { + content: "\e246"; +} +.glyphicon-object-align-left:before { + content: "\e247"; +} +.glyphicon-object-align-vertical:before { + content: "\e248"; +} +.glyphicon-object-align-right:before { + content: "\e249"; +} +.glyphicon-triangle-right:before { + content: "\e250"; +} +.glyphicon-triangle-left:before { + content: "\e251"; +} +.glyphicon-triangle-bottom:before { + content: "\e252"; +} +.glyphicon-triangle-top:before { + content: "\e253"; +} +.glyphicon-console:before { + content: "\e254"; +} +.glyphicon-superscript:before { + content: "\e255"; +} +.glyphicon-subscript:before { + content: "\e256"; +} +.glyphicon-menu-left:before { + content: "\e257"; +} +.glyphicon-menu-right:before { + content: "\e258"; +} +.glyphicon-menu-down:before { + content: "\e259"; +} +.glyphicon-menu-up:before { + content: "\e260"; +} +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +*:before, +*:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +html { + font-size: 10px; + + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #333; + background-color: #fff; +} +input, +button, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} +a { + color: #337ab7; + text-decoration: none; +} +a:hover, +a:focus { + color: #23527c; + text-decoration: underline; +} +a:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +figure { + margin: 0; +} +img { + vertical-align: middle; +} +.img-responsive, +.thumbnail > img, +.thumbnail a > img, +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + max-width: 100%; + height: auto; +} +.img-rounded { + border-radius: 6px; +} +.img-thumbnail { + display: inline-block; + max-width: 100%; + height: auto; + padding: 4px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: all .2s ease-in-out; + -o-transition: all .2s ease-in-out; + transition: all .2s ease-in-out; +} +.img-circle { + border-radius: 50%; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eee; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} +.sr-only-focusable:active, +.sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; +} +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: inherit; + font-weight: 500; + line-height: 1.1; + color: inherit; +} +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small, +.h1 small, +.h2 small, +.h3 small, +.h4 small, +.h5 small, +.h6 small, +h1 .small, +h2 .small, +h3 .small, +h4 .small, +h5 .small, +h6 .small, +.h1 .small, +.h2 .small, +.h3 .small, +.h4 .small, +.h5 .small, +.h6 .small { + font-weight: normal; + line-height: 1; + color: #777; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 10px; +} +h1 small, +.h1 small, +h2 small, +.h2 small, +h3 small, +.h3 small, +h1 .small, +.h1 .small, +h2 .small, +.h2 .small, +h3 .small, +.h3 .small { + font-size: 65%; +} +h4, +.h4, +h5, +.h5, +h6, +.h6 { + margin-top: 10px; + margin-bottom: 10px; +} +h4 small, +.h4 small, +h5 small, +.h5 small, +h6 small, +.h6 small, +h4 .small, +.h4 .small, +h5 .small, +.h5 .small, +h6 .small, +.h6 .small { + font-size: 75%; +} +h1, +.h1 { + font-size: 36px; +} +h2, +.h2 { + font-size: 30px; +} +h3, +.h3 { + font-size: 24px; +} +h4, +.h4 { + font-size: 18px; +} +h5, +.h5 { + font-size: 14px; +} +h6, +.h6 { + font-size: 12px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 16px; + font-weight: 300; + line-height: 1.4; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +small, +.small { + font-size: 85%; +} +mark, +.mark { + padding: .2em; + background-color: #fcf8e3; +} +.text-left { + text-align: left; +} +.text-right { + text-align: right; +} +.text-center { + text-align: center; +} +.text-justify { + text-align: justify; +} +.text-nowrap { + white-space: nowrap; +} +.text-lowercase { + text-transform: lowercase; +} +.text-uppercase { + text-transform: uppercase; +} +.text-capitalize { + text-transform: capitalize; +} +.text-muted { + color: #777; +} +.text-primary { + color: #337ab7; +} +a.text-primary:hover { + color: #286090; +} +.text-success { + color: #3c763d; +} +a.text-success:hover { + color: #2b542c; +} +.text-info { + color: #31708f; +} +a.text-info:hover { + color: #245269; +} +.text-warning { + color: #8a6d3b; +} +a.text-warning:hover { + color: #66512c; +} +.text-danger { + color: #a94442; +} +a.text-danger:hover { + color: #843534; +} +.bg-primary { + color: #fff; + background-color: #337ab7; +} +a.bg-primary:hover { + background-color: #286090; +} +.bg-success { + background-color: #dff0d8; +} +a.bg-success:hover { + background-color: #c1e2b3; +} +.bg-info { + background-color: #d9edf7; +} +a.bg-info:hover { + background-color: #afd9ee; +} +.bg-warning { + background-color: #fcf8e3; +} +a.bg-warning:hover { + background-color: #f7ecb5; +} +.bg-danger { + background-color: #f2dede; +} +a.bg-danger:hover { + background-color: #e4b9b9; +} +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #eee; +} +ul, +ol { + margin-top: 0; + margin-bottom: 10px; +} +ul ul, +ol ul, +ul ol, +ol ol { + margin-bottom: 0; +} +.list-unstyled { + padding-left: 0; + list-style: none; +} +.list-inline { + padding-left: 0; + margin-left: -5px; + list-style: none; +} +.list-inline > li { + display: inline-block; + padding-right: 5px; + padding-left: 5px; +} +dl { + margin-top: 0; + margin-bottom: 20px; +} +dt, +dd { + line-height: 1.42857143; +} +dt { + font-weight: bold; +} +dd { + margin-left: 0; +} +@media (min-width: 768px) { + .dl-horizontal dt { + float: left; + width: 160px; + overflow: hidden; + clear: left; + text-align: right; + text-overflow: ellipsis; + white-space: nowrap; + } + .dl-horizontal dd { + margin-left: 180px; + } +} +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #777; +} +.initialism { + font-size: 90%; + text-transform: uppercase; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 17.5px; + border-left: 5px solid #eee; +} +blockquote p:last-child, +blockquote ul:last-child, +blockquote ol:last-child { + margin-bottom: 0; +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #777; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} +.blockquote-reverse, +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + text-align: right; + border-right: 5px solid #eee; + border-left: 0; +} +.blockquote-reverse footer:before, +blockquote.pull-right footer:before, +.blockquote-reverse small:before, +blockquote.pull-right small:before, +.blockquote-reverse .small:before, +blockquote.pull-right .small:before { + content: ''; +} +.blockquote-reverse footer:after, +blockquote.pull-right footer:after, +.blockquote-reverse small:after, +blockquote.pull-right small:after, +.blockquote-reverse .small:after, +blockquote.pull-right .small:after { + content: '\00A0 \2014'; +} +address { + margin-bottom: 20px; + font-style: normal; + line-height: 1.42857143; +} +code, +kbd, +pre, +samp { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; +} +code { + padding: 2px 4px; + font-size: 90%; + color: #c7254e; + background-color: #f9f2f4; + border-radius: 4px; +} +kbd { + padding: 2px 4px; + font-size: 90%; + color: #fff; + background-color: #333; + border-radius: 3px; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); +} +kbd kbd { + padding: 0; + font-size: 100%; + font-weight: bold; + -webkit-box-shadow: none; + box-shadow: none; +} +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: #333; + word-break: break-all; + word-wrap: break-word; + background-color: #f5f5f5; + border: 1px solid #ccc; + border-radius: 4px; +} +pre code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0; +} +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} +.container { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +@media (min-width: 768px) { + .container { + width: 750px; + } +} +@media (min-width: 992px) { + .container { + width: 970px; + } +} +@media (min-width: 1200px) { + .container { + width: 1170px; + } +} +.container-fluid { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +.row { + margin-right: -15px; + margin-left: -15px; +} +.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { + position: relative; + min-height: 1px; + padding-right: 15px; + padding-left: 15px; +} +.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { + float: left; +} +.col-xs-12 { + width: 100%; +} +.col-xs-11 { + width: 91.66666667%; +} +.col-xs-10 { + width: 83.33333333%; +} +.col-xs-9 { + width: 75%; +} +.col-xs-8 { + width: 66.66666667%; +} +.col-xs-7 { + width: 58.33333333%; +} +.col-xs-6 { + width: 50%; +} +.col-xs-5 { + width: 41.66666667%; +} +.col-xs-4 { + width: 33.33333333%; +} +.col-xs-3 { + width: 25%; +} +.col-xs-2 { + width: 16.66666667%; +} +.col-xs-1 { + width: 8.33333333%; +} +.col-xs-pull-12 { + right: 100%; +} +.col-xs-pull-11 { + right: 91.66666667%; +} +.col-xs-pull-10 { + right: 83.33333333%; +} +.col-xs-pull-9 { + right: 75%; +} +.col-xs-pull-8 { + right: 66.66666667%; +} +.col-xs-pull-7 { + right: 58.33333333%; +} +.col-xs-pull-6 { + right: 50%; +} +.col-xs-pull-5 { + right: 41.66666667%; +} +.col-xs-pull-4 { + right: 33.33333333%; +} +.col-xs-pull-3 { + right: 25%; +} +.col-xs-pull-2 { + right: 16.66666667%; +} +.col-xs-pull-1 { + right: 8.33333333%; +} +.col-xs-pull-0 { + right: auto; +} +.col-xs-push-12 { + left: 100%; +} +.col-xs-push-11 { + left: 91.66666667%; +} +.col-xs-push-10 { + left: 83.33333333%; +} +.col-xs-push-9 { + left: 75%; +} +.col-xs-push-8 { + left: 66.66666667%; +} +.col-xs-push-7 { + left: 58.33333333%; +} +.col-xs-push-6 { + left: 50%; +} +.col-xs-push-5 { + left: 41.66666667%; +} +.col-xs-push-4 { + left: 33.33333333%; +} +.col-xs-push-3 { + left: 25%; +} +.col-xs-push-2 { + left: 16.66666667%; +} +.col-xs-push-1 { + left: 8.33333333%; +} +.col-xs-push-0 { + left: auto; +} +.col-xs-offset-12 { + margin-left: 100%; +} +.col-xs-offset-11 { + margin-left: 91.66666667%; +} +.col-xs-offset-10 { + margin-left: 83.33333333%; +} +.col-xs-offset-9 { + margin-left: 75%; +} +.col-xs-offset-8 { + margin-left: 66.66666667%; +} +.col-xs-offset-7 { + margin-left: 58.33333333%; +} +.col-xs-offset-6 { + margin-left: 50%; +} +.col-xs-offset-5 { + margin-left: 41.66666667%; +} +.col-xs-offset-4 { + margin-left: 33.33333333%; +} +.col-xs-offset-3 { + margin-left: 25%; +} +.col-xs-offset-2 { + margin-left: 16.66666667%; +} +.col-xs-offset-1 { + margin-left: 8.33333333%; +} +.col-xs-offset-0 { + margin-left: 0; +} +@media (min-width: 768px) { + .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { + float: left; + } + .col-sm-12 { + width: 100%; + } + .col-sm-11 { + width: 91.66666667%; + } + .col-sm-10 { + width: 83.33333333%; + } + .col-sm-9 { + width: 75%; + } + .col-sm-8 { + width: 66.66666667%; + } + .col-sm-7 { + width: 58.33333333%; + } + .col-sm-6 { + width: 50%; + } + .col-sm-5 { + width: 41.66666667%; + } + .col-sm-4 { + width: 33.33333333%; + } + .col-sm-3 { + width: 25%; + } + .col-sm-2 { + width: 16.66666667%; + } + .col-sm-1 { + width: 8.33333333%; + } + .col-sm-pull-12 { + right: 100%; + } + .col-sm-pull-11 { + right: 91.66666667%; + } + .col-sm-pull-10 { + right: 83.33333333%; + } + .col-sm-pull-9 { + right: 75%; + } + .col-sm-pull-8 { + right: 66.66666667%; + } + .col-sm-pull-7 { + right: 58.33333333%; + } + .col-sm-pull-6 { + right: 50%; + } + .col-sm-pull-5 { + right: 41.66666667%; + } + .col-sm-pull-4 { + right: 33.33333333%; + } + .col-sm-pull-3 { + right: 25%; + } + .col-sm-pull-2 { + right: 16.66666667%; + } + .col-sm-pull-1 { + right: 8.33333333%; + } + .col-sm-pull-0 { + right: auto; + } + .col-sm-push-12 { + left: 100%; + } + .col-sm-push-11 { + left: 91.66666667%; + } + .col-sm-push-10 { + left: 83.33333333%; + } + .col-sm-push-9 { + left: 75%; + } + .col-sm-push-8 { + left: 66.66666667%; + } + .col-sm-push-7 { + left: 58.33333333%; + } + .col-sm-push-6 { + left: 50%; + } + .col-sm-push-5 { + left: 41.66666667%; + } + .col-sm-push-4 { + left: 33.33333333%; + } + .col-sm-push-3 { + left: 25%; + } + .col-sm-push-2 { + left: 16.66666667%; + } + .col-sm-push-1 { + left: 8.33333333%; + } + .col-sm-push-0 { + left: auto; + } + .col-sm-offset-12 { + margin-left: 100%; + } + .col-sm-offset-11 { + margin-left: 91.66666667%; + } + .col-sm-offset-10 { + margin-left: 83.33333333%; + } + .col-sm-offset-9 { + margin-left: 75%; + } + .col-sm-offset-8 { + margin-left: 66.66666667%; + } + .col-sm-offset-7 { + margin-left: 58.33333333%; + } + .col-sm-offset-6 { + margin-left: 50%; + } + .col-sm-offset-5 { + margin-left: 41.66666667%; + } + .col-sm-offset-4 { + margin-left: 33.33333333%; + } + .col-sm-offset-3 { + margin-left: 25%; + } + .col-sm-offset-2 { + margin-left: 16.66666667%; + } + .col-sm-offset-1 { + margin-left: 8.33333333%; + } + .col-sm-offset-0 { + margin-left: 0; + } +} +@media (min-width: 992px) { + .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { + float: left; + } + .col-md-12 { + width: 100%; + } + .col-md-11 { + width: 91.66666667%; + } + .col-md-10 { + width: 83.33333333%; + } + .col-md-9 { + width: 75%; + } + .col-md-8 { + width: 66.66666667%; + } + .col-md-7 { + width: 58.33333333%; + } + .col-md-6 { + width: 50%; + } + .col-md-5 { + width: 41.66666667%; + } + .col-md-4 { + width: 33.33333333%; + } + .col-md-3 { + width: 25%; + } + .col-md-2 { + width: 16.66666667%; + } + .col-md-1 { + width: 8.33333333%; + } + .col-md-pull-12 { + right: 100%; + } + .col-md-pull-11 { + right: 91.66666667%; + } + .col-md-pull-10 { + right: 83.33333333%; + } + .col-md-pull-9 { + right: 75%; + } + .col-md-pull-8 { + right: 66.66666667%; + } + .col-md-pull-7 { + right: 58.33333333%; + } + .col-md-pull-6 { + right: 50%; + } + .col-md-pull-5 { + right: 41.66666667%; + } + .col-md-pull-4 { + right: 33.33333333%; + } + .col-md-pull-3 { + right: 25%; + } + .col-md-pull-2 { + right: 16.66666667%; + } + .col-md-pull-1 { + right: 8.33333333%; + } + .col-md-pull-0 { + right: auto; + } + .col-md-push-12 { + left: 100%; + } + .col-md-push-11 { + left: 91.66666667%; + } + .col-md-push-10 { + left: 83.33333333%; + } + .col-md-push-9 { + left: 75%; + } + .col-md-push-8 { + left: 66.66666667%; + } + .col-md-push-7 { + left: 58.33333333%; + } + .col-md-push-6 { + left: 50%; + } + .col-md-push-5 { + left: 41.66666667%; + } + .col-md-push-4 { + left: 33.33333333%; + } + .col-md-push-3 { + left: 25%; + } + .col-md-push-2 { + left: 16.66666667%; + } + .col-md-push-1 { + left: 8.33333333%; + } + .col-md-push-0 { + left: auto; + } + .col-md-offset-12 { + margin-left: 100%; + } + .col-md-offset-11 { + margin-left: 91.66666667%; + } + .col-md-offset-10 { + margin-left: 83.33333333%; + } + .col-md-offset-9 { + margin-left: 75%; + } + .col-md-offset-8 { + margin-left: 66.66666667%; + } + .col-md-offset-7 { + margin-left: 58.33333333%; + } + .col-md-offset-6 { + margin-left: 50%; + } + .col-md-offset-5 { + margin-left: 41.66666667%; + } + .col-md-offset-4 { + margin-left: 33.33333333%; + } + .col-md-offset-3 { + margin-left: 25%; + } + .col-md-offset-2 { + margin-left: 16.66666667%; + } + .col-md-offset-1 { + margin-left: 8.33333333%; + } + .col-md-offset-0 { + margin-left: 0; + } +} +@media (min-width: 1200px) { + .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { + float: left; + } + .col-lg-12 { + width: 100%; + } + .col-lg-11 { + width: 91.66666667%; + } + .col-lg-10 { + width: 83.33333333%; + } + .col-lg-9 { + width: 75%; + } + .col-lg-8 { + width: 66.66666667%; + } + .col-lg-7 { + width: 58.33333333%; + } + .col-lg-6 { + width: 50%; + } + .col-lg-5 { + width: 41.66666667%; + } + .col-lg-4 { + width: 33.33333333%; + } + .col-lg-3 { + width: 25%; + } + .col-lg-2 { + width: 16.66666667%; + } + .col-lg-1 { + width: 8.33333333%; + } + .col-lg-pull-12 { + right: 100%; + } + .col-lg-pull-11 { + right: 91.66666667%; + } + .col-lg-pull-10 { + right: 83.33333333%; + } + .col-lg-pull-9 { + right: 75%; + } + .col-lg-pull-8 { + right: 66.66666667%; + } + .col-lg-pull-7 { + right: 58.33333333%; + } + .col-lg-pull-6 { + right: 50%; + } + .col-lg-pull-5 { + right: 41.66666667%; + } + .col-lg-pull-4 { + right: 33.33333333%; + } + .col-lg-pull-3 { + right: 25%; + } + .col-lg-pull-2 { + right: 16.66666667%; + } + .col-lg-pull-1 { + right: 8.33333333%; + } + .col-lg-pull-0 { + right: auto; + } + .col-lg-push-12 { + left: 100%; + } + .col-lg-push-11 { + left: 91.66666667%; + } + .col-lg-push-10 { + left: 83.33333333%; + } + .col-lg-push-9 { + left: 75%; + } + .col-lg-push-8 { + left: 66.66666667%; + } + .col-lg-push-7 { + left: 58.33333333%; + } + .col-lg-push-6 { + left: 50%; + } + .col-lg-push-5 { + left: 41.66666667%; + } + .col-lg-push-4 { + left: 33.33333333%; + } + .col-lg-push-3 { + left: 25%; + } + .col-lg-push-2 { + left: 16.66666667%; + } + .col-lg-push-1 { + left: 8.33333333%; + } + .col-lg-push-0 { + left: auto; + } + .col-lg-offset-12 { + margin-left: 100%; + } + .col-lg-offset-11 { + margin-left: 91.66666667%; + } + .col-lg-offset-10 { + margin-left: 83.33333333%; + } + .col-lg-offset-9 { + margin-left: 75%; + } + .col-lg-offset-8 { + margin-left: 66.66666667%; + } + .col-lg-offset-7 { + margin-left: 58.33333333%; + } + .col-lg-offset-6 { + margin-left: 50%; + } + .col-lg-offset-5 { + margin-left: 41.66666667%; + } + .col-lg-offset-4 { + margin-left: 33.33333333%; + } + .col-lg-offset-3 { + margin-left: 25%; + } + .col-lg-offset-2 { + margin-left: 16.66666667%; + } + .col-lg-offset-1 { + margin-left: 8.33333333%; + } + .col-lg-offset-0 { + margin-left: 0; + } +} +table { + background-color: transparent; +} +caption { + padding-top: 8px; + padding-bottom: 8px; + color: #777; + text-align: left; +} +th { + text-align: left; +} +.table { + width: 100%; + max-width: 100%; + margin-bottom: 20px; +} +.table > thead > tr > th, +.table > tbody > tr > th, +.table > tfoot > tr > th, +.table > thead > tr > td, +.table > tbody > tr > td, +.table > tfoot > tr > td { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid #ddd; +} +.table > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid #ddd; +} +.table > caption + thead > tr:first-child > th, +.table > colgroup + thead > tr:first-child > th, +.table > thead:first-child > tr:first-child > th, +.table > caption + thead > tr:first-child > td, +.table > colgroup + thead > tr:first-child > td, +.table > thead:first-child > tr:first-child > td { + border-top: 0; +} +.table > tbody + tbody { + border-top: 2px solid #ddd; +} +.table .table { + background-color: #fff; +} +.table-condensed > thead > tr > th, +.table-condensed > tbody > tr > th, +.table-condensed > tfoot > tr > th, +.table-condensed > thead > tr > td, +.table-condensed > tbody > tr > td, +.table-condensed > tfoot > tr > td { + padding: 5px; +} +.table-bordered { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > tbody > tr > th, +.table-bordered > tfoot > tr > th, +.table-bordered > thead > tr > td, +.table-bordered > tbody > tr > td, +.table-bordered > tfoot > tr > td { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > thead > tr > td { + border-bottom-width: 2px; +} +.table-striped > tbody > tr:nth-of-type(odd) { + background-color: #f9f9f9; +} +.table-hover > tbody > tr:hover { + background-color: #f5f5f5; +} +table col[class*="col-"] { + position: static; + display: table-column; + float: none; +} +table td[class*="col-"], +table th[class*="col-"] { + position: static; + display: table-cell; + float: none; +} +.table > thead > tr > td.active, +.table > tbody > tr > td.active, +.table > tfoot > tr > td.active, +.table > thead > tr > th.active, +.table > tbody > tr > th.active, +.table > tfoot > tr > th.active, +.table > thead > tr.active > td, +.table > tbody > tr.active > td, +.table > tfoot > tr.active > td, +.table > thead > tr.active > th, +.table > tbody > tr.active > th, +.table > tfoot > tr.active > th { + background-color: #f5f5f5; +} +.table-hover > tbody > tr > td.active:hover, +.table-hover > tbody > tr > th.active:hover, +.table-hover > tbody > tr.active:hover > td, +.table-hover > tbody > tr:hover > .active, +.table-hover > tbody > tr.active:hover > th { + background-color: #e8e8e8; +} +.table > thead > tr > td.success, +.table > tbody > tr > td.success, +.table > tfoot > tr > td.success, +.table > thead > tr > th.success, +.table > tbody > tr > th.success, +.table > tfoot > tr > th.success, +.table > thead > tr.success > td, +.table > tbody > tr.success > td, +.table > tfoot > tr.success > td, +.table > thead > tr.success > th, +.table > tbody > tr.success > th, +.table > tfoot > tr.success > th { + background-color: #dff0d8; +} +.table-hover > tbody > tr > td.success:hover, +.table-hover > tbody > tr > th.success:hover, +.table-hover > tbody > tr.success:hover > td, +.table-hover > tbody > tr:hover > .success, +.table-hover > tbody > tr.success:hover > th { + background-color: #d0e9c6; +} +.table > thead > tr > td.info, +.table > tbody > tr > td.info, +.table > tfoot > tr > td.info, +.table > thead > tr > th.info, +.table > tbody > tr > th.info, +.table > tfoot > tr > th.info, +.table > thead > tr.info > td, +.table > tbody > tr.info > td, +.table > tfoot > tr.info > td, +.table > thead > tr.info > th, +.table > tbody > tr.info > th, +.table > tfoot > tr.info > th { + background-color: #d9edf7; +} +.table-hover > tbody > tr > td.info:hover, +.table-hover > tbody > tr > th.info:hover, +.table-hover > tbody > tr.info:hover > td, +.table-hover > tbody > tr:hover > .info, +.table-hover > tbody > tr.info:hover > th { + background-color: #c4e3f3; +} +.table > thead > tr > td.warning, +.table > tbody > tr > td.warning, +.table > tfoot > tr > td.warning, +.table > thead > tr > th.warning, +.table > tbody > tr > th.warning, +.table > tfoot > tr > th.warning, +.table > thead > tr.warning > td, +.table > tbody > tr.warning > td, +.table > tfoot > tr.warning > td, +.table > thead > tr.warning > th, +.table > tbody > tr.warning > th, +.table > tfoot > tr.warning > th { + background-color: #fcf8e3; +} +.table-hover > tbody > tr > td.warning:hover, +.table-hover > tbody > tr > th.warning:hover, +.table-hover > tbody > tr.warning:hover > td, +.table-hover > tbody > tr:hover > .warning, +.table-hover > tbody > tr.warning:hover > th { + background-color: #faf2cc; +} +.table > thead > tr > td.danger, +.table > tbody > tr > td.danger, +.table > tfoot > tr > td.danger, +.table > thead > tr > th.danger, +.table > tbody > tr > th.danger, +.table > tfoot > tr > th.danger, +.table > thead > tr.danger > td, +.table > tbody > tr.danger > td, +.table > tfoot > tr.danger > td, +.table > thead > tr.danger > th, +.table > tbody > tr.danger > th, +.table > tfoot > tr.danger > th { + background-color: #f2dede; +} +.table-hover > tbody > tr > td.danger:hover, +.table-hover > tbody > tr > th.danger:hover, +.table-hover > tbody > tr.danger:hover > td, +.table-hover > tbody > tr:hover > .danger, +.table-hover > tbody > tr.danger:hover > th { + background-color: #ebcccc; +} +.table-responsive { + min-height: .01%; + overflow-x: auto; +} +@media screen and (max-width: 767px) { + .table-responsive { + width: 100%; + margin-bottom: 15px; + overflow-y: hidden; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 1px solid #ddd; + } + .table-responsive > .table { + margin-bottom: 0; + } + .table-responsive > .table > thead > tr > th, + .table-responsive > .table > tbody > tr > th, + .table-responsive > .table > tfoot > tr > th, + .table-responsive > .table > thead > tr > td, + .table-responsive > .table > tbody > tr > td, + .table-responsive > .table > tfoot > tr > td { + white-space: nowrap; + } + .table-responsive > .table-bordered { + border: 0; + } + .table-responsive > .table-bordered > thead > tr > th:first-child, + .table-responsive > .table-bordered > tbody > tr > th:first-child, + .table-responsive > .table-bordered > tfoot > tr > th:first-child, + .table-responsive > .table-bordered > thead > tr > td:first-child, + .table-responsive > .table-bordered > tbody > tr > td:first-child, + .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; + } + .table-responsive > .table-bordered > thead > tr > th:last-child, + .table-responsive > .table-bordered > tbody > tr > th:last-child, + .table-responsive > .table-bordered > tfoot > tr > th:last-child, + .table-responsive > .table-bordered > thead > tr > td:last-child, + .table-responsive > .table-bordered > tbody > tr > td:last-child, + .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; + } + .table-responsive > .table-bordered > tbody > tr:last-child > th, + .table-responsive > .table-bordered > tfoot > tr:last-child > th, + .table-responsive > .table-bordered > tbody > tr:last-child > td, + .table-responsive > .table-bordered > tfoot > tr:last-child > td { + border-bottom: 0; + } +} +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0; +} +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: #333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} +label { + display: inline-block; + max-width: 100%; + margin-bottom: 5px; + font-weight: bold; +} +input[type="search"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + line-height: normal; +} +input[type="file"] { + display: block; +} +input[type="range"] { + display: block; + width: 100%; +} +select[multiple], +select[size] { + height: auto; +} +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +output { + display: block; + padding-top: 7px; + font-size: 14px; + line-height: 1.42857143; + color: #555; +} +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: #555; + background-color: #fff; + background-image: none; + border: 1px solid #ccc; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; +} +.form-control:focus { + border-color: #66afe9; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); +} +.form-control::-moz-placeholder { + color: #999; + opacity: 1; +} +.form-control:-ms-input-placeholder { + color: #999; +} +.form-control::-webkit-input-placeholder { + color: #999; +} +.form-control[disabled], +.form-control[readonly], +fieldset[disabled] .form-control { + cursor: not-allowed; + background-color: #eee; + opacity: 1; +} +textarea.form-control { + height: auto; +} +input[type="search"] { + -webkit-appearance: none; +} +@media screen and (-webkit-min-device-pixel-ratio: 0) { + input[type="date"], + input[type="time"], + input[type="datetime-local"], + input[type="month"] { + line-height: 34px; + } + input[type="date"].input-sm, + input[type="time"].input-sm, + input[type="datetime-local"].input-sm, + input[type="month"].input-sm, + .input-group-sm input[type="date"], + .input-group-sm input[type="time"], + .input-group-sm input[type="datetime-local"], + .input-group-sm input[type="month"] { + line-height: 30px; + } + input[type="date"].input-lg, + input[type="time"].input-lg, + input[type="datetime-local"].input-lg, + input[type="month"].input-lg, + .input-group-lg input[type="date"], + .input-group-lg input[type="time"], + .input-group-lg input[type="datetime-local"], + .input-group-lg input[type="month"] { + line-height: 46px; + } +} +.form-group { + margin-bottom: 15px; +} +.radio, +.checkbox { + position: relative; + display: block; + margin-top: 10px; + margin-bottom: 10px; +} +.radio label, +.checkbox label { + min-height: 20px; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + cursor: pointer; +} +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + position: absolute; + margin-top: 4px \9; + margin-left: -20px; +} +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; +} +.radio-inline, +.checkbox-inline { + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + vertical-align: middle; + cursor: pointer; +} +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; +} +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"].disabled, +input[type="checkbox"].disabled, +fieldset[disabled] input[type="radio"], +fieldset[disabled] input[type="checkbox"] { + cursor: not-allowed; +} +.radio-inline.disabled, +.checkbox-inline.disabled, +fieldset[disabled] .radio-inline, +fieldset[disabled] .checkbox-inline { + cursor: not-allowed; +} +.radio.disabled label, +.checkbox.disabled label, +fieldset[disabled] .radio label, +fieldset[disabled] .checkbox label { + cursor: not-allowed; +} +.form-control-static { + padding-top: 7px; + padding-bottom: 7px; + margin-bottom: 0; +} +.form-control-static.input-lg, +.form-control-static.input-sm { + padding-right: 0; + padding-left: 0; +} +.input-sm { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-sm { + height: 30px; + line-height: 30px; +} +textarea.input-sm, +select[multiple].input-sm { + height: auto; +} +.form-group-sm .form-control { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.form-group-sm .form-control { + height: 30px; + line-height: 30px; +} +textarea.form-group-sm .form-control, +select[multiple].form-group-sm .form-control { + height: auto; +} +.form-group-sm .form-control-static { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; +} +.input-lg { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +select.input-lg { + height: 46px; + line-height: 46px; +} +textarea.input-lg, +select[multiple].input-lg { + height: auto; +} +.form-group-lg .form-control { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +select.form-group-lg .form-control { + height: 46px; + line-height: 46px; +} +textarea.form-group-lg .form-control, +select[multiple].form-group-lg .form-control { + height: auto; +} +.form-group-lg .form-control-static { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; +} +.has-feedback { + position: relative; +} +.has-feedback .form-control { + padding-right: 42.5px; +} +.form-control-feedback { + position: absolute; + top: 0; + right: 0; + z-index: 2; + display: block; + width: 34px; + height: 34px; + line-height: 34px; + text-align: center; + pointer-events: none; +} +.input-lg + .form-control-feedback { + width: 46px; + height: 46px; + line-height: 46px; +} +.input-sm + .form-control-feedback { + width: 30px; + height: 30px; + line-height: 30px; +} +.has-success .help-block, +.has-success .control-label, +.has-success .radio, +.has-success .checkbox, +.has-success .radio-inline, +.has-success .checkbox-inline, +.has-success.radio label, +.has-success.checkbox label, +.has-success.radio-inline label, +.has-success.checkbox-inline label { + color: #3c763d; +} +.has-success .form-control { + border-color: #3c763d; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-success .form-control:focus { + border-color: #2b542c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; +} +.has-success .input-group-addon { + color: #3c763d; + background-color: #dff0d8; + border-color: #3c763d; +} +.has-success .form-control-feedback { + color: #3c763d; +} +.has-warning .help-block, +.has-warning .control-label, +.has-warning .radio, +.has-warning .checkbox, +.has-warning .radio-inline, +.has-warning .checkbox-inline, +.has-warning.radio label, +.has-warning.checkbox label, +.has-warning.radio-inline label, +.has-warning.checkbox-inline label { + color: #8a6d3b; +} +.has-warning .form-control { + border-color: #8a6d3b; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-warning .form-control:focus { + border-color: #66512c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; +} +.has-warning .input-group-addon { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #8a6d3b; +} +.has-warning .form-control-feedback { + color: #8a6d3b; +} +.has-error .help-block, +.has-error .control-label, +.has-error .radio, +.has-error .checkbox, +.has-error .radio-inline, +.has-error .checkbox-inline, +.has-error.radio label, +.has-error.checkbox label, +.has-error.radio-inline label, +.has-error.checkbox-inline label { + color: #a94442; +} +.has-error .form-control { + border-color: #a94442; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-error .form-control:focus { + border-color: #843534; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; +} +.has-error .input-group-addon { + color: #a94442; + background-color: #f2dede; + border-color: #a94442; +} +.has-error .form-control-feedback { + color: #a94442; +} +.has-feedback label ~ .form-control-feedback { + top: 25px; +} +.has-feedback label.sr-only ~ .form-control-feedback { + top: 0; +} +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #737373; +} +@media (min-width: 768px) { + .form-inline .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .form-inline .form-control-static { + display: inline-block; + } + .form-inline .input-group { + display: inline-table; + vertical-align: middle; + } + .form-inline .input-group .input-group-addon, + .form-inline .input-group .input-group-btn, + .form-inline .input-group .form-control { + width: auto; + } + .form-inline .input-group > .form-control { + width: 100%; + } + .form-inline .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio, + .form-inline .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio label, + .form-inline .checkbox label { + padding-left: 0; + } + .form-inline .radio input[type="radio"], + .form-inline .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .form-inline .has-feedback .form-control-feedback { + top: 0; + } +} +.form-horizontal .radio, +.form-horizontal .checkbox, +.form-horizontal .radio-inline, +.form-horizontal .checkbox-inline { + padding-top: 7px; + margin-top: 0; + margin-bottom: 0; +} +.form-horizontal .radio, +.form-horizontal .checkbox { + min-height: 27px; +} +.form-horizontal .form-group { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .form-horizontal .control-label { + padding-top: 7px; + margin-bottom: 0; + text-align: right; + } +} +.form-horizontal .has-feedback .form-control-feedback { + right: 15px; +} +@media (min-width: 768px) { + .form-horizontal .form-group-lg .control-label { + padding-top: 14.333333px; + } +} +@media (min-width: 768px) { + .form-horizontal .form-group-sm .control-label { + padding-top: 6px; + } +} +.btn { + display: inline-block; + padding: 6px 12px; + margin-bottom: 0; + font-size: 14px; + font-weight: normal; + line-height: 1.42857143; + text-align: center; + white-space: nowrap; + vertical-align: middle; + -ms-touch-action: manipulation; + touch-action: manipulation; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.btn:focus, +.btn:active:focus, +.btn.active:focus, +.btn.focus, +.btn:active.focus, +.btn.active.focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.btn:hover, +.btn:focus, +.btn.focus { + color: #333; + text-decoration: none; +} +.btn:active, +.btn.active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn.disabled, +.btn[disabled], +fieldset[disabled] .btn { + pointer-events: none; + cursor: not-allowed; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + box-shadow: none; + opacity: .65; +} +.btn-default { + color: #333; + background-color: #fff; + border-color: #ccc; +} +.btn-default:hover, +.btn-default:focus, +.btn-default.focus, +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + color: #333; + background-color: #e6e6e6; + border-color: #adadad; +} +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + background-image: none; +} +.btn-default.disabled, +.btn-default[disabled], +fieldset[disabled] .btn-default, +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled.focus, +.btn-default[disabled].focus, +fieldset[disabled] .btn-default.focus, +.btn-default.disabled:active, +.btn-default[disabled]:active, +fieldset[disabled] .btn-default:active, +.btn-default.disabled.active, +.btn-default[disabled].active, +fieldset[disabled] .btn-default.active { + background-color: #fff; + border-color: #ccc; +} +.btn-default .badge { + color: #fff; + background-color: #333; +} +.btn-primary { + color: #fff; + background-color: #337ab7; + border-color: #2e6da4; +} +.btn-primary:hover, +.btn-primary:focus, +.btn-primary.focus, +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + color: #fff; + background-color: #286090; + border-color: #204d74; +} +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + background-image: none; +} +.btn-primary.disabled, +.btn-primary[disabled], +fieldset[disabled] .btn-primary, +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled.focus, +.btn-primary[disabled].focus, +fieldset[disabled] .btn-primary.focus, +.btn-primary.disabled:active, +.btn-primary[disabled]:active, +fieldset[disabled] .btn-primary:active, +.btn-primary.disabled.active, +.btn-primary[disabled].active, +fieldset[disabled] .btn-primary.active { + background-color: #337ab7; + border-color: #2e6da4; +} +.btn-primary .badge { + color: #337ab7; + background-color: #fff; +} +.btn-success { + color: #fff; + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success:hover, +.btn-success:focus, +.btn-success.focus, +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + color: #fff; + background-color: #449d44; + border-color: #398439; +} +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + background-image: none; +} +.btn-success.disabled, +.btn-success[disabled], +fieldset[disabled] .btn-success, +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled.focus, +.btn-success[disabled].focus, +fieldset[disabled] .btn-success.focus, +.btn-success.disabled:active, +.btn-success[disabled]:active, +fieldset[disabled] .btn-success:active, +.btn-success.disabled.active, +.btn-success[disabled].active, +fieldset[disabled] .btn-success.active { + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success .badge { + color: #5cb85c; + background-color: #fff; +} +.btn-info { + color: #fff; + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info:hover, +.btn-info:focus, +.btn-info.focus, +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + color: #fff; + background-color: #31b0d5; + border-color: #269abc; +} +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + background-image: none; +} +.btn-info.disabled, +.btn-info[disabled], +fieldset[disabled] .btn-info, +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled.focus, +.btn-info[disabled].focus, +fieldset[disabled] .btn-info.focus, +.btn-info.disabled:active, +.btn-info[disabled]:active, +fieldset[disabled] .btn-info:active, +.btn-info.disabled.active, +.btn-info[disabled].active, +fieldset[disabled] .btn-info.active { + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info .badge { + color: #5bc0de; + background-color: #fff; +} +.btn-warning { + color: #fff; + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning:hover, +.btn-warning:focus, +.btn-warning.focus, +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + color: #fff; + background-color: #ec971f; + border-color: #d58512; +} +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + background-image: none; +} +.btn-warning.disabled, +.btn-warning[disabled], +fieldset[disabled] .btn-warning, +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled.focus, +.btn-warning[disabled].focus, +fieldset[disabled] .btn-warning.focus, +.btn-warning.disabled:active, +.btn-warning[disabled]:active, +fieldset[disabled] .btn-warning:active, +.btn-warning.disabled.active, +.btn-warning[disabled].active, +fieldset[disabled] .btn-warning.active { + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning .badge { + color: #f0ad4e; + background-color: #fff; +} +.btn-danger { + color: #fff; + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger:hover, +.btn-danger:focus, +.btn-danger.focus, +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + color: #fff; + background-color: #c9302c; + border-color: #ac2925; +} +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + background-image: none; +} +.btn-danger.disabled, +.btn-danger[disabled], +fieldset[disabled] .btn-danger, +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled.focus, +.btn-danger[disabled].focus, +fieldset[disabled] .btn-danger.focus, +.btn-danger.disabled:active, +.btn-danger[disabled]:active, +fieldset[disabled] .btn-danger:active, +.btn-danger.disabled.active, +.btn-danger[disabled].active, +fieldset[disabled] .btn-danger.active { + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger .badge { + color: #d9534f; + background-color: #fff; +} +.btn-link { + font-weight: normal; + color: #337ab7; + border-radius: 0; +} +.btn-link, +.btn-link:active, +.btn-link.active, +.btn-link[disabled], +fieldset[disabled] .btn-link { + background-color: transparent; + -webkit-box-shadow: none; + box-shadow: none; +} +.btn-link, +.btn-link:hover, +.btn-link:focus, +.btn-link:active { + border-color: transparent; +} +.btn-link:hover, +.btn-link:focus { + color: #23527c; + text-decoration: underline; + background-color: transparent; +} +.btn-link[disabled]:hover, +fieldset[disabled] .btn-link:hover, +.btn-link[disabled]:focus, +fieldset[disabled] .btn-link:focus { + color: #777; + text-decoration: none; +} +.btn-lg, +.btn-group-lg > .btn { + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +.btn-sm, +.btn-group-sm > .btn { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-xs, +.btn-group-xs > .btn { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-block { + display: block; + width: 100%; +} +.btn-block + .btn-block { + margin-top: 5px; +} +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} +.fade { + opacity: 0; + -webkit-transition: opacity .15s linear; + -o-transition: opacity .15s linear; + transition: opacity .15s linear; +} +.fade.in { + opacity: 1; +} +.collapse { + display: none; + visibility: hidden; +} +.collapse.in { + display: block; + visibility: visible; +} +tr.collapse.in { + display: table-row; +} +tbody.collapse.in { + display: table-row-group; +} +.collapsing { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition-timing-function: ease; + -o-transition-timing-function: ease; + transition-timing-function: ease; + -webkit-transition-duration: .35s; + -o-transition-duration: .35s; + transition-duration: .35s; + -webkit-transition-property: height, visibility; + -o-transition-property: height, visibility; + transition-property: height, visibility; +} +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px solid; + border-right: 4px solid transparent; + border-left: 4px solid transparent; +} +.dropup, +.dropdown { + position: relative; +} +.dropdown-toggle:focus { + outline: 0; +} +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + font-size: 14px; + text-align: left; + list-style: none; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); + box-shadow: 0 6px 12px rgba(0, 0, 0, .175); +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 1.42857143; + color: #333; + white-space: nowrap; +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + color: #262626; + text-decoration: none; + background-color: #f5f5f5; +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #fff; + text-decoration: none; + background-color: #337ab7; + outline: 0; +} +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #777; +} +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + cursor: not-allowed; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.open > .dropdown-menu { + display: block; +} +.open > a { + outline: 0; +} +.dropdown-menu-right { + right: 0; + left: auto; +} +.dropdown-menu-left { + right: auto; + left: 0; +} +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: 12px; + line-height: 1.42857143; + color: #777; + white-space: nowrap; +} +.dropdown-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 990; +} +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + content: ""; + border-top: 0; + border-bottom: 4px solid; +} +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 2px; +} +@media (min-width: 768px) { + .navbar-right .dropdown-menu { + right: 0; + left: auto; + } + .navbar-right .dropdown-menu-left { + right: auto; + left: 0; + } +} +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle; +} +.btn-group > .btn, +.btn-group-vertical > .btn { + position: relative; + float: left; +} +.btn-group > .btn:hover, +.btn-group-vertical > .btn:hover, +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus, +.btn-group > .btn:active, +.btn-group-vertical > .btn:active, +.btn-group > .btn.active, +.btn-group-vertical > .btn.active { + z-index: 2; +} +.btn-group .btn + .btn, +.btn-group .btn + .btn-group, +.btn-group .btn-group + .btn, +.btn-group .btn-group + .btn-group { + margin-left: -1px; +} +.btn-toolbar { + margin-left: -5px; +} +.btn-toolbar .btn-group, +.btn-toolbar .input-group { + float: left; +} +.btn-toolbar > .btn, +.btn-toolbar > .btn-group, +.btn-toolbar > .input-group { + margin-left: 5px; +} +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0; +} +.btn-group > .btn:first-child { + margin-left: 0; +} +.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn:last-child:not(:first-child), +.btn-group > .dropdown-toggle:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group > .btn-group { + float: left; +} +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} +.btn-group > .btn + .dropdown-toggle { + padding-right: 8px; + padding-left: 8px; +} +.btn-group > .btn-lg + .dropdown-toggle { + padding-right: 12px; + padding-left: 12px; +} +.btn-group.open .dropdown-toggle { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn-group.open .dropdown-toggle.btn-link { + -webkit-box-shadow: none; + box-shadow: none; +} +.btn .caret { + margin-left: 0; +} +.btn-lg .caret { + border-width: 5px 5px 0; + border-bottom-width: 0; +} +.dropup .btn-lg .caret { + border-width: 0 5px 5px; +} +.btn-group-vertical > .btn, +.btn-group-vertical > .btn-group, +.btn-group-vertical > .btn-group > .btn { + display: block; + float: none; + width: 100%; + max-width: 100%; +} +.btn-group-vertical > .btn-group > .btn { + float: none; +} +.btn-group-vertical > .btn + .btn, +.btn-group-vertical > .btn + .btn-group, +.btn-group-vertical > .btn-group + .btn, +.btn-group-vertical > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0; +} +.btn-group-vertical > .btn:not(:first-child):not(:last-child) { + border-radius: 0; +} +.btn-group-vertical > .btn:first-child:not(:last-child) { + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn:last-child:not(:first-child) { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-left-radius: 4px; +} +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.btn-group-justified { + display: table; + width: 100%; + table-layout: fixed; + border-collapse: separate; +} +.btn-group-justified > .btn, +.btn-group-justified > .btn-group { + display: table-cell; + float: none; + width: 1%; +} +.btn-group-justified > .btn-group .btn { + width: 100%; +} +.btn-group-justified > .btn-group .dropdown-menu { + left: auto; +} +[data-toggle="buttons"] > .btn input[type="radio"], +[data-toggle="buttons"] > .btn-group > .btn input[type="radio"], +[data-toggle="buttons"] > .btn input[type="checkbox"], +[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none; +} +.input-group { + position: relative; + display: table; + border-collapse: separate; +} +.input-group[class*="col-"] { + float: none; + padding-right: 0; + padding-left: 0; +} +.input-group .form-control { + position: relative; + z-index: 2; + float: left; + width: 100%; + margin-bottom: 0; +} +.input-group-lg > .form-control, +.input-group-lg > .input-group-addon, +.input-group-lg > .input-group-btn > .btn { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +select.input-group-lg > .form-control, +select.input-group-lg > .input-group-addon, +select.input-group-lg > .input-group-btn > .btn { + height: 46px; + line-height: 46px; +} +textarea.input-group-lg > .form-control, +textarea.input-group-lg > .input-group-addon, +textarea.input-group-lg > .input-group-btn > .btn, +select[multiple].input-group-lg > .form-control, +select[multiple].input-group-lg > .input-group-addon, +select[multiple].input-group-lg > .input-group-btn > .btn { + height: auto; +} +.input-group-sm > .form-control, +.input-group-sm > .input-group-addon, +.input-group-sm > .input-group-btn > .btn { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-group-sm > .form-control, +select.input-group-sm > .input-group-addon, +select.input-group-sm > .input-group-btn > .btn { + height: 30px; + line-height: 30px; +} +textarea.input-group-sm > .form-control, +textarea.input-group-sm > .input-group-addon, +textarea.input-group-sm > .input-group-btn > .btn, +select[multiple].input-group-sm > .form-control, +select[multiple].input-group-sm > .input-group-addon, +select[multiple].input-group-sm > .input-group-btn > .btn { + height: auto; +} +.input-group-addon, +.input-group-btn, +.input-group .form-control { + display: table-cell; +} +.input-group-addon:not(:first-child):not(:last-child), +.input-group-btn:not(:first-child):not(:last-child), +.input-group .form-control:not(:first-child):not(:last-child) { + border-radius: 0; +} +.input-group-addon, +.input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle; +} +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: normal; + line-height: 1; + color: #555; + text-align: center; + background-color: #eee; + border: 1px solid #ccc; + border-radius: 4px; +} +.input-group-addon.input-sm { + padding: 5px 10px; + font-size: 12px; + border-radius: 3px; +} +.input-group-addon.input-lg { + padding: 10px 16px; + font-size: 18px; + border-radius: 6px; +} +.input-group-addon input[type="radio"], +.input-group-addon input[type="checkbox"] { + margin-top: 0; +} +.input-group .form-control:first-child, +.input-group-addon:first-child, +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group > .btn, +.input-group-btn:first-child > .dropdown-toggle, +.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.input-group-addon:first-child { + border-right: 0; +} +.input-group .form-control:last-child, +.input-group-addon:last-child, +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group > .btn, +.input-group-btn:last-child > .dropdown-toggle, +.input-group-btn:first-child > .btn:not(:first-child), +.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.input-group-addon:last-child { + border-left: 0; +} +.input-group-btn { + position: relative; + font-size: 0; + white-space: nowrap; +} +.input-group-btn > .btn { + position: relative; +} +.input-group-btn > .btn + .btn { + margin-left: -1px; +} +.input-group-btn > .btn:hover, +.input-group-btn > .btn:focus, +.input-group-btn > .btn:active { + z-index: 2; +} +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group { + margin-right: -1px; +} +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group { + margin-left: -1px; +} +.nav { + padding-left: 0; + margin-bottom: 0; + list-style: none; +} +.nav > li { + position: relative; + display: block; +} +.nav > li > a { + position: relative; + display: block; + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #eee; +} +.nav > li.disabled > a { + color: #777; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #777; + text-decoration: none; + cursor: not-allowed; + background-color: transparent; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eee; + border-color: #337ab7; +} +.nav .nav-divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.nav > li > a > img { + max-width: none; +} +.nav-tabs { + border-bottom: 1px solid #ddd; +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 4px 4px 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eee #eee #ddd; +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555; + cursor: default; + background-color: #fff; + border: 1px solid #ddd; + border-bottom-color: transparent; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-tabs.nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 4px; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #fff; + background-color: #337ab7; +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.tab-content > .tab-pane { + display: none; + visibility: hidden; +} +.tab-content > .active { + display: block; + visibility: visible; +} +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar { + position: relative; + min-height: 50px; + margin-bottom: 20px; + border: 1px solid transparent; +} +@media (min-width: 768px) { + .navbar { + border-radius: 4px; + } +} +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} +.navbar-collapse { + padding-right: 15px; + padding-left: 15px; + overflow-x: visible; + -webkit-overflow-scrolling: touch; + border-top: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); +} +.navbar-collapse.in { + overflow-y: auto; +} +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + visibility: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-right: 0; + padding-left: 0; + } +} +.navbar-fixed-top .navbar-collapse, +.navbar-fixed-bottom .navbar-collapse { + max-height: 340px; +} +@media (max-device-width: 480px) and (orientation: landscape) { + .navbar-fixed-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + max-height: 200px; + } +} +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; +} +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} +.navbar-brand { + float: left; + height: 50px; + padding: 15px 15px; + font-size: 18px; + line-height: 20px; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} +.navbar-brand > img { + display: block; +} +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} +.navbar-toggle { + position: relative; + float: right; + padding: 9px 10px; + margin-top: 8px; + margin-right: 15px; + margin-bottom: 8px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.navbar-toggle:focus { + outline: 0; +} +.navbar-toggle .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px; +} +.navbar-toggle .icon-bar + .icon-bar { + margin-top: 4px; +} +@media (min-width: 768px) { + .navbar-toggle { + display: none; + } +} +.navbar-nav { + margin: 7.5px -15px; +} +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 15px; + padding-bottom: 15px; + } +} +.navbar-form { + padding: 10px 15px; + margin-top: 8px; + margin-right: -15px; + margin-bottom: 8px; + margin-left: -15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); +} +@media (min-width: 768px) { + .navbar-form .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .navbar-form .form-control-static { + display: inline-block; + } + .navbar-form .input-group { + display: inline-table; + vertical-align: middle; + } + .navbar-form .input-group .input-group-addon, + .navbar-form .input-group .input-group-btn, + .navbar-form .input-group .form-control { + width: auto; + } + .navbar-form .input-group > .form-control { + width: 100%; + } + .navbar-form .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio, + .navbar-form .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio label, + .navbar-form .checkbox label { + padding-left: 0; + } + .navbar-form .radio input[type="radio"], + .navbar-form .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .navbar-form .has-feedback .form-control-feedback { + top: 0; + } +} +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } + .navbar-form .form-group:last-child { + margin-bottom: 0; + } +} +@media (min-width: 768px) { + .navbar-form { + width: auto; + padding-top: 0; + padding-bottom: 0; + margin-right: 0; + margin-left: 0; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } +} +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + margin-bottom: 0; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.navbar-btn { + margin-top: 8px; + margin-bottom: 8px; +} +.navbar-btn.btn-sm { + margin-top: 10px; + margin-bottom: 10px; +} +.navbar-btn.btn-xs { + margin-top: 14px; + margin-bottom: 14px; +} +.navbar-text { + margin-top: 15px; + margin-bottom: 15px; +} +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-right: 15px; + margin-left: 15px; + } +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + margin-right: -15px; + } + .navbar-right ~ .navbar-right { + margin-right: 0; + } +} +.navbar-default { + background-color: #f8f8f8; + border-color: #e7e7e7; +} +.navbar-default .navbar-brand { + color: #777; +} +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #5e5e5e; + background-color: transparent; +} +.navbar-default .navbar-text { + color: #777; +} +.navbar-default .navbar-nav > li > a { + color: #777; +} +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #333; + background-color: transparent; +} +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #555; + background-color: #e7e7e7; +} +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #ccc; + background-color: transparent; +} +.navbar-default .navbar-toggle { + border-color: #ddd; +} +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: #ddd; +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #888; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #e7e7e7; +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + color: #555; + background-color: #e7e7e7; +} +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #777; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: #333; + background-color: transparent; + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #555; + background-color: #e7e7e7; + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #ccc; + background-color: transparent; + } +} +.navbar-default .navbar-link { + color: #777; +} +.navbar-default .navbar-link:hover { + color: #333; +} +.navbar-default .btn-link { + color: #777; +} +.navbar-default .btn-link:hover, +.navbar-default .btn-link:focus { + color: #333; +} +.navbar-default .btn-link[disabled]:hover, +fieldset[disabled] .navbar-default .btn-link:hover, +.navbar-default .btn-link[disabled]:focus, +fieldset[disabled] .navbar-default .btn-link:focus { + color: #ccc; +} +.navbar-inverse { + background-color: #222; + border-color: #080808; +} +.navbar-inverse .navbar-brand { + color: #9d9d9d; +} +.navbar-inverse .navbar-brand:hover, +.navbar-inverse .navbar-brand:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-text { + color: #9d9d9d; +} +.navbar-inverse .navbar-nav > li > a { + color: #9d9d9d; +} +.navbar-inverse .navbar-nav > li > a:hover, +.navbar-inverse .navbar-nav > li > a:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-nav > .active > a, +.navbar-inverse .navbar-nav > .active > a:hover, +.navbar-inverse .navbar-nav > .active > a:focus { + color: #fff; + background-color: #080808; +} +.navbar-inverse .navbar-nav > .disabled > a, +.navbar-inverse .navbar-nav > .disabled > a:hover, +.navbar-inverse .navbar-nav > .disabled > a:focus { + color: #444; + background-color: transparent; +} +.navbar-inverse .navbar-toggle { + border-color: #333; +} +.navbar-inverse .navbar-toggle:hover, +.navbar-inverse .navbar-toggle:focus { + background-color: #333; +} +.navbar-inverse .navbar-toggle .icon-bar { + background-color: #fff; +} +.navbar-inverse .navbar-collapse, +.navbar-inverse .navbar-form { + border-color: #101010; +} +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .open > a:hover, +.navbar-inverse .navbar-nav > .open > a:focus { + color: #fff; + background-color: #080808; +} +@media (max-width: 767px) { + .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { + border-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu .divider { + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { + color: #9d9d9d; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { + color: #fff; + background-color: transparent; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #fff; + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #444; + background-color: transparent; + } +} +.navbar-inverse .navbar-link { + color: #9d9d9d; +} +.navbar-inverse .navbar-link:hover { + color: #fff; +} +.navbar-inverse .btn-link { + color: #9d9d9d; +} +.navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link:focus { + color: #fff; +} +.navbar-inverse .btn-link[disabled]:hover, +fieldset[disabled] .navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link[disabled]:focus, +fieldset[disabled] .navbar-inverse .btn-link:focus { + color: #444; +} +.breadcrumb { + padding: 8px 15px; + margin-bottom: 20px; + list-style: none; + background-color: #f5f5f5; + border-radius: 4px; +} +.breadcrumb > li { + display: inline-block; +} +.breadcrumb > li + li:before { + padding: 0 5px; + color: #ccc; + content: "/\00a0"; +} +.breadcrumb > .active { + color: #777; +} +.pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 4px; +} +.pagination > li { + display: inline; +} +.pagination > li > a, +.pagination > li > span { + position: relative; + float: left; + padding: 6px 12px; + margin-left: -1px; + line-height: 1.42857143; + color: #337ab7; + text-decoration: none; + background-color: #fff; + border: 1px solid #ddd; +} +.pagination > li:first-child > a, +.pagination > li:first-child > span { + margin-left: 0; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} +.pagination > li:last-child > a, +.pagination > li:last-child > span { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + color: #23527c; + background-color: #eee; + border-color: #ddd; +} +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 2; + color: #fff; + cursor: default; + background-color: #337ab7; + border-color: #337ab7; +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #777; + cursor: not-allowed; + background-color: #fff; + border-color: #ddd; +} +.pagination-lg > li > a, +.pagination-lg > li > span { + padding: 10px 16px; + font-size: 18px; +} +.pagination-lg > li:first-child > a, +.pagination-lg > li:first-child > span { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; +} +.pagination-lg > li:last-child > a, +.pagination-lg > li:last-child > span { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; +} +.pagination-sm > li > a, +.pagination-sm > li > span { + padding: 5px 10px; + font-size: 12px; +} +.pagination-sm > li:first-child > a, +.pagination-sm > li:first-child > span { + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; +} +.pagination-sm > li:last-child > a, +.pagination-sm > li:last-child > span { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; +} +.pager { + padding-left: 0; + margin: 20px 0; + text-align: center; + list-style: none; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 15px; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #eee; +} +.pager .next > a, +.pager .next > span { + float: right; +} +.pager .previous > a, +.pager .previous > span { + float: left; +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #777; + cursor: not-allowed; + background-color: #fff; +} +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; +} +a.label:hover, +a.label:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +.label:empty { + display: none; +} +.btn .label { + position: relative; + top: -1px; +} +.label-default { + background-color: #777; +} +.label-default[href]:hover, +.label-default[href]:focus { + background-color: #5e5e5e; +} +.label-primary { + background-color: #337ab7; +} +.label-primary[href]:hover, +.label-primary[href]:focus { + background-color: #286090; +} +.label-success { + background-color: #5cb85c; +} +.label-success[href]:hover, +.label-success[href]:focus { + background-color: #449d44; +} +.label-info { + background-color: #5bc0de; +} +.label-info[href]:hover, +.label-info[href]:focus { + background-color: #31b0d5; +} +.label-warning { + background-color: #f0ad4e; +} +.label-warning[href]:hover, +.label-warning[href]:focus { + background-color: #ec971f; +} +.label-danger { + background-color: #d9534f; +} +.label-danger[href]:hover, +.label-danger[href]:focus { + background-color: #c9302c; +} +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: 12px; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + background-color: #777; + border-radius: 10px; +} +.badge:empty { + display: none; +} +.btn .badge { + position: relative; + top: -1px; +} +.btn-xs .badge { + top: 0; + padding: 1px 5px; +} +a.badge:hover, +a.badge:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: #337ab7; + background-color: #fff; +} +.list-group-item > .badge { + float: right; +} +.list-group-item > .badge + .badge { + margin-right: 5px; +} +.nav-pills > li > a > .badge { + margin-left: 3px; +} +.jumbotron { + padding: 30px 15px; + margin-bottom: 30px; + color: inherit; + background-color: #eee; +} +.jumbotron h1, +.jumbotron .h1 { + color: inherit; +} +.jumbotron p { + margin-bottom: 15px; + font-size: 21px; + font-weight: 200; +} +.jumbotron > hr { + border-top-color: #d5d5d5; +} +.container .jumbotron, +.container-fluid .jumbotron { + border-radius: 6px; +} +.jumbotron .container { + max-width: 100%; +} +@media screen and (min-width: 768px) { + .jumbotron { + padding: 48px 0; + } + .container .jumbotron, + .container-fluid .jumbotron { + padding-right: 60px; + padding-left: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 63px; + } +} +.thumbnail { + display: block; + padding: 4px; + margin-bottom: 20px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: border .2s ease-in-out; + -o-transition: border .2s ease-in-out; + transition: border .2s ease-in-out; +} +.thumbnail > img, +.thumbnail a > img { + margin-right: auto; + margin-left: auto; +} +a.thumbnail:hover, +a.thumbnail:focus, +a.thumbnail.active { + border-color: #337ab7; +} +.thumbnail .caption { + padding: 9px; + color: #333; +} +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; +} +.alert h4 { + margin-top: 0; + color: inherit; +} +.alert .alert-link { + font-weight: bold; +} +.alert > p, +.alert > ul { + margin-bottom: 0; +} +.alert > p + p { + margin-top: 5px; +} +.alert-dismissable, +.alert-dismissible { + padding-right: 35px; +} +.alert-dismissable .close, +.alert-dismissible .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; +} +.alert-success { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.alert-success hr { + border-top-color: #c9e2b3; +} +.alert-success .alert-link { + color: #2b542c; +} +.alert-info { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.alert-info hr { + border-top-color: #a6e1ec; +} +.alert-info .alert-link { + color: #245269; +} +.alert-warning { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.alert-warning hr { + border-top-color: #f7e1b5; +} +.alert-warning .alert-link { + color: #66512c; +} +.alert-danger { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.alert-danger hr { + border-top-color: #e4b9c0; +} +.alert-danger .alert-link { + color: #843534; +} +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@-o-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #f5f5f5; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); +} +.progress-bar { + float: left; + width: 0; + height: 100%; + font-size: 12px; + line-height: 20px; + color: #fff; + text-align: center; + background-color: #337ab7; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + -webkit-transition: width .6s ease; + -o-transition: width .6s ease; + transition: width .6s ease; +} +.progress-striped .progress-bar, +.progress-bar-striped { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + background-size: 40px 40px; +} +.progress.active .progress-bar, +.progress-bar.active { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} +.progress-bar-success { + background-color: #5cb85c; +} +.progress-striped .progress-bar-success { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-info { + background-color: #5bc0de; +} +.progress-striped .progress-bar-info { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-warning { + background-color: #f0ad4e; +} +.progress-striped .progress-bar-warning { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-danger { + background-color: #d9534f; +} +.progress-striped .progress-bar-danger { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.media { + margin-top: 15px; +} +.media:first-child { + margin-top: 0; +} +.media, +.media-body { + overflow: hidden; + zoom: 1; +} +.media-body { + width: 10000px; +} +.media-object { + display: block; +} +.media-right, +.media > .pull-right { + padding-left: 10px; +} +.media-left, +.media > .pull-left { + padding-right: 10px; +} +.media-left, +.media-right, +.media-body { + display: table-cell; + vertical-align: top; +} +.media-middle { + vertical-align: middle; +} +.media-bottom { + vertical-align: bottom; +} +.media-heading { + margin-top: 0; + margin-bottom: 5px; +} +.media-list { + padding-left: 0; + list-style: none; +} +.list-group { + padding-left: 0; + margin-bottom: 20px; +} +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + margin-bottom: -1px; + background-color: #fff; + border: 1px solid #ddd; +} +.list-group-item:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +a.list-group-item { + color: #555; +} +a.list-group-item .list-group-item-heading { + color: #333; +} +a.list-group-item:hover, +a.list-group-item:focus { + color: #555; + text-decoration: none; + background-color: #f5f5f5; +} +.list-group-item.disabled, +.list-group-item.disabled:hover, +.list-group-item.disabled:focus { + color: #777; + cursor: not-allowed; + background-color: #eee; +} +.list-group-item.disabled .list-group-item-heading, +.list-group-item.disabled:hover .list-group-item-heading, +.list-group-item.disabled:focus .list-group-item-heading { + color: inherit; +} +.list-group-item.disabled .list-group-item-text, +.list-group-item.disabled:hover .list-group-item-text, +.list-group-item.disabled:focus .list-group-item-text { + color: #777; +} +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + z-index: 2; + color: #fff; + background-color: #337ab7; + border-color: #337ab7; +} +.list-group-item.active .list-group-item-heading, +.list-group-item.active:hover .list-group-item-heading, +.list-group-item.active:focus .list-group-item-heading, +.list-group-item.active .list-group-item-heading > small, +.list-group-item.active:hover .list-group-item-heading > small, +.list-group-item.active:focus .list-group-item-heading > small, +.list-group-item.active .list-group-item-heading > .small, +.list-group-item.active:hover .list-group-item-heading > .small, +.list-group-item.active:focus .list-group-item-heading > .small { + color: inherit; +} +.list-group-item.active .list-group-item-text, +.list-group-item.active:hover .list-group-item-text, +.list-group-item.active:focus .list-group-item-text { + color: #c7ddef; +} +.list-group-item-success { + color: #3c763d; + background-color: #dff0d8; +} +a.list-group-item-success { + color: #3c763d; +} +a.list-group-item-success .list-group-item-heading { + color: inherit; +} +a.list-group-item-success:hover, +a.list-group-item-success:focus { + color: #3c763d; + background-color: #d0e9c6; +} +a.list-group-item-success.active, +a.list-group-item-success.active:hover, +a.list-group-item-success.active:focus { + color: #fff; + background-color: #3c763d; + border-color: #3c763d; +} +.list-group-item-info { + color: #31708f; + background-color: #d9edf7; +} +a.list-group-item-info { + color: #31708f; +} +a.list-group-item-info .list-group-item-heading { + color: inherit; +} +a.list-group-item-info:hover, +a.list-group-item-info:focus { + color: #31708f; + background-color: #c4e3f3; +} +a.list-group-item-info.active, +a.list-group-item-info.active:hover, +a.list-group-item-info.active:focus { + color: #fff; + background-color: #31708f; + border-color: #31708f; +} +.list-group-item-warning { + color: #8a6d3b; + background-color: #fcf8e3; +} +a.list-group-item-warning { + color: #8a6d3b; +} +a.list-group-item-warning .list-group-item-heading { + color: inherit; +} +a.list-group-item-warning:hover, +a.list-group-item-warning:focus { + color: #8a6d3b; + background-color: #faf2cc; +} +a.list-group-item-warning.active, +a.list-group-item-warning.active:hover, +a.list-group-item-warning.active:focus { + color: #fff; + background-color: #8a6d3b; + border-color: #8a6d3b; +} +.list-group-item-danger { + color: #a94442; + background-color: #f2dede; +} +a.list-group-item-danger { + color: #a94442; +} +a.list-group-item-danger .list-group-item-heading { + color: inherit; +} +a.list-group-item-danger:hover, +a.list-group-item-danger:focus { + color: #a94442; + background-color: #ebcccc; +} +a.list-group-item-danger.active, +a.list-group-item-danger.active:hover, +a.list-group-item-danger.active:focus { + color: #fff; + background-color: #a94442; + border-color: #a94442; +} +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px; +} +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} +.panel { + margin-bottom: 20px; + background-color: #fff; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: 0 1px 1px rgba(0, 0, 0, .05); +} +.panel-body { + padding: 15px; +} +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel-heading > .dropdown .dropdown-toggle { + color: inherit; +} +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + color: inherit; +} +.panel-title > a, +.panel-title > small, +.panel-title > .small, +.panel-title > small > a, +.panel-title > .small > a { + color: inherit; +} +.panel-footer { + padding: 10px 15px; + background-color: #f5f5f5; + border-top: 1px solid #ddd; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .list-group, +.panel > .panel-collapse > .list-group { + margin-bottom: 0; +} +.panel > .list-group .list-group-item, +.panel > .panel-collapse > .list-group .list-group-item { + border-width: 1px 0; + border-radius: 0; +} +.panel > .list-group:first-child .list-group-item:first-child, +.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { + border-top: 0; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .list-group:last-child .list-group-item:last-child, +.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { + border-bottom: 0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel-heading + .list-group .list-group-item:first-child { + border-top-width: 0; +} +.list-group + .panel-footer { + border-top-width: 0; +} +.panel > .table, +.panel > .table-responsive > .table, +.panel > .panel-collapse > .table { + margin-bottom: 0; +} +.panel > .table caption, +.panel > .table-responsive > .table caption, +.panel > .panel-collapse > .table caption { + padding-right: 15px; + padding-left: 15px; +} +.panel > .table:first-child, +.panel > .table-responsive:first-child > .table:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { + border-top-left-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { + border-top-right-radius: 3px; +} +.panel > .table:last-child, +.panel > .table-responsive:last-child > .table:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { + border-bottom-right-radius: 3px; +} +.panel > .panel-body + .table, +.panel > .panel-body + .table-responsive, +.panel > .table + .panel-body, +.panel > .table-responsive + .panel-body { + border-top: 1px solid #ddd; +} +.panel > .table > tbody:first-child > tr:first-child th, +.panel > .table > tbody:first-child > tr:first-child td { + border-top: 0; +} +.panel > .table-bordered, +.panel > .table-responsive > .table-bordered { + border: 0; +} +.panel > .table-bordered > thead > tr > th:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, +.panel > .table-bordered > tbody > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, +.panel > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-bordered > thead > tr > td:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, +.panel > .table-bordered > tbody > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, +.panel > .table-bordered > tfoot > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; +} +.panel > .table-bordered > thead > tr > th:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, +.panel > .table-bordered > tbody > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, +.panel > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-bordered > thead > tr > td:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, +.panel > .table-bordered > tbody > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, +.panel > .table-bordered > tfoot > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; +} +.panel > .table-bordered > thead > tr:first-child > td, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, +.panel > .table-bordered > tbody > tr:first-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, +.panel > .table-bordered > thead > tr:first-child > th, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, +.panel > .table-bordered > tbody > tr:first-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { + border-bottom: 0; +} +.panel > .table-bordered > tbody > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, +.panel > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-bordered > tbody > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, +.panel > .table-bordered > tfoot > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { + border-bottom: 0; +} +.panel > .table-responsive { + margin-bottom: 0; + border: 0; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + border-radius: 4px; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group .panel-heading { + border-bottom: 0; +} +.panel-group .panel-heading + .panel-collapse > .panel-body, +.panel-group .panel-heading + .panel-collapse > .list-group { + border-top: 1px solid #ddd; +} +.panel-group .panel-footer { + border-top: 0; +} +.panel-group .panel-footer + .panel-collapse .panel-body { + border-bottom: 1px solid #ddd; +} +.panel-default { + border-color: #ddd; +} +.panel-default > .panel-heading { + color: #333; + background-color: #f5f5f5; + border-color: #ddd; +} +.panel-default > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ddd; +} +.panel-default > .panel-heading .badge { + color: #f5f5f5; + background-color: #333; +} +.panel-default > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ddd; +} +.panel-primary { + border-color: #337ab7; +} +.panel-primary > .panel-heading { + color: #fff; + background-color: #337ab7; + border-color: #337ab7; +} +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #337ab7; +} +.panel-primary > .panel-heading .badge { + color: #337ab7; + background-color: #fff; +} +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #337ab7; +} +.panel-success { + border-color: #d6e9c6; +} +.panel-success > .panel-heading { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.panel-success > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #d6e9c6; +} +.panel-success > .panel-heading .badge { + color: #dff0d8; + background-color: #3c763d; +} +.panel-success > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #d6e9c6; +} +.panel-info { + border-color: #bce8f1; +} +.panel-info > .panel-heading { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.panel-info > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #bce8f1; +} +.panel-info > .panel-heading .badge { + color: #d9edf7; + background-color: #31708f; +} +.panel-info > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #bce8f1; +} +.panel-warning { + border-color: #faebcc; +} +.panel-warning > .panel-heading { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.panel-warning > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #faebcc; +} +.panel-warning > .panel-heading .badge { + color: #fcf8e3; + background-color: #8a6d3b; +} +.panel-warning > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #faebcc; +} +.panel-danger { + border-color: #ebccd1; +} +.panel-danger > .panel-heading { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.panel-danger > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ebccd1; +} +.panel-danger > .panel-heading .badge { + color: #f2dede; + background-color: #a94442; +} +.panel-danger > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ebccd1; +} +.embed-responsive { + position: relative; + display: block; + height: 0; + padding: 0; + overflow: hidden; +} +.embed-responsive .embed-responsive-item, +.embed-responsive iframe, +.embed-responsive embed, +.embed-responsive object, +.embed-responsive video { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: 0; +} +.embed-responsive.embed-responsive-16by9 { + padding-bottom: 56.25%; +} +.embed-responsive.embed-responsive-4by3 { + padding-bottom: 75%; +} +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); +} +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, .15); +} +.well-lg { + padding: 24px; + border-radius: 6px; +} +.well-sm { + padding: 9px; + border-radius: 3px; +} +.close { + float: right; + font-size: 21px; + font-weight: bold; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + filter: alpha(opacity=20); + opacity: .2; +} +.close:hover, +.close:focus { + color: #000; + text-decoration: none; + cursor: pointer; + filter: alpha(opacity=50); + opacity: .5; +} +button.close { + -webkit-appearance: none; + padding: 0; + cursor: pointer; + background: transparent; + border: 0; +} +.modal-open { + overflow: hidden; +} +.modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + display: none; + overflow: hidden; + -webkit-overflow-scrolling: touch; + outline: 0; +} +.modal.fade .modal-dialog { + -webkit-transition: -webkit-transform .3s ease-out; + -o-transition: -o-transform .3s ease-out; + transition: transform .3s ease-out; + -webkit-transform: translate(0, -25%); + -ms-transform: translate(0, -25%); + -o-transform: translate(0, -25%); + transform: translate(0, -25%); +} +.modal.in .modal-dialog { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + -o-transform: translate(0, 0); + transform: translate(0, 0); +} +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto; +} +.modal-dialog { + position: relative; + width: auto; + margin: 10px; +} +.modal-content { + position: relative; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + outline: 0; + -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); + box-shadow: 0 3px 9px rgba(0, 0, 0, .5); +} +.modal-backdrop { + position: absolute; + top: 0; + right: 0; + left: 0; + background-color: #000; +} +.modal-backdrop.fade { + filter: alpha(opacity=0); + opacity: 0; +} +.modal-backdrop.in { + filter: alpha(opacity=50); + opacity: .5; +} +.modal-header { + min-height: 16.42857143px; + padding: 15px; + border-bottom: 1px solid #e5e5e5; +} +.modal-header .close { + margin-top: -2px; +} +.modal-title { + margin: 0; + line-height: 1.42857143; +} +.modal-body { + position: relative; + padding: 15px; +} +.modal-footer { + padding: 15px; + text-align: right; + border-top: 1px solid #e5e5e5; +} +.modal-footer .btn + .btn { + margin-bottom: 0; + margin-left: 5px; +} +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} +@media (min-width: 768px) { + .modal-dialog { + width: 600px; + margin: 30px auto; + } + .modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + } + .modal-sm { + width: 300px; + } +} +@media (min-width: 992px) { + .modal-lg { + width: 900px; + } +} +.tooltip { + position: absolute; + z-index: 1070; + display: block; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 12px; + font-weight: normal; + line-height: 1.4; + visibility: visible; + filter: alpha(opacity=0); + opacity: 0; +} +.tooltip.in { + filter: alpha(opacity=90); + opacity: .9; +} +.tooltip.top { + padding: 5px 0; + margin-top: -3px; +} +.tooltip.right { + padding: 0 5px; + margin-left: 3px; +} +.tooltip.bottom { + padding: 5px 0; + margin-top: 3px; +} +.tooltip.left { + padding: 0 5px; + margin-left: -3px; +} +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #fff; + text-align: center; + text-decoration: none; + background-color: #000; + border-radius: 4px; +} +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-left .tooltip-arrow { + right: 5px; + bottom: 0; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-right .tooltip-arrow { + bottom: 0; + left: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000; +} +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000; +} +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-left .tooltip-arrow { + top: 0; + right: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-right .tooltip-arrow { + top: 0; + left: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: none; + max-width: 276px; + padding: 1px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + font-weight: normal; + line-height: 1.42857143; + text-align: left; + white-space: normal; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); + box-shadow: 0 5px 10px rgba(0, 0, 0, .2); +} +.popover.top { + margin-top: -10px; +} +.popover.right { + margin-left: 10px; +} +.popover.bottom { + margin-top: 10px; +} +.popover.left { + margin-left: -10px; +} +.popover-title { + padding: 8px 14px; + margin: 0; + font-size: 14px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-radius: 5px 5px 0 0; +} +.popover-content { + padding: 9px 14px; +} +.popover > .arrow, +.popover > .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.popover > .arrow { + border-width: 11px; +} +.popover > .arrow:after { + content: ""; + border-width: 10px; +} +.popover.top > .arrow { + bottom: -11px; + left: 50%; + margin-left: -11px; + border-top-color: #999; + border-top-color: rgba(0, 0, 0, .25); + border-bottom-width: 0; +} +.popover.top > .arrow:after { + bottom: 1px; + margin-left: -10px; + content: " "; + border-top-color: #fff; + border-bottom-width: 0; +} +.popover.right > .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-right-color: #999; + border-right-color: rgba(0, 0, 0, .25); + border-left-width: 0; +} +.popover.right > .arrow:after { + bottom: -10px; + left: 1px; + content: " "; + border-right-color: #fff; + border-left-width: 0; +} +.popover.bottom > .arrow { + top: -11px; + left: 50%; + margin-left: -11px; + border-top-width: 0; + border-bottom-color: #999; + border-bottom-color: rgba(0, 0, 0, .25); +} +.popover.bottom > .arrow:after { + top: 1px; + margin-left: -10px; + content: " "; + border-top-width: 0; + border-bottom-color: #fff; +} +.popover.left > .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-right-width: 0; + border-left-color: #999; + border-left-color: rgba(0, 0, 0, .25); +} +.popover.left > .arrow:after { + right: 1px; + bottom: -10px; + content: " "; + border-right-width: 0; + border-left-color: #fff; +} +.carousel { + position: relative; +} +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} +.carousel-inner > .item { + position: relative; + display: none; + -webkit-transition: .6s ease-in-out left; + -o-transition: .6s ease-in-out left; + transition: .6s ease-in-out left; +} +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + line-height: 1; +} +@media all and (transform-3d), (-webkit-transform-3d) { + .carousel-inner > .item { + -webkit-transition: -webkit-transform .6s ease-in-out; + -o-transition: -o-transform .6s ease-in-out; + transition: transform .6s ease-in-out; + + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-perspective: 1000; + perspective: 1000; + } + .carousel-inner > .item.next, + .carousel-inner > .item.active.right { + left: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } + .carousel-inner > .item.prev, + .carousel-inner > .item.active.left { + left: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } + .carousel-inner > .item.next.left, + .carousel-inner > .item.prev.right, + .carousel-inner > .item.active { + left: 0; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} +.carousel-inner > .active { + left: 0; +} +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} +.carousel-inner > .next { + left: 100%; +} +.carousel-inner > .prev { + left: -100%; +} +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} +.carousel-inner > .active.left { + left: -100%; +} +.carousel-inner > .active.right { + left: 100%; +} +.carousel-control { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 15%; + font-size: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); + filter: alpha(opacity=50); + opacity: .5; +} +.carousel-control.left { + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001))); + background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control.right { + right: 0; + left: auto; + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5))); + background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control:hover, +.carousel-control:focus { + color: #fff; + text-decoration: none; + filter: alpha(opacity=90); + outline: 0; + opacity: .9; +} +.carousel-control .icon-prev, +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-left, +.carousel-control .glyphicon-chevron-right { + position: absolute; + top: 50%; + z-index: 5; + display: inline-block; +} +.carousel-control .icon-prev, +.carousel-control .glyphicon-chevron-left { + left: 50%; + margin-left: -10px; +} +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-right { + right: 50%; + margin-right: -10px; +} +.carousel-control .icon-prev, +.carousel-control .icon-next { + width: 20px; + height: 20px; + margin-top: -10px; + font-family: serif; + line-height: 1; +} +.carousel-control .icon-prev:before { + content: '\2039'; +} +.carousel-control .icon-next:before { + content: '\203a'; +} +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + padding-left: 0; + margin-left: -30%; + text-align: center; + list-style: none; +} +.carousel-indicators li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + cursor: pointer; + background-color: #000 \9; + background-color: rgba(0, 0, 0, 0); + border: 1px solid #fff; + border-radius: 10px; +} +.carousel-indicators .active { + width: 12px; + height: 12px; + margin: 0; + background-color: #fff; +} +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); +} +.carousel-caption .btn { + text-shadow: none; +} +@media screen and (min-width: 768px) { + .carousel-control .glyphicon-chevron-left, + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-prev, + .carousel-control .icon-next { + width: 30px; + height: 30px; + margin-top: -15px; + font-size: 30px; + } + .carousel-control .glyphicon-chevron-left, + .carousel-control .icon-prev { + margin-left: -15px; + } + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-next { + margin-right: -15px; + } + .carousel-caption { + right: 20%; + left: 20%; + padding-bottom: 30px; + } + .carousel-indicators { + bottom: 20px; + } +} +.clearfix:before, +.clearfix:after, +.dl-horizontal dd:before, +.dl-horizontal dd:after, +.container:before, +.container:after, +.container-fluid:before, +.container-fluid:after, +.row:before, +.row:after, +.form-horizontal .form-group:before, +.form-horizontal .form-group:after, +.btn-toolbar:before, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:before, +.btn-group-vertical > .btn-group:after, +.nav:before, +.nav:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after, +.navbar-collapse:before, +.navbar-collapse:after, +.pager:before, +.pager:after, +.panel-body:before, +.panel-body:after, +.modal-footer:before, +.modal-footer:after { + display: table; + content: " "; +} +.clearfix:after, +.dl-horizontal dd:after, +.container:after, +.container-fluid:after, +.row:after, +.form-horizontal .form-group:after, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:after, +.nav:after, +.navbar:after, +.navbar-header:after, +.navbar-collapse:after, +.pager:after, +.panel-body:after, +.modal-footer:after { + clear: both; +} +.center-block { + display: block; + margin-right: auto; + margin-left: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; + visibility: hidden !important; +} +.affix { + position: fixed; +} +@-ms-viewport { + width: device-width; +} +.visible-xs, +.visible-sm, +.visible-md, +.visible-lg { + display: none !important; +} +.visible-xs-block, +.visible-xs-inline, +.visible-xs-inline-block, +.visible-sm-block, +.visible-sm-inline, +.visible-sm-inline-block, +.visible-md-block, +.visible-md-inline, +.visible-md-inline-block, +.visible-lg-block, +.visible-lg-inline, +.visible-lg-inline-block { + display: none !important; +} +@media (max-width: 767px) { + .visible-xs { + display: block !important; + } + table.visible-xs { + display: table; + } + tr.visible-xs { + display: table-row !important; + } + th.visible-xs, + td.visible-xs { + display: table-cell !important; + } +} +@media (max-width: 767px) { + .visible-xs-block { + display: block !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline { + display: inline !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline-block { + display: inline-block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm { + display: block !important; + } + table.visible-sm { + display: table; + } + tr.visible-sm { + display: table-row !important; + } + th.visible-sm, + td.visible-sm { + display: table-cell !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-block { + display: block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline { + display: inline !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline-block { + display: inline-block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md { + display: block !important; + } + table.visible-md { + display: table; + } + tr.visible-md { + display: table-row !important; + } + th.visible-md, + td.visible-md { + display: table-cell !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-block { + display: block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline { + display: inline !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline-block { + display: inline-block !important; + } +} +@media (min-width: 1200px) { + .visible-lg { + display: block !important; + } + table.visible-lg { + display: table; + } + tr.visible-lg { + display: table-row !important; + } + th.visible-lg, + td.visible-lg { + display: table-cell !important; + } +} +@media (min-width: 1200px) { + .visible-lg-block { + display: block !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline { + display: inline !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline-block { + display: inline-block !important; + } +} +@media (max-width: 767px) { + .hidden-xs { + display: none !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .hidden-sm { + display: none !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-md { + display: none !important; + } +} +@media (min-width: 1200px) { + .hidden-lg { + display: none !important; + } +} +.visible-print { + display: none !important; +} +@media print { + .visible-print { + display: block !important; + } + table.visible-print { + display: table; + } + tr.visible-print { + display: table-row !important; + } + th.visible-print, + td.visible-print { + display: table-cell !important; + } +} +.visible-print-block { + display: none !important; +} +@media print { + .visible-print-block { + display: block !important; + } +} +.visible-print-inline { + display: none !important; +} +@media print { + .visible-print-inline { + display: inline !important; + } +} +.visible-print-inline-block { + display: none !important; +} +@media print { + .visible-print-inline-block { + display: inline-block !important; + } +} +@media print { + .hidden-print { + display: none !important; + } +} +/*# sourceMappingURL=bootstrap.css.map */ \ No newline at end of file diff --git a/nfv/nfv-docs/html_layout/css/dashboard.css b/nfv/nfv-docs/html_layout/css/dashboard.css new file mode 100755 index 00000000..0f043235 --- /dev/null +++ b/nfv/nfv-docs/html_layout/css/dashboard.css @@ -0,0 +1,640 @@ + +h4, h5 { + font-weight: bold; + color: #333333; + text-rendering: optimizelegibility; +} + +h4 { + margin: 0; + font-size: 16px; +} + +h5 { + margin: 0; + font-size: 12px; +} + +html { + font-size: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} + +body { + margin: 0; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 13px; + line-height: 18px; + color: #333333; + background-color: #ffffff; + min-width: 890px; +} + +html, body { + height: 100%; +} + +.clearfix { + *zoom: 1; +} + +.clearfix:before, .clearfix:after { + display: table; + content: ""; +} + +.clearfix:after { + clear: both; +} + +.row { + margin-left: -20px; + *zoom: 1; +} + +.row:before, .row:after { + display: table; + content: ""; +} + +.row:after { + clear: both; +} + +[class*="span"] { + float: left; + margin-left: 20px; +} + +.span1 { + width: 60px; +} + +.span2 { + width: 140px; +} + +.span3 { + width: 220px; +} + +.span4 { + width: 300px; +} + +.span5 { + width: 380px; +} + +.span6 { + width: 460px; +} + +.span7 { + width: 540px; +} + +.span8 { + width: 620px; +} + +.span9 { + width: 700px; +} + +.span10 { + width: 780px; +} + +.span11 { + width: 860px; +} + +.span12, .container { + width: 940px; +} + +.offset1 { + margin-left: 100px; +} + +.offset2 { + margin-left: 180px; +} + +.offset3 { + margin-left: 260px; +} + +.offset4 { + margin-left: 340px; +} + +.offset5 { + margin-left: 420px; +} + +.offset6 { + margin-left: 500px; +} + +.offset7 { + margin-left: 580px; +} + +.offset8 { + margin-left: 660px; +} + +.offset9 { + margin-left: 740px; +} + +.offset10 { + margin-left: 820px; +} + +.offset11 { + margin-left: 900px; +} + +.row-fluid { + width: 100%; + *zoom: 1; +} + +.row-fluid:before, .row-fluid:after { + display: table; + content: ""; +} + +.row-fluid:after { + clear: both; +} + +.row-fluid > [class*="span"] { + float: left; + margin-left: 2.127659574%; +} + +.row-fluid > [class*="span"]:first-child { + margin-left: 0; +} + +.row-fluid > .span1 { + width: 6.382978723%; +} + +.row-fluid > .span2 { + width: 14.89361702%; +} + +.row-fluid > .span3 { + width: 23.404255317%; +} + +.row-fluid > .span4 { + width: 31.914893614%; +} + +.row-fluid > .span5 { + width: 40.425531911%; +} + +.row-fluid > .span6 { + width: 48.936170208%; +} + +.row-fluid > .span7 { + width: 57.446808505%; +} + +.row-fluid > .span8 { + width: 65.957446802%; +} + +.row-fluid > .span9 { + width: 74.468085099%; +} + +.row-fluid > .span10 { + width: 82.978723396%; +} + +.row-fluid > .span11 { + width: 91.489361693%; +} + +.row-fluid > .span12 { + width: 99.99999999%; +} + +.container { + width: auto; + margin-left: 50px; + margin-right: 50px; + *zoom: 1; +} + +.container:before, .container:after { + display: table; + content: ""; +} + +.container:after { + clear: both; +} + +.container-fluid { + padding-left: 20px; + padding-right: 20px; + *zoom: 1; +} + +.container-fluid:before, .container-fluid:after { + display: table; + content: ""; +} + +.container-fluid:after { + clear: both; +} + +#container { + *zoom: 1; + height: 100%; +} + +#container:before, #container:after { + display: table; + content: ""; +} + +#container:after { + clear: both; +} + +.main_nav { + list-style: none; + width: 240px; + margin: 10px 0 20px 0; +} + +.main_nav a { + color: #999999; + width: 230px; + display: block; + margin-left: 20px; +} + +.main_nav a.active { + background: #ffffff; + border: 2px solid #d8d8d8; + border-right: 0; + border-bottom-color: #cccccc; +} + +#main_content { + padding-left: 265px; + padding-right: 25px; + padding-bottom: 20px; +} + +#main_content .nav-tabs { + margin-bottom: 0; +} + +#main_content .tab-content { + border: 1px solid #dddddd; + border-top: 0 none; + padding-top: 130px; + padding-left: 45px; + padding-right: 45px; + padding-bottom: 45px; +} + +#main_content .row-fluid { + margin: 5px 0px 5px; +} + +#main_content .row-fluid:last-child { + margin-bottom: 0; +} + +.page-header { + padding-bottom: 17px; + margin: 18px 0; + border-bottom: 1px solid #eeeeee; +} + +.page-header h1 { + line-height: 1; +} + +.topbar { + background: #f2f2f2; + border-bottom: 1px solid #e5e5e5; + padding: 10px 25px; + margin-top: 0; + margin-left: -25px; + margin-bottom: 20px; + margin-right: -25px; + min-width: 700px; + width: 100%; + background-color: #474747; + background-image: -moz-linear-gradient(top, #333333, #666666); + background-image: -ms-linear-gradient(top, #333333, #666666); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#666666)); + background-image: -webkit-linear-gradient(top, #333333, #666666); + background-image: -o-linear-gradient(top, #333333, #666666); + background-image: linear-gradient(top, #333333, #666666); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#666666', GradientType=0); + position: fixed; + z-index: 999; +} + +.topbar .page-header h2 { + color: #ffffff; +} + +.topbar .page-header h2 small.datetime { + font-size: 15px; +} + +.topbar #user_info { + color: #eeeeee; +} + +.topbar #user_info a { + color: #eeeeee; + font-weight: bold; +} + +.topbar #user_info a:hover { + color: #ffffff; + text-decoration: underline; +} + +.sidebar { + width: 240px; + min-height: 100%; + background: #666666; + border-bottom: none; + position: fixed; + -webkit-box-shadow: 0px 5px 50px 0px rgba(0, 0, 0, 0.5) inset; + -moz-box-shadow: 0px 5px 50px 0px rgba(0, 0, 0, 0.5) inset; + box-shadow: 0px 5px 50px 0px rgba(0, 0, 0, 0.5) inset; + float: left; +} + +.sidebar h4 { + margin-left: 14px; + color: #999999; +} + +.sidebar .branding { + padding-bottom: 40px; + background-color: #404040; + background-image: -moz-linear-gradient(top, #000000, #a0a0a0); + background-image: -ms-linear-gradient(top, #000000, #a0a0a0); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#000000), to(#a0a0a0)); + background-image: -webkit-linear-gradient(top, #000000, #a0a0a0); + background-image: -o-linear-gradient(top, #000000, #a0a0a0); + background-image: linear-gradient(top, #000000, #a0a0a0); + background-repeat: repeat-x; +} + +h1.brand { + width: 100%; + margin: 0; + background-color: #f5f5f5; + padding-bottom: 40px; +} + +h1.brand a { + display: block; + float: left; + width: 116px; + height: 123px; + text-indent: -9999px; + margin-left: 56px; + margin-top: 15px; + margin-bottom: 25px; +} + +.sidebar .branding .brand { + background: none; + padding-bottom: 0px; +} + +.sidebar .branding .brand a { + float: none; + background: url('../images/windriver-red-med.png') top center no-repeat; + color: #ffffff; + text-indent: inherit; + text-align: center; + text-decoration: none; + text-shadow: 2px 1px 15px #000000; + font-size: 20px; + line-height: 20px; + margin: 20px 0px 0px 0px; + padding-top: 42px; + padding-bottom: 0px; + height: 30px; + width: 240px; +} + +.sidebar .branding .system { + text-align: center; + color: #ffffff; + padding-bottom: 20px; + font-size: 15px; +} + +.sidebar .nav-tabs { + margin-top: -39px; +} + +.sidebar .nav-tabs li a { + color: #ffffff; +} + +.sidebar .main_nav { + width: 240px; + padding-left: 5px; +} + +.sidebar .main_nav a { + font-size: 14px; + padding: 12px 12px 0px; + color: #dddddd; +} + +.sidebar .main_nav a.active { + color: #666666; +} + +.sidebar .switcher_bar { + background: #000000; + width: 100%; + margin: 15px 0px; + -webkit-box-shadow: 0px 4px 8px 0px rgba(0, 0, 0, 0.5); + -moz-box-shadow: 0px 4px 8px 0px rgba(0, 0, 0, 0.5); + box-shadow: 0px 4px 8px 0px rgba(0, 0, 0, 0.5); +} + +.main-table > thead > tr > th { + background: #e4e4e4; +} + +.main-table > tbody > tr:hover > td { + background-color: #e9e9e9; + cursor: default; +} + +.table-header { + min-height: 35px; + padding: 5px 0; +} + +.table-actions { + float: right; + min-width: 400px; + margin-top: 18px; +} + +.table_header .table_actions { + min-width: 0; +} + +.table-header h3 { + font-weight: normal; + font-size: 24px; + margin-bottom: 15px; + float: left; +} + +.table-header .table-actions { + min-width: 0; +} + +.table-header .table-actions a { + display: inline-block; + float: none; +} + +.table thead th { + background: #f5f5f5; +} + +.table tfoot tr td { + border-top: 1px solid #000000; + background-color: #e4e4e4; + font-size: 11px; + line-height: 14px; +} + +.subhead { + border-bottom: 1px dotted; + padding-bottom: 5px; + border-color: #c5e2ea; + margin-bottom: 20px; +} + +.subhead h1 { + font-size: 24px; +} + +.subhead h2 { + color: black; + font-size: 20px; +} + +.subhead h3 { + font-size: 18px; + color: black; +} + +.subhead h2, +.subhead h3 +{ + position: relative; + margin-left: -20px; + padding-left: 20px; +} + +.method-section { + margin-left: 10px; + margin-bottom: 5px; +} + +.method-section h3 { + font-size: 16px; + color: black; + position: relative; + margin-top: 10px; + margin-left: 10px; + margin-bottom: 10px; +} + +.method-label { + display: flex; + padding: .3em .6em .3em; + font-size: 85%; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; + width: 5.2em; + align-items: center; + -webkit-flex-align: center; + -ms-flex-align: center; + -webkit-align-items: center; +} + +.method.col-md-1 { + padding-left: 0px; + padding-right: 0px; +} + +.method-urls { + margin:0; +} + +.btn-details { + width: 6em; + height:auto; + display:inline-block; + margin-bottom: 2px; +} + +.btn-examples { + width: 6em; + height:auto; + display:inline-block; + margin-bottom: 2px; +} + +#main_content .row-fluid .examples { + margin: 10px 0px 5px; +} + +.btn:focus { + outline: none; +} + +pre { + width: 900px; /* specify width */ + white-space: pre-wrap; /* CSS3 browsers */ + white-space: -moz-pre-wrap !important; /* 1999+ Mozilla */ + white-space: -pre-wrap; /* Opera 4 thru 6 */ + white-space: -o-pre-wrap; /* Opera 7 and up */ + word-wrap: break-word; /* IE 5.5+ and up */ + /* overflow-x: auto; */ /* Firefox 2 only */ + /* width: 99%; */ /* only if needed */ +} diff --git a/nfv/nfv-docs/html_layout/images/network_model.svg b/nfv/nfv-docs/html_layout/images/network_model.svg new file mode 100755 index 00000000..659b5566 --- /dev/null +++ b/nfv/nfv-docs/html_layout/images/network_model.svg @@ -0,0 +1,842 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + NetworkResourceType + network_attributes + resource_id + reservation_id + + + status + + + qos_value + NetworkQosType + + qos_name + + + + + sharing_criteria + NetworkType + type_of_segment + bandwidth + type_of_network + + + network_qos + is_shared + meta_data + layer3_attributes + wrs_physical_network + + + + + NetworkSubnetType + gateway_ip + network_id + ip_version + + + is_dhcp_enabled + meta_data + wrs_subnet_ip + wrs_subnet_prefix + + + NetworkSubnetResourceType + + resource_id + subnet_attributes + status + + + + + + + + + diff --git a/nfv/nfv-docs/html_layout/images/windriver-favicon.ico b/nfv/nfv-docs/html_layout/images/windriver-favicon.ico new file mode 100755 index 00000000..d5edb059 Binary files /dev/null and b/nfv/nfv-docs/html_layout/images/windriver-favicon.ico differ diff --git a/nfv/nfv-docs/html_layout/images/windriver-red-med.png b/nfv/nfv-docs/html_layout/images/windriver-red-med.png new file mode 100755 index 00000000..76317dc2 Binary files /dev/null and b/nfv/nfv-docs/html_layout/images/windriver-red-med.png differ diff --git a/nfv/nfv-docs/html_layout/javascript/bootstrap.js b/nfv/nfv-docs/html_layout/javascript/bootstrap.js new file mode 100755 index 00000000..e44ce3f8 --- /dev/null +++ b/nfv/nfv-docs/html_layout/javascript/bootstrap.js @@ -0,0 +1,2306 @@ +/*! + * Bootstrap v3.3.2 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ + +if (typeof jQuery === 'undefined') { + throw new Error('Bootstrap\'s JavaScript requires jQuery') +} + ++function ($) { + 'use strict'; + var version = $.fn.jquery.split(' ')[0].split('.') + if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1)) { + throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher') + } +}(jQuery); + +/* ======================================================================== + * Bootstrap: transition.js v3.3.2 + * http://getbootstrap.com/javascript/#transitions + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) + // ============================================================ + + function transitionEnd() { + var el = document.createElement('bootstrap') + + var transEndEventNames = { + WebkitTransition : 'webkitTransitionEnd', + MozTransition : 'transitionend', + OTransition : 'oTransitionEnd otransitionend', + transition : 'transitionend' + } + + for (var name in transEndEventNames) { + if (el.style[name] !== undefined) { + return { end: transEndEventNames[name] } + } + } + + return false // explicit for ie8 ( ._.) + } + + // http://blog.alexmaccaw.com/css-transitions + $.fn.emulateTransitionEnd = function (duration) { + var called = false + var $el = this + $(this).one('bsTransitionEnd', function () { called = true }) + var callback = function () { if (!called) $($el).trigger($.support.transition.end) } + setTimeout(callback, duration) + return this + } + + $(function () { + $.support.transition = transitionEnd() + + if (!$.support.transition) return + + $.event.special.bsTransitionEnd = { + bindType: $.support.transition.end, + delegateType: $.support.transition.end, + handle: function (e) { + if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments) + } + } + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: alert.js v3.3.2 + * http://getbootstrap.com/javascript/#alerts + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // ALERT CLASS DEFINITION + // ====================== + + var dismiss = '[data-dismiss="alert"]' + var Alert = function (el) { + $(el).on('click', dismiss, this.close) + } + + Alert.VERSION = '3.3.2' + + Alert.TRANSITION_DURATION = 150 + + Alert.prototype.close = function (e) { + var $this = $(this) + var selector = $this.attr('data-target') + + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 + } + + var $parent = $(selector) + + if (e) e.preventDefault() + + if (!$parent.length) { + $parent = $this.closest('.alert') + } + + $parent.trigger(e = $.Event('close.bs.alert')) + + if (e.isDefaultPrevented()) return + + $parent.removeClass('in') + + function removeElement() { + // detach from parent, fire event then clean up data + $parent.detach().trigger('closed.bs.alert').remove() + } + + $.support.transition && $parent.hasClass('fade') ? + $parent + .one('bsTransitionEnd', removeElement) + .emulateTransitionEnd(Alert.TRANSITION_DURATION) : + removeElement() + } + + + // ALERT PLUGIN DEFINITION + // ======================= + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.alert') + + if (!data) $this.data('bs.alert', (data = new Alert(this))) + if (typeof option == 'string') data[option].call($this) + }) + } + + var old = $.fn.alert + + $.fn.alert = Plugin + $.fn.alert.Constructor = Alert + + + // ALERT NO CONFLICT + // ================= + + $.fn.alert.noConflict = function () { + $.fn.alert = old + return this + } + + + // ALERT DATA-API + // ============== + + $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: button.js v3.3.2 + * http://getbootstrap.com/javascript/#buttons + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // BUTTON PUBLIC CLASS DEFINITION + // ============================== + + var Button = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Button.DEFAULTS, options) + this.isLoading = false + } + + Button.VERSION = '3.3.2' + + Button.DEFAULTS = { + loadingText: 'loading...' + } + + Button.prototype.setState = function (state) { + var d = 'disabled' + var $el = this.$element + var val = $el.is('input') ? 'val' : 'html' + var data = $el.data() + + state = state + 'Text' + + if (data.resetText == null) $el.data('resetText', $el[val]()) + + // push to event loop to allow forms to submit + setTimeout($.proxy(function () { + $el[val](data[state] == null ? this.options[state] : data[state]) + + if (state == 'loadingText') { + this.isLoading = true + $el.addClass(d).attr(d, d) + } else if (this.isLoading) { + this.isLoading = false + $el.removeClass(d).removeAttr(d) + } + }, this), 0) + } + + Button.prototype.toggle = function () { + var changed = true + var $parent = this.$element.closest('[data-toggle="buttons"]') + + if ($parent.length) { + var $input = this.$element.find('input') + if ($input.prop('type') == 'radio') { + if ($input.prop('checked') && this.$element.hasClass('active')) changed = false + else $parent.find('.active').removeClass('active') + } + if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change') + } else { + this.$element.attr('aria-pressed', !this.$element.hasClass('active')) + } + + if (changed) this.$element.toggleClass('active') + } + + + // BUTTON PLUGIN DEFINITION + // ======================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.button') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.button', (data = new Button(this, options))) + + if (option == 'toggle') data.toggle() + else if (option) data.setState(option) + }) + } + + var old = $.fn.button + + $.fn.button = Plugin + $.fn.button.Constructor = Button + + + // BUTTON NO CONFLICT + // ================== + + $.fn.button.noConflict = function () { + $.fn.button = old + return this + } + + + // BUTTON DATA-API + // =============== + + $(document) + .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { + var $btn = $(e.target) + if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') + Plugin.call($btn, 'toggle') + e.preventDefault() + }) + .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) { + $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type)) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: carousel.js v3.3.2 + * http://getbootstrap.com/javascript/#carousel + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // CAROUSEL CLASS DEFINITION + // ========================= + + var Carousel = function (element, options) { + this.$element = $(element) + this.$indicators = this.$element.find('.carousel-indicators') + this.options = options + this.paused = + this.sliding = + this.interval = + this.$active = + this.$items = null + + this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this)) + + this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element + .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) + .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) + } + + Carousel.VERSION = '3.3.2' + + Carousel.TRANSITION_DURATION = 600 + + Carousel.DEFAULTS = { + interval: 5000, + pause: 'hover', + wrap: true, + keyboard: true + } + + Carousel.prototype.keydown = function (e) { + if (/input|textarea/i.test(e.target.tagName)) return + switch (e.which) { + case 37: this.prev(); break + case 39: this.next(); break + default: return + } + + e.preventDefault() + } + + Carousel.prototype.cycle = function (e) { + e || (this.paused = false) + + this.interval && clearInterval(this.interval) + + this.options.interval + && !this.paused + && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) + + return this + } + + Carousel.prototype.getItemIndex = function (item) { + this.$items = item.parent().children('.item') + return this.$items.index(item || this.$active) + } + + Carousel.prototype.getItemForDirection = function (direction, active) { + var activeIndex = this.getItemIndex(active) + var willWrap = (direction == 'prev' && activeIndex === 0) + || (direction == 'next' && activeIndex == (this.$items.length - 1)) + if (willWrap && !this.options.wrap) return active + var delta = direction == 'prev' ? -1 : 1 + var itemIndex = (activeIndex + delta) % this.$items.length + return this.$items.eq(itemIndex) + } + + Carousel.prototype.to = function (pos) { + var that = this + var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) + + if (pos > (this.$items.length - 1) || pos < 0) return + + if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" + if (activeIndex == pos) return this.pause().cycle() + + return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos)) + } + + Carousel.prototype.pause = function (e) { + e || (this.paused = true) + + if (this.$element.find('.next, .prev').length && $.support.transition) { + this.$element.trigger($.support.transition.end) + this.cycle(true) + } + + this.interval = clearInterval(this.interval) + + return this + } + + Carousel.prototype.next = function () { + if (this.sliding) return + return this.slide('next') + } + + Carousel.prototype.prev = function () { + if (this.sliding) return + return this.slide('prev') + } + + Carousel.prototype.slide = function (type, next) { + var $active = this.$element.find('.item.active') + var $next = next || this.getItemForDirection(type, $active) + var isCycling = this.interval + var direction = type == 'next' ? 'left' : 'right' + var that = this + + if ($next.hasClass('active')) return (this.sliding = false) + + var relatedTarget = $next[0] + var slideEvent = $.Event('slide.bs.carousel', { + relatedTarget: relatedTarget, + direction: direction + }) + this.$element.trigger(slideEvent) + if (slideEvent.isDefaultPrevented()) return + + this.sliding = true + + isCycling && this.pause() + + if (this.$indicators.length) { + this.$indicators.find('.active').removeClass('active') + var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]) + $nextIndicator && $nextIndicator.addClass('active') + } + + var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid" + if ($.support.transition && this.$element.hasClass('slide')) { + $next.addClass(type) + $next[0].offsetWidth // force reflow + $active.addClass(direction) + $next.addClass(direction) + $active + .one('bsTransitionEnd', function () { + $next.removeClass([type, direction].join(' ')).addClass('active') + $active.removeClass(['active', direction].join(' ')) + that.sliding = false + setTimeout(function () { + that.$element.trigger(slidEvent) + }, 0) + }) + .emulateTransitionEnd(Carousel.TRANSITION_DURATION) + } else { + $active.removeClass('active') + $next.addClass('active') + this.sliding = false + this.$element.trigger(slidEvent) + } + + isCycling && this.cycle() + + return this + } + + + // CAROUSEL PLUGIN DEFINITION + // ========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.carousel') + var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) + var action = typeof option == 'string' ? option : options.slide + + if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) + if (typeof option == 'number') data.to(option) + else if (action) data[action]() + else if (options.interval) data.pause().cycle() + }) + } + + var old = $.fn.carousel + + $.fn.carousel = Plugin + $.fn.carousel.Constructor = Carousel + + + // CAROUSEL NO CONFLICT + // ==================== + + $.fn.carousel.noConflict = function () { + $.fn.carousel = old + return this + } + + + // CAROUSEL DATA-API + // ================= + + var clickHandler = function (e) { + var href + var $this = $(this) + var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 + if (!$target.hasClass('carousel')) return + var options = $.extend({}, $target.data(), $this.data()) + var slideIndex = $this.attr('data-slide-to') + if (slideIndex) options.interval = false + + Plugin.call($target, options) + + if (slideIndex) { + $target.data('bs.carousel').to(slideIndex) + } + + e.preventDefault() + } + + $(document) + .on('click.bs.carousel.data-api', '[data-slide]', clickHandler) + .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler) + + $(window).on('load', function () { + $('[data-ride="carousel"]').each(function () { + var $carousel = $(this) + Plugin.call($carousel, $carousel.data()) + }) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: collapse.js v3.3.2 + * http://getbootstrap.com/javascript/#collapse + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // COLLAPSE PUBLIC CLASS DEFINITION + // ================================ + + var Collapse = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Collapse.DEFAULTS, options) + this.$trigger = $(this.options.trigger).filter('[href="#' + element.id + '"], [data-target="#' + element.id + '"]') + this.transitioning = null + + if (this.options.parent) { + this.$parent = this.getParent() + } else { + this.addAriaAndCollapsedClass(this.$element, this.$trigger) + } + + if (this.options.toggle) this.toggle() + } + + Collapse.VERSION = '3.3.2' + + Collapse.TRANSITION_DURATION = 350 + + Collapse.DEFAULTS = { + toggle: true, + trigger: '[data-toggle="collapse"]' + } + + Collapse.prototype.dimension = function () { + var hasWidth = this.$element.hasClass('width') + return hasWidth ? 'width' : 'height' + } + + Collapse.prototype.show = function () { + if (this.transitioning || this.$element.hasClass('in')) return + + var activesData + var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing') + + if (actives && actives.length) { + activesData = actives.data('bs.collapse') + if (activesData && activesData.transitioning) return + } + + var startEvent = $.Event('show.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + if (actives && actives.length) { + Plugin.call(actives, 'hide') + activesData || actives.data('bs.collapse', null) + } + + var dimension = this.dimension() + + this.$element + .removeClass('collapse') + .addClass('collapsing')[dimension](0) + .attr('aria-expanded', true) + + this.$trigger + .removeClass('collapsed') + .attr('aria-expanded', true) + + this.transitioning = 1 + + var complete = function () { + this.$element + .removeClass('collapsing') + .addClass('collapse in')[dimension]('') + this.transitioning = 0 + this.$element + .trigger('shown.bs.collapse') + } + + if (!$.support.transition) return complete.call(this) + + var scrollSize = $.camelCase(['scroll', dimension].join('-')) + + this.$element + .one('bsTransitionEnd', $.proxy(complete, this)) + .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]) + } + + Collapse.prototype.hide = function () { + if (this.transitioning || !this.$element.hasClass('in')) return + + var startEvent = $.Event('hide.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + var dimension = this.dimension() + + this.$element[dimension](this.$element[dimension]())[0].offsetHeight + + this.$element + .addClass('collapsing') + .removeClass('collapse in') + .attr('aria-expanded', false) + + this.$trigger + .addClass('collapsed') + .attr('aria-expanded', false) + + this.transitioning = 1 + + var complete = function () { + this.transitioning = 0 + this.$element + .removeClass('collapsing') + .addClass('collapse') + .trigger('hidden.bs.collapse') + } + + if (!$.support.transition) return complete.call(this) + + this.$element + [dimension](0) + .one('bsTransitionEnd', $.proxy(complete, this)) + .emulateTransitionEnd(Collapse.TRANSITION_DURATION) + } + + Collapse.prototype.toggle = function () { + this[this.$element.hasClass('in') ? 'hide' : 'show']() + } + + Collapse.prototype.getParent = function () { + return $(this.options.parent) + .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]') + .each($.proxy(function (i, element) { + var $element = $(element) + this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element) + }, this)) + .end() + } + + Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) { + var isOpen = $element.hasClass('in') + + $element.attr('aria-expanded', isOpen) + $trigger + .toggleClass('collapsed', !isOpen) + .attr('aria-expanded', isOpen) + } + + function getTargetFromTrigger($trigger) { + var href + var target = $trigger.attr('data-target') + || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 + + return $(target) + } + + + // COLLAPSE PLUGIN DEFINITION + // ========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.collapse') + var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) + + if (!data && options.toggle && option == 'show') options.toggle = false + if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.collapse + + $.fn.collapse = Plugin + $.fn.collapse.Constructor = Collapse + + + // COLLAPSE NO CONFLICT + // ==================== + + $.fn.collapse.noConflict = function () { + $.fn.collapse = old + return this + } + + + // COLLAPSE DATA-API + // ================= + + $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { + var $this = $(this) + + if (!$this.attr('data-target')) e.preventDefault() + + var $target = getTargetFromTrigger($this) + var data = $target.data('bs.collapse') + var option = data ? 'toggle' : $.extend({}, $this.data(), { trigger: this }) + + Plugin.call($target, option) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: dropdown.js v3.3.2 + * http://getbootstrap.com/javascript/#dropdowns + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // DROPDOWN CLASS DEFINITION + // ========================= + + var backdrop = '.dropdown-backdrop' + var toggle = '[data-toggle="dropdown"]' + var Dropdown = function (element) { + $(element).on('click.bs.dropdown', this.toggle) + } + + Dropdown.VERSION = '3.3.2' + + Dropdown.prototype.toggle = function (e) { + var $this = $(this) + + if ($this.is('.disabled, :disabled')) return + + var $parent = getParent($this) + var isActive = $parent.hasClass('open') + + clearMenus() + + if (!isActive) { + if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { + // if mobile we use a backdrop because click events don't delegate + $('