diff -Nru gce-compute-image-packages-20180510+dfsg1/debian/changelog gce-compute-image-packages-20180905+dfsg1/debian/changelog --- gce-compute-image-packages-20180510+dfsg1/debian/changelog 2018-06-07 23:27:01.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/debian/changelog 2018-09-16 22:17:02.000000000 +0000 @@ -1,3 +1,59 @@ +gce-compute-image-packages (20180905+dfsg1-0ubuntu1~14.04.0) trusty; urgency=medium + + * Backport to Trusty + - Revert ordering shutdown scripts after snapd.service. + - Revert adding /snap/bin to PATH for startup/shutdown scripts + - Revert to using dh-systemd because Xenial does not have the debhelper + * Revert "Build depend on debhelper (>= 9.20160709) instead of on dh-systemd" + This reverts commit 1afdbde3f27ab4d1712b1a0d4cc14df3a0528bdc. + Xenial does not have that debhelper version thus dh-systemd is needed there. + * Build-depend on libboost-regex-dev for regex support + * Choose std:: or boost:: regex based on support for C++11 + * Set LDFLAGS at the end of the the c++ command line right before libs. + This fixes passig additional libs to make, namely -lboost_regex + * Link with boost for regex support + * Free tests from C++11 constructs to let them being compiled without C++11 support + + -- Balint Reczey Mon, 17 Sep 2018 00:17:02 +0200 + +gce-compute-image-packages (20180905+dfsg1-0ubuntu1) cosmic; urgency=medium + + [ Balint Reczey ] + * New upstream version 20180905+dfsg1 (LP: #1792466) + - Restart the network daemon if networking is restarted. + - Prevent setup of the default ethernet interface. + - Accounts daemon can now verify username is 32 characters or less. + - Prevent IP forwarding daemon log spam. + - Make default shell configurable when executing metadata scripts. + - Rename distro directory to distro_lib. + * debian/control: Update Vcs-* fields to point to the new packaging repository + * Update shared library symlinks + * Drop 0001-Adjust-tests-to-changed-LoadJsonArrayToCache-behavio.patch, + it is integrated upstream + * Build depend on debhelper (>= 9.20160709) instead of on dh-systemd + to keep Lintian happy. + + [ Google Cloud Team ] + * Remove NTP dependency from packaging + + -- Balint Reczey Thu, 13 Sep 2018 23:08:33 +0200 + +gce-compute-image-packages (20180510+dfsg1-0ubuntu5) cosmic; urgency=medium + + * Depend on the same version of google-compute-engine-oslogin + * Only Recommend rsyslog | system-log-daemon (LP: #1780109) + + -- Balint Reczey Wed, 04 Jul 2018 17:22:09 +0200 + +gce-compute-image-packages (20180510+dfsg1-0ubuntu4) cosmic; urgency=medium + + * debian/patches/0004-order-shutdown-scripts-after-snapd.patch: Order + shutdown scripts after snapd.service. + * debian/patches/0005-add-snap-bin-to-path.patch: Add /snap/bin to + PATH for startup/shutdown scripts. + + -- Daniel Watkins Thu, 17 May 2018 12:30:32 -0400 + gce-compute-image-packages (20180510+dfsg1-0ubuntu3~14.04.3) trusty; urgency=medium * Restart services upon upgrade with upstart, too diff -Nru gce-compute-image-packages-20180510+dfsg1/debian/control gce-compute-image-packages-20180905+dfsg1/debian/control --- gce-compute-image-packages-20180510+dfsg1/debian/control 2018-06-07 23:27:01.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/debian/control 2018-09-16 22:17:02.000000000 +0000 @@ -6,6 +6,7 @@ debhelper (>= 9), dh-python, dh-systemd, + libboost-regex-dev, libcurl4-openssl-dev, libgtest-dev, libjson-c-dev, @@ -16,8 +17,8 @@ python-mock, python-boto Standards-Version: 3.9.8 -Vcs-Browser: https://git.launchpad.net/~rbalint/ubuntu/+source/gce-compute-image-packages -Vcs-Git: git://git.launchpad.net/~rbalint/ubuntu/+source/gce-compute-image-packages +Vcs-Browser: https://code.launchpad.net/~ubuntu-core-dev/+git/gce-compute-image-packages +Vcs-Git: https://git.launchpad.net/~ubuntu-core-dev/+git/gce-compute-image-packages -b ubuntu/master Homepage: https://github.com/GoogleCloudPlatform/compute-image-packages XSBC-Original-Maintainer: Daniel Watkins @@ -25,12 +26,11 @@ Architecture: all Depends: ${python:Depends}, ${misc:Depends}, - google-compute-engine-oslogin, + google-compute-engine-oslogin (= ${source:Version}), python-google-compute-engine (= ${source:Version}), - chrony | ntp | time-daemon, - rsyslog | system-log-daemon, cloud-init Provides: irqbalance +Recommends: rsyslog | system-log-daemon Suggests: libpam-cracklib Conflicts: gce-cloud-config, gce-daemon, diff -Nru gce-compute-image-packages-20180510+dfsg1/debian/google-compute-engine-oslogin.links gce-compute-image-packages-20180905+dfsg1/debian/google-compute-engine-oslogin.links --- gce-compute-image-packages-20180510+dfsg1/debian/google-compute-engine-oslogin.links 2018-06-07 23:27:01.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/debian/google-compute-engine-oslogin.links 2018-09-16 22:17:02.000000000 +0000 @@ -1,2 +1,2 @@ -lib/libnss_cache_google-compute-engine-oslogin-1.3.0.so lib/libnss_cache_oslogin.so.2 -lib/libnss_google-compute-engine-oslogin-1.3.0.so lib/libnss_oslogin.so.2 +lib/libnss_cache_google-compute-engine-oslogin-1.3.1.so lib/libnss_cache_oslogin.so.2 +lib/libnss_google-compute-engine-oslogin-1.3.1.so lib/libnss_oslogin.so.2 diff -Nru gce-compute-image-packages-20180510+dfsg1/debian/patches/0001-Adjust-tests-to-changed-LoadJsonArrayToCache-behavio.patch gce-compute-image-packages-20180905+dfsg1/debian/patches/0001-Adjust-tests-to-changed-LoadJsonArrayToCache-behavio.patch --- gce-compute-image-packages-20180510+dfsg1/debian/patches/0001-Adjust-tests-to-changed-LoadJsonArrayToCache-behavio.patch 2018-06-07 23:27:01.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/debian/patches/0001-Adjust-tests-to-changed-LoadJsonArrayToCache-behavio.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -From 12dbd8b7eddc59f4f417d5326bbf15f50788afeb Mon Sep 17 00:00:00 2001 -From: Balint Reczey -Date: Tue, 15 May 2018 16:56:31 +0200 -Subject: [PATCH] Adjust tests to changed LoadJsonArrayToCache() behaviour - -The change was committed in 0bd54db18db9b78e480f70ee1611df3914da13f0 ---- - google_compute_engine_oslogin/utils/oslogin_utils_test.cc | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/google_compute_engine_oslogin/utils/oslogin_utils_test.cc b/google_compute_engine_oslogin/utils/oslogin_utils_test.cc -index 4653639..271245f 100644 ---- a/google_compute_engine_oslogin/utils/oslogin_utils_test.cc -+++ b/google_compute_engine_oslogin/utils/oslogin_utils_test.cc -@@ -152,7 +152,7 @@ TEST(NssCacheTest, TestLoadJsonFinalResponse) { - string response = - "{\"nextPageToken\": \"0\"}"; - -- ASSERT_TRUE(nss_cache.LoadJsonArrayToCache(response)); -+ ASSERT_FALSE(nss_cache.LoadJsonArrayToCache(response)); - ASSERT_STREQ(nss_cache.GetPageToken().c_str(), ""); - - size_t buflen = 500; --- -2.17.0 - diff -Nru gce-compute-image-packages-20180510+dfsg1/debian/patches/0001-Choose-std-or-boost-regex-based-on-support-for-C-11.patch gce-compute-image-packages-20180905+dfsg1/debian/patches/0001-Choose-std-or-boost-regex-based-on-support-for-C-11.patch --- gce-compute-image-packages-20180510+dfsg1/debian/patches/0001-Choose-std-or-boost-regex-based-on-support-for-C-11.patch 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/debian/patches/0001-Choose-std-or-boost-regex-based-on-support-for-C-11.patch 2018-09-16 22:17:02.000000000 +0000 @@ -0,0 +1,42 @@ +From b10710564c13417f902b184ddcfe443c97db396e Mon Sep 17 00:00:00 2001 +From: Balint Reczey +Date: Fri, 14 Sep 2018 19:00:39 +0200 +Subject: [PATCH] Choose std:: or boost:: regex based on support for C++11 + +instead of checking GCC versions. + +This is more reliable since C++11 support may be disabled with newer GCCs +and it can be enabled in older ones by passing -std=c++11. + +Fixes #650. +--- + google_compute_engine_oslogin/utils/oslogin_utils.cc | 7 +------ + 1 file changed, 1 insertion(+), 6 deletions(-) + +diff --git a/google_compute_engine_oslogin/utils/oslogin_utils.cc b/google_compute_engine_oslogin/utils/oslogin_utils.cc +index 4e0cb79..659c434 100644 +--- a/google_compute_engine_oslogin/utils/oslogin_utils.cc ++++ b/google_compute_engine_oslogin/utils/oslogin_utils.cc +@@ -23,18 +23,13 @@ + #include + #include + +-#ifdef __GNUC__ +-#if __GNUC__ > 4 || \ +- (__GNUC__ == 4 && (__GNUC_MINOR__ > 9 || \ +- (__GNUC_MINOR__ == 9 && \ +- __GNUC_PATCHLEVEL__ > 0))) ++#if __cplusplus >= 201103L + #include + #define Regex std + #else + #include + #define Regex boost + #endif +-#endif + + #include "oslogin_utils.h" + +-- +2.17.1 + diff -Nru gce-compute-image-packages-20180510+dfsg1/debian/patches/0002-Set-LDFLAGS-at-the-end-of-the-c-command-line-right-b.patch gce-compute-image-packages-20180905+dfsg1/debian/patches/0002-Set-LDFLAGS-at-the-end-of-the-c-command-line-right-b.patch --- gce-compute-image-packages-20180510+dfsg1/debian/patches/0002-Set-LDFLAGS-at-the-end-of-the-c-command-line-right-b.patch 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/debian/patches/0002-Set-LDFLAGS-at-the-end-of-the-c-command-line-right-b.patch 2018-09-16 22:17:02.000000000 +0000 @@ -0,0 +1,36 @@ +From ba8c215018d7c9f8e8480fc5f7575c3f561a2fe9 Mon Sep 17 00:00:00 2001 +From: Balint Reczey +Date: Sun, 16 Sep 2018 13:34:26 +0200 +Subject: [PATCH 2/3] Set LDFLAGS at the end of the c++ command line right + before libs + +This fixes passig additional libs to make, namely -lboost_regex +--- + google_compute_engine_oslogin/Makefile | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/google_compute_engine_oslogin/Makefile b/google_compute_engine_oslogin/Makefile +index 4af7ed1..716a631 100644 +--- a/google_compute_engine_oslogin/Makefile ++++ b/google_compute_engine_oslogin/Makefile +@@ -96,7 +96,7 @@ $(NSS): $(NSS_LIBRARY_SOURCE) $(UTILS) + $(NSS_SRC) $(UTILS) $(LIBS) + + $(NSS_CACHE_BIN): $(NSS_CACHE_SRC) $(UTILS_SRC) +- $(CXX) $(CXXFLAGS) $(LDFLAGS) $(INCLUDE_FLAGS) -o $(NSS_CACHE_BIN) $(NSS_CACHE_SRC) $(UTILS_SRC) $(LIBS) ++ $(CXX) $(CXXFLAGS) $(INCLUDE_FLAGS) -o $(NSS_CACHE_BIN) $(NSS_CACHE_SRC) $(UTILS_SRC) $(LDFLAGS) $(LIBS) + + $(LIBNSS_CACHE_OSLOGIN_NAME): $(LIBNSS_CACHE_OBJ) $(LIBNSS_COMPAT_OBJ) + $(CXX) $(LIBNSS_SO_FLAGS) -o $(LIBNSS_CACHE_OSLOGIN_NAME) $(LIBNSS_CACHE_OBJ) $(LIBNSS_COMPAT_OBJ) +@@ -122,7 +122,7 @@ $(PAM_ADMIN_OBJ): $(PAM_ADMIN_SRC) + $(CXX) $(CXXFLAGS) -c $(PAM_ADMIN_SRC) -o $(PAM_ADMIN_OBJ) + + $(AUTHKEYS_BIN): $(AUTHKEYS_SRC) $(UTILS_SRC) +- $(CXX) $(CXXFLAGS) $(LDFLAGS) $(INCLUDE_FLAGS) -o $(AUTHKEYS_BIN) $(AUTHKEYS_SRC) $(UTILS_SRC) $(LIBS) ++ $(CXX) $(CXXFLAGS) $(INCLUDE_FLAGS) -o $(AUTHKEYS_BIN) $(AUTHKEYS_SRC) $(UTILS_SRC) $(LDFLAGS) $(LIBS) + + $(UTILS): $(UTILS_SRC) + $(CXX) $(CXXFLAGS) $(INCLUDE_FLAGS) -c $(UTILS_SRC) -o $(UTILS) +-- +2.17.1 + diff -Nru gce-compute-image-packages-20180510+dfsg1/debian/patches/0003-Use-C-style-iteration-without-auto-over-array-instea.patch gce-compute-image-packages-20180905+dfsg1/debian/patches/0003-Use-C-style-iteration-without-auto-over-array-instea.patch --- gce-compute-image-packages-20180510+dfsg1/debian/patches/0003-Use-C-style-iteration-without-auto-over-array-instea.patch 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/debian/patches/0003-Use-C-style-iteration-without-auto-over-array-instea.patch 2018-09-16 22:17:02.000000000 +0000 @@ -0,0 +1,40 @@ +From 1b67dc27a9e76f49c9eae94bb46b8587f11403c4 Mon Sep 17 00:00:00 2001 +From: Balint Reczey +Date: Sun, 16 Sep 2018 23:33:14 +0200 +Subject: [PATCH 3/3] Use C style iteration without auto over array instead of + range-based for loop + +This allows compiling tests without C++11 support. +--- + google_compute_engine_oslogin/utils/oslogin_utils_test.cc | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/google_compute_engine_oslogin/utils/oslogin_utils_test.cc b/google_compute_engine_oslogin/utils/oslogin_utils_test.cc +index c1247c8..1643cd0 100644 +--- a/google_compute_engine_oslogin/utils/oslogin_utils_test.cc ++++ b/google_compute_engine_oslogin/utils/oslogin_utils_test.cc +@@ -440,8 +440,8 @@ TEST(ValidateUserNameTest, ValidateValidUserNames) { + "A_.-", + "ausernamethirtytwocharacterslong" + }; +- for (auto test_user : cases) { +- ASSERT_TRUE(ValidateUserName(test_user)); ++ for (int i = 0; i < sizeof(cases)/sizeof(cases[0]); i++) { ++ ASSERT_TRUE(ValidateUserName(cases[i])); + } + } + +@@ -459,8 +459,8 @@ TEST(ValidateUserNameTest, ValidateInvalidUserNames) { + "usernamethirtythreecharacterslong", + "../../etc/shadow", + }; +- for (auto test_user : cases) { +- ASSERT_FALSE(ValidateUserName(test_user)); ++ for (int i = 0; i < sizeof(cases)/sizeof(cases[0]); i++) { ++ ASSERT_FALSE(ValidateUserName(cases[i])); + } + } + +-- +2.17.1 + diff -Nru gce-compute-image-packages-20180510+dfsg1/debian/patches/0004-order-shutdown-scripts-after-snapd.patch gce-compute-image-packages-20180905+dfsg1/debian/patches/0004-order-shutdown-scripts-after-snapd.patch --- gce-compute-image-packages-20180510+dfsg1/debian/patches/0004-order-shutdown-scripts-after-snapd.patch 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/debian/patches/0004-order-shutdown-scripts-after-snapd.patch 2018-09-16 22:17:02.000000000 +0000 @@ -0,0 +1,15 @@ +Description: Order shutdown scripts after snapd.service + This ensures that snaps will be available when the scripts run on shutdown +Author: Daniel Watkins +Last-Update: 2018-05-17 + +--- a/google_compute_engine_init/systemd/google-shutdown-scripts.service ++++ b/google_compute_engine_init/systemd/google-shutdown-scripts.service +@@ -2,6 +2,7 @@ + Description=Google Compute Engine Shutdown Scripts + After=local-fs.target network-online.target network.target rsyslog.service systemd-resolved.service + After=google-instance-setup.service google-network-daemon.service ++After=snapd.service + Wants=local-fs.target network-online.target network.target + + [Service] diff -Nru gce-compute-image-packages-20180510+dfsg1/debian/patches/0005-add-snap-bin-to-path.patch gce-compute-image-packages-20180905+dfsg1/debian/patches/0005-add-snap-bin-to-path.patch --- gce-compute-image-packages-20180510+dfsg1/debian/patches/0005-add-snap-bin-to-path.patch 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/debian/patches/0005-add-snap-bin-to-path.patch 2018-09-16 22:17:02.000000000 +0000 @@ -0,0 +1,28 @@ +Description: Add /snap/bin to PATH for startup/shutdown scripts + These scripts often rely on binaries installed by the google-cloud-sdk snap, + so we need to ensure those binaries are available in the execution environment + of the units. This patch should be dropped once LP: #1771858 has been fixed + in systemd. +Author: Daniel Watkins +Last-Update: 2018-05-17 + +--- a/google_compute_engine_init/systemd/google-shutdown-scripts.service ++++ b/google_compute_engine_init/systemd/google-shutdown-scripts.service +@@ -11,6 +11,7 @@ + Type=oneshot + RemainAfterExit=true + TimeoutStopSec=0 ++Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin + + [Install] + WantedBy=multi-user.target +--- a/google_compute_engine_init/systemd/google-startup-scripts.service ++++ b/google_compute_engine_init/systemd/google-startup-scripts.service +@@ -11,6 +11,7 @@ + ExecStart=/usr/bin/google_metadata_script_runner --script-type startup + KillMode=process + Type=oneshot ++Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin + + [Install] + WantedBy=multi-user.target diff -Nru gce-compute-image-packages-20180510+dfsg1/debian/patches/series gce-compute-image-packages-20180905+dfsg1/debian/patches/series --- gce-compute-image-packages-20180510+dfsg1/debian/patches/series 2018-06-07 23:27:01.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/debian/patches/series 2018-09-16 22:17:02.000000000 +0000 @@ -1,5 +1,9 @@ -0001-Adjust-tests-to-changed-LoadJsonArrayToCache-behavio.patch +0001-Choose-std-or-boost-regex-based-on-support-for-C-11.patch +0002-Set-LDFLAGS-at-the-end-of-the-c-command-line-right-b.patch +0003-Use-C-style-iteration-without-auto-over-array-instea.patch fix-shutdown-script-ordering-upstart.patch fix-startup-script-ordering-upstart.patch fix-startup-script-ordering.patch #0003-order-startup-scripts-after-snap-seeding.service +#0004-order-shutdown-scripts-after-snapd.patch +#0005-add-snap-bin-to-path.patch diff -Nru gce-compute-image-packages-20180510+dfsg1/debian/rules gce-compute-image-packages-20180905+dfsg1/debian/rules --- gce-compute-image-packages-20180510+dfsg1/debian/rules 2018-06-07 23:27:01.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/debian/rules 2018-09-16 22:17:02.000000000 +0000 @@ -8,6 +8,9 @@ DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) +# link with boost for regex support +export DEB_LDFLAGS_MAINT_APPEND = -lboost_regex + %: dh $@ --with python2,systemd --buildsystem=pybuild @@ -18,7 +21,7 @@ override_dh_auto_test: (cd gtest && cmake . && $(MAKE) ) (cd $(OSLOGIN_DIR)/utils && \ - $(CXX) $(CXXFLAGS) -L../../gtest -o test_runner oslogin_utils_test.cc oslogin_utils.cc -I/usr/include/json-c -lcurl -ljson-c -lgtest -lpthread && \ + $(CXX) $(CXXFLAGS) -L../../gtest -o test_runner oslogin_utils_test.cc oslogin_utils.cc -I/usr/include/json-c $(LDFLAGS) -lcurl -ljson-c -lgtest -lpthread && \ ./test_runner && \ rm test_runner) diff -Nru gce-compute-image-packages-20180510+dfsg1/debian/source/lintian-overrides gce-compute-image-packages-20180905+dfsg1/debian/source/lintian-overrides --- gce-compute-image-packages-20180510+dfsg1/debian/source/lintian-overrides 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/debian/source/lintian-overrides 2018-09-16 22:17:02.000000000 +0000 @@ -0,0 +1,2 @@ +# binnmus are not used in Ubuntu +gce-compute-image-packages source: not-binnmuable-all-depends-any gce-compute-image-packages -> google-compute-engine-oslogin diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/accounts/accounts_utils.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/accounts/accounts_utils.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/accounts/accounts_utils.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/accounts/accounts_utils.py 2018-09-05 21:51:39.000000000 +0000 @@ -26,7 +26,7 @@ from google_compute_engine import constants from google_compute_engine import file_utils -USER_REGEX = re.compile(r'\A[A-Za-z0-9._][A-Za-z0-9._-]*\Z') +USER_REGEX = re.compile(r'\A[A-Za-z0-9._][A-Za-z0-9._-]{0,31}\Z') DEFAULT_GROUPADD_CMD = 'groupadd {group}' DEFAULT_USERADD_CMD = 'useradd -m -s /bin/bash -p * {user}' DEFAULT_USERDEL_CMD = 'userdel -r {user}' diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/accounts/oslogin_utils.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/accounts/oslogin_utils.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/accounts/oslogin_utils.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/accounts/oslogin_utils.py 2018-09-05 21:51:39.000000000 +0000 @@ -15,6 +15,7 @@ """Utilities for provisioning or deprovisioning a Linux user account.""" +import errno import os import subprocess import time @@ -50,7 +51,7 @@ try: return subprocess.call([constants.OSLOGIN_CONTROL_SCRIPT, action]) except OSError as e: - if e.errno == os.errno.ENOENT: + if e.errno == errno.ENOENT: return None else: raise @@ -83,7 +84,7 @@ try: return subprocess.call([constants.OSLOGIN_NSS_CACHE_SCRIPT]) except OSError as e: - if e.errno == os.errno.ENOENT: + if e.errno == errno.ENOENT: return None else: raise @@ -94,7 +95,7 @@ try: os.remove(constants.OSLOGIN_NSS_CACHE) except OSError as e: - if e.errno != os.errno.ENOENT: + if e.errno != errno.ENOENT: raise def UpdateOsLogin(self, enable, duration=NSS_CACHE_DURATION_SEC): diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/accounts/tests/accounts_utils_test.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/accounts/tests/accounts_utils_test.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/accounts/tests/accounts_utils_test.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/accounts/tests/accounts_utils_test.py 2018-09-05 21:51:39.000000000 +0000 @@ -572,6 +572,7 @@ 'abc xyz', 'xyz*', 'xyz$', + 'areallylongusernamethatexceedsthethirtytwocharacterlimit' ] for user in invalid_users: self.assertFalse( diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/clock_skew/clock_skew_daemon.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/clock_skew/clock_skew_daemon.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/clock_skew/clock_skew_daemon.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/clock_skew/clock_skew_daemon.py 2018-09-05 21:51:39.000000000 +0000 @@ -17,13 +17,13 @@ import logging.handlers import optparse -import subprocess from google_compute_engine import config_manager from google_compute_engine import constants from google_compute_engine import file_utils from google_compute_engine import logger from google_compute_engine import metadata_watcher +from google_compute_engine.compat import distro_utils LOCKFILE = constants.LOCALSTATEDIR + '/lock/google_clock_skew.lock' @@ -42,6 +42,7 @@ facility = logging.handlers.SysLogHandler.LOG_DAEMON self.logger = logger.Logger( name='google-clock-skew', debug=debug, facility=facility) + self.distro_utils = distro_utils.Utils(debug=debug) self.watcher = metadata_watcher.MetadataWatcher(logger=self.logger) try: with file_utils.LockFile(LOCKFILE): @@ -59,13 +60,7 @@ response: string, the metadata response with the new drift token value. """ self.logger.info('Clock drift token has changed: %s.', response) - command = ['/sbin/hwclock', '--hctosys'] - try: - subprocess.check_call(command) - except subprocess.CalledProcessError: - self.logger.warning('Failed to sync system time with hardware clock.') - else: - self.logger.info('Synced system time with hardware clock.') + self.distro_utils.HandleClockSync(self.logger) def main(): diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/clock_skew/tests/clock_skew_daemon_test.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/clock_skew/tests/clock_skew_daemon_test.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/clock_skew/tests/clock_skew_daemon_test.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/clock_skew/tests/clock_skew_daemon_test.py 2018-09-05 21:51:39.000000000 +0000 @@ -15,8 +15,6 @@ """Unittest for clock_skew_daemon.py module.""" -import subprocess - from google_compute_engine.clock_skew import clock_skew_daemon from google_compute_engine.test_compat import mock from google_compute_engine.test_compat import unittest @@ -71,36 +69,18 @@ ] self.assertEqual(mocks.mock_calls, expected_calls) - @mock.patch('google_compute_engine.clock_skew.clock_skew_daemon.subprocess.check_call') - def testHandleClockSync(self, mock_call): - command = ['/sbin/hwclock', '--hctosys'] - mock_sync = mock.create_autospec(clock_skew_daemon.ClockSkewDaemon) - mock_logger = mock.Mock() - mock_sync.logger = mock_logger - - clock_skew_daemon.ClockSkewDaemon.HandleClockSync(mock_sync, 'Response') - mock_call.assert_called_once_with(command) - expected_calls = [ - mock.call.info(mock.ANY, 'Response'), - mock.call.info(mock.ANY), - ] - self.assertEqual(mock_logger.mock_calls, expected_calls) - - @mock.patch('google_compute_engine.clock_skew.clock_skew_daemon.subprocess.check_call') - def testHandleClockSyncError(self, mock_call): - command = ['/sbin/hwclock', '--hctosys'] + @mock.patch('google_compute_engine.clock_skew.clock_skew_daemon.distro_utils') + def testHandleClockSync(self, mock_distro_utils): mock_sync = mock.create_autospec(clock_skew_daemon.ClockSkewDaemon) mock_logger = mock.Mock() mock_sync.logger = mock_logger - mock_call.side_effect = subprocess.CalledProcessError(1, 'Test') + mock_sync.distro_utils = mock_distro_utils clock_skew_daemon.ClockSkewDaemon.HandleClockSync(mock_sync, 'Response') - mock_call.assert_called_once_with(command) - expected_calls = [ - mock.call.info(mock.ANY, 'Response'), - mock.call.warning(mock.ANY), - ] + expected_calls = [mock.call.info(mock.ANY, 'Response')] self.assertEqual(mock_logger.mock_calls, expected_calls) + expected_calls = [mock.call.HandleClockSync(mock_logger)] + self.assertEqual(mock_distro_utils.mock_calls, expected_calls) if __name__ == '__main__': diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/compat.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/compat.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/compat.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/compat.py 2018-09-05 21:51:39.000000000 +0000 @@ -24,30 +24,35 @@ else: import platform as distro -distribution = distro.linux_distribution() +if 'freebsd' in sys.platform: + distribution = distro.version().split() +else: + distribution = distro.linux_distribution() distro_name = distribution[0].lower() distro_version = distribution[1].split('.')[0] distro_utils = None if 'centos' in distro_name and distro_version == '6': - import google_compute_engine.distro.el_6.utils as distro_utils + import google_compute_engine.distro_lib.el_6.utils as distro_utils elif 'centos' in distro_name: - import google_compute_engine.distro.el_7.utils as distro_utils + import google_compute_engine.distro_lib.el_7.utils as distro_utils elif 'red hat enterprise linux' in distro_name and distro_version == '6': - import google_compute_engine.distro.el_6.utils as distro_utils + import google_compute_engine.distro_lib.el_6.utils as distro_utils elif 'red hat enterprise linux' in distro_name: - import google_compute_engine.distro.el_7.utils as distro_utils + import google_compute_engine.distro_lib.el_7.utils as distro_utils elif 'debian' in distro_name and distro_version == '8': - import google_compute_engine.distro.debian_8.utils as distro_utils + import google_compute_engine.distro_lib.debian_8.utils as distro_utils elif 'debian' in distro_name: - import google_compute_engine.distro.debian_9.utils as distro_utils + import google_compute_engine.distro_lib.debian_9.utils as distro_utils elif 'suse' in distro_name and distro_version == '11': - import google_compute_engine.distro.sles_11.utils as distro_utils + import google_compute_engine.distro_lib.sles_11.utils as distro_utils elif 'suse' in distro_name: - import google_compute_engine.distro.sles_12.utils as distro_utils + import google_compute_engine.distro_lib.sles_12.utils as distro_utils +elif 'freebsd' in distro_name: + import google_compute_engine.distro_lib.freebsd_11.utils as distro_utils else: # Default to Debian 9. - import google_compute_engine.distro.debian_9.utils as distro_utils + import google_compute_engine.distro_lib.debian_9.utils as distro_utils RETRY_LIMIT = 3 TIMEOUT = 10 diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/constants.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/constants.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/constants.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/constants.py 2018-09-05 21:51:39.000000000 +0000 @@ -18,7 +18,6 @@ import platform OSLOGIN_CONTROL_SCRIPT = 'google_oslogin_control' -OSLOGIN_NSS_CACHE = '/etc/oslogin_passwd.cache' OSLOGIN_NSS_CACHE_SCRIPT = 'google_oslogin_nss_cache' if platform.system() == 'FreeBSD': @@ -26,13 +25,16 @@ BOTOCONFDIR = '/usr/local' SYSCONFDIR = '/usr/local/etc' LOCALSTATEDIR = '/var/spool' + OSLOGIN_NSS_CACHE = '/usr/local/etc/oslogin_passwd.cache' elif platform.system() == 'OpenBSD': LOCALBASE = '/usr/local' BOTOCONFDIR = '' SYSCONFDIR = '/usr/local/etc' LOCALSTATEDIR = '/var/spool' + OSLOGIN_NSS_CACHE = '/usr/local/etc/oslogin_passwd.cache' else: LOCALBASE = '' BOTOCONFDIR = '' SYSCONFDIR = '/etc/default' LOCALSTATEDIR = '/var' + OSLOGIN_NSS_CACHE = '/etc/oslogin_passwd.cache' diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/debian_8/tests/utils_test.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/debian_8/tests/utils_test.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/debian_8/tests/utils_test.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/debian_8/tests/utils_test.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ -#!/usr/bin/python -# Copyright 2018 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Unittest for utils.py module.""" - -from google_compute_engine.distro.debian_8 import utils -from google_compute_engine.test_compat import mock -from google_compute_engine.test_compat import unittest - - -class UtilsTest(unittest.TestCase): - - def setUp(self): - self.mock_logger = mock.Mock() - self.mock_setup = mock.create_autospec(utils.Utils) - - @mock.patch('google_compute_engine.distro.helpers.CallDhclient') - def testEnableNetworkInterfaces(self, mock_call): - mocks = mock.Mock() - mocks.attach_mock(mock_call, 'call') - - utils.Utils.EnableNetworkInterfaces( - self.mock_setup, ['A', 'B'], self.mock_logger) - expected_calls = [mock.call.call(['A', 'B'], mock.ANY)] - self.assertEqual(mocks.mock_calls, expected_calls) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/debian_8/utils.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/debian_8/utils.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/debian_8/utils.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/debian_8/utils.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -#!/usr/bin/python -# Copyright 2018 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Utilities that are distro specific for use on Debian 8.""" - -from google_compute_engine.distro import helpers -from google_compute_engine.distro import utils - - -class Utils(utils.Utils): - """Utilities used by Linux guest services on Debian 8.""" - - def EnableNetworkInterfaces( - self, interfaces, logger, dhclient_script=None): - """Enable the list of network interfaces. - - Args: - interfaces: list of string, the output device names to enable. - logger: logger object, used to write to SysLog and serial port. - dhclient_script: string, the path to a dhclient script used by dhclient. - """ - helpers.CallDhclient(interfaces, logger) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/debian_9/tests/utils_test.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/debian_9/tests/utils_test.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/debian_9/tests/utils_test.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/debian_9/tests/utils_test.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ -#!/usr/bin/python -# Copyright 2018 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Unittest for utils.py module.""" - -from google_compute_engine.distro.debian_9 import utils -from google_compute_engine.test_compat import mock -from google_compute_engine.test_compat import unittest - - -class UtilsTest(unittest.TestCase): - - def setUp(self): - self.mock_logger = mock.Mock() - self.mock_setup = mock.create_autospec(utils.Utils) - - @mock.patch('google_compute_engine.distro.helpers.CallDhclient') - def testEnableNetworkInterfaces(self, mock_call): - mocks = mock.Mock() - mocks.attach_mock(mock_call, 'call') - - utils.Utils.EnableNetworkInterfaces( - self.mock_setup, ['A', 'B'], self.mock_logger) - expected_calls = [mock.call.call(['A', 'B'], mock.ANY)] - self.assertEqual(mocks.mock_calls, expected_calls) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/debian_9/utils.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/debian_9/utils.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/debian_9/utils.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/debian_9/utils.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -#!/usr/bin/python -# Copyright 2018 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Utilities that are distro specific for use on Debian 9.""" - -from google_compute_engine.distro import helpers -from google_compute_engine.distro import utils - - -class Utils(utils.Utils): - """Utilities used by Linux guest services on Debian 9.""" - - def EnableNetworkInterfaces( - self, interfaces, logger, dhclient_script=None): - """Enable the list of network interfaces. - - Args: - interfaces: list of string, the output device names to enable. - logger: logger object, used to write to SysLog and serial port. - dhclient_script: string, the path to a dhclient script used by dhclient. - """ - helpers.CallDhclient(interfaces, logger) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/el_6/tests/utils_test.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/el_6/tests/utils_test.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/el_6/tests/utils_test.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/el_6/tests/utils_test.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,43 +0,0 @@ -#!/usr/bin/python -# Copyright 2018 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Unittest for utils.py module.""" - -from google_compute_engine.distro.el_6 import utils -from google_compute_engine.test_compat import mock -from google_compute_engine.test_compat import unittest - - -class UtilsTest(unittest.TestCase): - - def setUp(self): - self.mock_logger = mock.Mock() - self.mock_setup = mock.create_autospec(utils.Utils) - - def tearDown(self): - pass - - @mock.patch('google_compute_engine.distro.helpers.CallDhclient') - def testEnableNetworkInterfaces(self, mock_call): - mocks = mock.Mock() - mocks.attach_mock(mock_call, 'call') - - utils.Utils.EnableNetworkInterfaces( - self.mock_setup, ['A', 'B'], self.mock_logger, - dhclient_script='test_script') - expected_calls = [ - mock.call.call(['A', 'B'], mock.ANY, dhclient_script='test_script'), - ] - self.assertEqual(mocks.mock_calls, expected_calls) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/el_6/utils.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/el_6/utils.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/el_6/utils.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/el_6/utils.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -#!/usr/bin/python -# Copyright 2018 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Utilities that are distro specific for use on EL 6.""" - -from google_compute_engine.distro import helpers -from google_compute_engine.distro import utils - - -class Utils(utils.Utils): - """Utilities used by Linux guest services on Debian 8.""" - - def EnableNetworkInterfaces( - self, interfaces, logger, dhclient_script=None): - """Enable the list of network interfaces. - - Args: - interfaces: list of string, the output device names to enable. - logger: logger object, used to write to SysLog and serial port. - dhclient_script: string, the path to a dhclient script used by dhclient. - """ - helpers.CallDhclient(interfaces, logger, dhclient_script=dhclient_script) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/el_7/tests/utils_test.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/el_7/tests/utils_test.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/el_7/tests/utils_test.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/el_7/tests/utils_test.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,122 +0,0 @@ -#!/usr/bin/python -# Copyright 2018 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Unittest for utils.py module.""" - -import os -import shutil -import tempfile - -from google_compute_engine.distro.el_7 import utils -from google_compute_engine.test_compat import builtin -from google_compute_engine.test_compat import mock -from google_compute_engine.test_compat import unittest - - -class UtilsTest(unittest.TestCase): - - def setUp(self): - # Create a temporary directory. - self.test_dir = tempfile.mkdtemp() - self.mock_logger = mock.Mock() - self.mock_setup = mock.create_autospec(utils.Utils) - self.mock_setup.network_path = '/etc/sysconfig/network-scripts' - - def tearDown(self): - # Remove the directory after the test. - shutil.rmtree(self.test_dir) - - def testModifyInterface(self): - config_file = os.path.join(self.test_dir, 'config.cfg') - config_content = [ - '# File comment.\n', - 'A="apple"\n', - 'B=banana\n', - 'B=banana\n', - ] - with open(config_file, 'w') as config: - for line in config_content: - config.write(line) - - # Write a value for an existing config without overriding it. - utils.Utils._ModifyInterface( - self.mock_setup, config_file, 'A', 'aardvark', replace=False) - self.assertEquals(open(config_file).readlines(), config_content) - # Write a value for a config that is not already set. - utils.Utils._ModifyInterface( - self.mock_setup, config_file, 'C', 'none', replace=False) - config_content.append('C=none\n') - self.assertEquals(open(config_file).readlines(), config_content) - # Write a value for an existing config with replacement. - utils.Utils._ModifyInterface( - self.mock_setup, config_file, 'A', 'aardvark', replace=True) - config_content[1] = 'A=aardvark\n' - self.assertEquals(open(config_file).readlines(), config_content) - # Write a value for an existing config with multiple occurrences. - utils.Utils._ModifyInterface( - self.mock_setup, config_file, 'B', '"banana"', replace=True) - config_content[2] = config_content[3] = 'B="banana"\n' - self.assertEquals(open(config_file).readlines(), config_content) - - @mock.patch('google_compute_engine.distro.el_7.utils.os.path.exists') - def testDisableNetworkManager(self, mock_exists): - mock_open = mock.mock_open() - mocks = mock.Mock() - mocks.attach_mock(mock_exists, 'exists') - mocks.attach_mock(mock_open, 'open') - mocks.attach_mock(self.mock_logger, 'logger') - mocks.attach_mock(self.mock_setup._ModifyInterface, 'modify') - mock_exists.side_effect = [True, False] - - with mock.patch('%s.open' % builtin, mock_open, create=False): - utils.Utils._DisableNetworkManager( - self.mock_setup, ['eth0', 'eth1'], self.mock_logger) - expected_calls = [ - mock.call.exists('/etc/sysconfig/network-scripts/ifcfg-eth0'), - mock.call.modify(mock.ANY, 'DEVICE', 'eth0', replace=False), - mock.call.modify(mock.ANY, 'NM_CONTROLLED', 'no', replace=True), - mock.call.exists('/etc/sysconfig/network-scripts/ifcfg-eth1'), - mock.call.open('/etc/sysconfig/network-scripts/ifcfg-eth1', 'w'), - mock.call.open().__enter__(), - mock.call.open().write(mock.ANY), - mock.call.open().__exit__(None, None, None), - mock.call.logger.info(mock.ANY, 'eth1'), - ] - self.assertEqual(mocks.mock_calls, expected_calls) - - @mock.patch('google_compute_engine.distro.el_7.utils.os.path.exists') - @mock.patch('google_compute_engine.distro.helpers.CallDhclient') - def testEnableNetworkInterfaces(self, mock_call, mock_exists): - mocks = mock.Mock() - mocks.attach_mock(mock_exists, 'exists') - mocks.attach_mock(self.mock_logger, 'logger') - mocks.attach_mock(self.mock_setup._DisableNetworkManager, 'disable') - mocks.attach_mock(mock_call, 'call_dhclient') - mock_exists.side_effect = [True, False] - - # Enable interfaces with network manager enabled. - utils.Utils.EnableNetworkInterfaces( - self.mock_setup, ['A', 'B'], self.mock_logger) - # Enable interfaces with network manager is not present. - utils.Utils.EnableNetworkInterfaces( - self.mock_setup, ['C', 'D'], self.mock_logger) - expected_calls = [ - mock.call.exists('/etc/sysconfig/network-scripts'), - mock.call.disable(['A', 'B'], mock.ANY), - mock.call.call_dhclient(['A', 'B'], mock.ANY), - mock.call.exists('/etc/sysconfig/network-scripts'), - mock.call.call_dhclient(['C', 'D'], mock.ANY), - ] - self.assertEqual(mocks.mock_calls, expected_calls) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/el_7/utils.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/el_7/utils.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/el_7/utils.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/el_7/utils.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,91 +0,0 @@ -#!/usr/bin/python -# Copyright 2018 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Utilities that are distro specific for use on EL 7.""" - -import fileinput -import os -import re - -from google_compute_engine import constants -from google_compute_engine.distro import helpers -from google_compute_engine.distro import utils - - -class Utils(utils.Utils): - """Utilities used by Linux guest services on EL 7.""" - - network_path = constants.LOCALBASE + '/etc/sysconfig/network-scripts' - - def EnableNetworkInterfaces( - self, interfaces, logger, dhclient_script=None): - """Enable the list of network interfaces. - - Args: - interfaces: list of string, the output device names to enable. - logger: logger object, used to write to SysLog and serial port. - dhclient_script: string, the path to a dhclient script used by dhclient. - """ - # Should always exist in EL 7. - if os.path.exists(self.network_path): - self._DisableNetworkManager(interfaces, logger) - helpers.CallDhclient(interfaces, logger) - - def _DisableNetworkManager(self, interfaces, logger): - """Disable network manager management on a list of network interfaces. - - Args: - interfaces: list of string, the output device names enable. - logger: logger object, used to write to SysLog and serial port. - """ - for interface in interfaces: - interface_config = os.path.join(self.network_path, 'ifcfg-%s' % interface) - if os.path.exists(interface_config): - self._ModifyInterface( - interface_config, 'DEVICE', interface, replace=False) - self._ModifyInterface( - interface_config, 'NM_CONTROLLED', 'no', replace=True) - else: - with open(interface_config, 'w') as interface_file: - interface_content = [ - '# Added by Google.', - 'BOOTPROTO=none', - 'DEFROUTE=no', - 'DEVICE=%s' % interface, - 'IPV6INIT=no', - 'NM_CONTROLLED=no', - 'NOZEROCONF=yes', - '', - ] - interface_file.write('\n'.join(interface_content)) - logger.info('Created config file for interface %s.', interface) - - def _ModifyInterface( - self, interface_config, config_key, config_value, replace=False): - """Write a value to a config file if not already present. - - Args: - interface_config: string, the path to a config file. - config_key: string, the configuration key to set. - config_value: string, the value to set for the configuration key. - replace: bool, replace the configuration option if already present. - """ - config_entry = '%s=%s' % (config_key, config_value) - if not open(interface_config).read().count(config_key): - with open(interface_config, 'a') as config: - config.write('%s\n' % config_entry) - elif replace: - for line in fileinput.input(interface_config, inplace=True): - print(re.sub(r'%s=.*' % config_key, config_entry, line.rstrip())) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/helpers.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/helpers.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/helpers.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/helpers.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -#!/usr/bin/python -# Copyright 2018 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Distro helpers.""" - -import os -import subprocess - - -def CallDhclient( - interfaces, logger, dhclient_script=None): - """Configure the network interfaces using dhclient. - - Args: - interfaces: list of string, the output device names to enable. - logger: logger object, used to write to SysLog and serial port. - dhclient_script: string, the path to a dhclient script used by dhclient. - """ - logger.info('Enabling the Ethernet interfaces %s.', interfaces) - - dhclient_command = ['dhclient'] - - if dhclient_script and os.path.exists(dhclient_script): - dhclient_command += ['-sf', dhclient_script] - - try: - subprocess.check_call(dhclient_command + ['-x'] + interfaces) - subprocess.check_call(dhclient_command + interfaces) - except subprocess.CalledProcessError: - logger.warning('Could not enable interfaces %s.', interfaces) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/sles_11/tests/utils_test.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/sles_11/tests/utils_test.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/sles_11/tests/utils_test.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/sles_11/tests/utils_test.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,73 +0,0 @@ -#!/usr/bin/python -# Copyright 2018 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Unittest for utils.py module.""" - -import subprocess - -from google_compute_engine.distro.sles_11 import utils -from google_compute_engine.test_compat import mock -from google_compute_engine.test_compat import unittest - - -class UtilsTest(unittest.TestCase): - - def setUp(self): - self.mock_logger = mock.Mock() - self.mock_setup = mock.create_autospec(utils.Utils) - - def testEnableNetworkInterfacesWithSingleNic(self): - mocks = mock.Mock() - - utils.Utils.EnableNetworkInterfaces( - self.mock_setup, ['eth0'], self.mock_logger) - expected_calls = [] - self.assertEqual(mocks.mock_calls, expected_calls) - - def testEnableNetworkInterfacesWithMultipleNics(self): - mocks = mock.Mock() - mocks.attach_mock(self.mock_setup._Dhcpcd, 'dhcpcd') - - utils.Utils.EnableNetworkInterfaces( - self.mock_setup, ['eth0', 'eth1', 'eth2'], self.mock_logger) - expected_calls = [ - mock.call.dhcpcd(['eth1', 'eth2'], mock.ANY), - ] - self.assertEqual(mocks.mock_calls, expected_calls) - - @mock.patch( - 'google_compute_engine.distro.sles_11.utils.subprocess.check_call') - def testDhcpcd(self, mock_call): - mocks = mock.Mock() - mocks.attach_mock(mock_call, 'call') - mocks.attach_mock(self.mock_logger, 'logger') - mock_call.side_effect = [ - None, None, None, None, - subprocess.CalledProcessError(1, 'Test'), - subprocess.CalledProcessError(1, 'Test'), - ] - - utils.Utils._Dhcpcd( - self.mock_setup, ['eth1', 'eth2', 'eth3'], self.mock_logger) - expected_calls = [ - mock.call.call(['/sbin/dhcpcd', '-x', 'eth1']), - mock.call.call(['/sbin/dhcpcd', 'eth1']), - mock.call.call(['/sbin/dhcpcd', '-x', 'eth2']), - mock.call.call(['/sbin/dhcpcd', 'eth2']), - mock.call.call(['/sbin/dhcpcd', '-x', 'eth3']), - mock.call.logger.info(mock.ANY, 'eth3'), - mock.call.call(['/sbin/dhcpcd','eth3']), - mock.call.logger.warning(mock.ANY, 'eth3'), - ] - self.assertEqual(mocks.mock_calls, expected_calls) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/sles_11/utils.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/sles_11/utils.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/sles_11/utils.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/sles_11/utils.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,60 +0,0 @@ -#!/usr/bin/python -# Copyright 2018 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Utilities that are distro specific for use on SUSE 11.""" - -import os -import subprocess - -from google_compute_engine import constants -from google_compute_engine.distro import utils - - -class Utils(utils.Utils): - """Utilities used by Linux guest services on SUSE 11.""" - - def EnableNetworkInterfaces( - self, interfaces, logger, dhclient_script=None): - """Enable the list of network interfaces. - - Args: - interfaces: list of string, the output device names to enable. - logger: logger object, used to write to SysLog and serial port. - dhclient_script: string, the path to a dhclient script used by dhclient. - """ - interfaces_to_up = [i for i in interfaces if i != 'eth0'] - if interfaces_to_up: - logger.info('Enabling the Ethernet interfaces %s.', interfaces_to_up) - self._Dhcpcd(interfaces_to_up, logger) - - def _Dhcpcd(self, interfaces, logger): - """Use dhcpcd to activate the interfaces. - - Args: - interfaces: list of string, the output device names to enable. - logger: logger object, used to write to SysLog and serial port. - """ - for interface in interfaces: - dhcpcd = ['/sbin/dhcpcd'] - try: - subprocess.check_call(dhcpcd + ['-x', interface]) - except subprocess.CalledProcessError: - # Dhcpcd not yet running for this device. - logger.info('Dhcpcd not yet running for interface %s.', interface) - try: - subprocess.check_call(dhcpcd + [interface]) - except subprocess.CalledProcessError: - # The interface is already active. - logger.warning('Could not activate interface %s.', interface) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/sles_12/tests/utils_test.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/sles_12/tests/utils_test.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/sles_12/tests/utils_test.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/sles_12/tests/utils_test.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,93 +0,0 @@ -#!/usr/bin/python -# Copyright 2018 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Unittest for utils.py module.""" - -import subprocess - -from google_compute_engine.distro.sles_12 import utils -from google_compute_engine.test_compat import builtin -from google_compute_engine.test_compat import mock -from google_compute_engine.test_compat import unittest - - -class UtilsTest(unittest.TestCase): - - def setUp(self): - self.mock_logger = mock.Mock() - self.mock_setup = mock.create_autospec(utils.Utils) - self.mock_setup.network_path = '/etc/sysconfig/network' - - def testEnableNetworkInterfacesWithSingleNic(self): - mocks = mock.Mock() - - utils.Utils.EnableNetworkInterfaces( - self.mock_setup, ['eth0'], self.mock_logger) - expected_calls = [] - self.assertEqual(mocks.mock_calls, expected_calls) - - def testEnableNetworkInterfacesWithMultipleNics(self): - mocks = mock.Mock() - mocks.attach_mock(self.mock_setup._WriteIfcfg, 'writeIfcfg') - mocks.attach_mock(self.mock_setup._Ifup, 'ifup') - - utils.Utils.EnableNetworkInterfaces( - self.mock_setup, ['eth0', 'eth1', 'eth2'], self.mock_logger) - expected_calls = [ - mock.call.writeIfcfg(['eth1', 'eth2'], mock.ANY), - mock.call.ifup(['eth1', 'eth2'], mock.ANY), - ] - self.assertEqual(mocks.mock_calls, expected_calls) - - def testWriteIfcfg(self): - mocks = mock.Mock() - mock_open = mock.mock_open() - mocks.attach_mock(mock_open, 'open') - with mock.patch('%s.open' % builtin, mock_open, create=False): - - utils.Utils._WriteIfcfg( - self.mock_setup, ['eth1', 'eth2'], self.mock_logger) - expected_calls = [ - mock.call.open('/etc/sysconfig/network/ifcfg-eth1', 'w'), - mock.call.open().__enter__(), - mock.call.open().write(mock.ANY), - mock.call.open().__exit__(None, None, None), - mock.call.open('/etc/sysconfig/network/ifcfg-eth2', 'w'), - mock.call.open().__enter__(), - mock.call.open().write(mock.ANY), - mock.call.open().__exit__(None, None, None), - ] - self.assertEqual(mocks.mock_calls, expected_calls) - - @mock.patch( - 'google_compute_engine.distro.sles_12.utils.subprocess.check_call') - def testIfup(self, mock_call): - mocks = mock.Mock() - mocks.attach_mock(mock_call, 'call') - mocks.attach_mock(self.mock_logger, 'logger') - mock_call.side_effect = [ - None, subprocess.CalledProcessError(1, 'Test'), - ] - - utils.Utils._Ifup(self.mock_setup, ['eth1', 'eth2'], self.mock_logger) - utils.Utils._Ifup(self.mock_setup, ['eth1', 'eth2'], self.mock_logger) - expectedIfupCall = [ - '/usr/sbin/wicked', 'ifup', '--timeout', '1', 'eth1', 'eth2', - ] - expected_calls = [ - mock.call.call(expectedIfupCall), - mock.call.call(expectedIfupCall), - mock.call.logger.warning(mock.ANY, ['eth1', 'eth2']), - ] - self.assertEqual(mocks.mock_calls, expected_calls) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/sles_12/utils.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/sles_12/utils.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/sles_12/utils.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/sles_12/utils.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,82 +0,0 @@ -#!/usr/bin/python -# Copyright 2018 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Utilities that are distro specific for use on SUSE 12.""" - -import os -import subprocess - -from google_compute_engine import constants -from google_compute_engine.distro import utils - - -class Utils(utils.Utils): - """Utilities used by Linux guest services on SUSE 12.""" - - network_path = constants.LOCALBASE + '/etc/sysconfig/network' - - def EnableNetworkInterfaces( - self, interfaces, logger, dhclient_script=None): - """Enable the list of network interfaces. - - Args: - interfaces: list of string, the output device names to enable. - logger: logger object, used to write to SysLog and serial port. - dhclient_script: string, the path to a dhclient script used by dhclient. - """ - interfaces_to_up = [i for i in interfaces if i != 'eth0'] - if interfaces_to_up: - logger.info('Enabling the Ethernet interfaces %s.', interfaces_to_up) - self._WriteIfcfg(interfaces_to_up, logger) - self._Ifup(interfaces_to_up, logger) - - def _WriteIfcfg(self, interfaces, logger): - """Write ifcfg files for multi-NIC support. - - Overwrites the files. This allows us to update ifcfg-* in the future. - Disable the network setup to override this behavior and customize the - configurations. - - Args: - interfaces: list of string, the output device names to enable. - logger: logger object, used to write to SysLog and serial port. - """ - for interface in interfaces: - interface_config = os.path.join( - self.network_path, 'ifcfg-%s' % interface) - interface_content = [ - '# Added by Google.', - 'STARTMODE=hotplug', - 'BOOTPROTO=dhcp', - 'DHCLIENT_SET_DEFAULT_ROUTE=yes', - 'DHCLIENT_ROUTE_PRIORITY=10%s00' % interface, - '', - ] - with open(interface_config, 'w') as interface_file: - interface_file.write('\n'.join(interface_content)) - logger.info('Created ifcfg file for interface %s.', interface) - - def _Ifup(self, interfaces, logger): - """Activate network interfaces. - - Args: - interfaces: list of string, the output device names to enable. - logger: logger object, used to write to SysLog and serial port. - """ - ifup = ['/usr/sbin/wicked', 'ifup', '--timeout', '1'] - try: - subprocess.check_call(ifup + interfaces) - except subprocess.CalledProcessError: - logger.warning('Could not activate interfaces %s.', interfaces) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/tests/helpers_test.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/tests/helpers_test.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/tests/helpers_test.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/tests/helpers_test.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -#!/usr/bin/python -# Copyright 2018 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Unittest for helpers.py module.""" - -import subprocess - -from google_compute_engine.distro import helpers -from google_compute_engine.test_compat import mock -from google_compute_engine.test_compat import unittest - - -class HelpersTest(unittest.TestCase): - - def setUp(self): - self.mock_logger = mock.Mock() - - @mock.patch('google_compute_engine.distro.helpers.os.path.exists') - @mock.patch('google_compute_engine.distro.helpers.subprocess.check_call') - def testCallDhclient(self, mock_call, mock_exists): - mocks = mock.Mock() - mocks.attach_mock(mock_exists, 'exists') - mocks.attach_mock(mock_call, 'call') - mocks.attach_mock(self.mock_logger, 'logger') - - mock_exists.side_effect = [False, True] - mock_call.side_effect = [ - None, None, None, None, None, None, - subprocess.CalledProcessError(1, 'Test'), - ] - - helpers.CallDhclient(['a', 'b'], self.mock_logger, 'test_script') - helpers.CallDhclient(['c', 'd'], self.mock_logger, 'test_script') - helpers.CallDhclient(['e', 'f'], self.mock_logger, None) - helpers.CallDhclient(['g', 'h'], self.mock_logger, None) - - expected_calls = [ - mock.call.logger.info(mock.ANY, ['a', 'b']), - mock.call.exists('test_script'), - mock.call.call(['dhclient', '-x', 'a', 'b']), - mock.call.call(['dhclient', 'a', 'b']), - mock.call.logger.info(mock.ANY, ['c', 'd']), - mock.call.exists('test_script'), - mock.call.call(['dhclient', '-sf', 'test_script', '-x', 'c', 'd']), - mock.call.call(['dhclient', '-sf', 'test_script', 'c', 'd']), - mock.call.logger.info(mock.ANY, ['e', 'f']), - mock.call.call(['dhclient', '-x', 'e', 'f']), - mock.call.call(['dhclient', 'e', 'f']), - mock.call.logger.info(mock.ANY, ['g', 'h']), - mock.call.call(['dhclient', '-x', 'g', 'h']), - mock.call.logger.warning(mock.ANY, ['g', 'h']), - ] - - self.assertEqual(mocks.mock_calls, expected_calls) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/utils.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/utils.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro/utils.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro/utils.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -#!/usr/bin/python -# Copyright 2018 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Utilities that are distro specific.""" - - -class Utils(object): - """Utilities used by Linux guest services.""" - - def __init__(self, debug=False): - """Constructor. - - Args: - debug: bool, True if debug output should write to the console. - """ - self.debug = debug - - def EnableNetworkInterfaces( - self, interfaces, logger, dhclient_script=None): - """Enable the list of network interfaces. - - Args: - interfaces: list of string, the output device names to enable. - logger: logger object, used to write to SysLog and serial port. - dhclient_script: string, the path to a dhclient script used by dhclient. - """ - pass diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/debian_8/tests/utils_test.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/debian_8/tests/utils_test.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/debian_8/tests/utils_test.py 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/debian_8/tests/utils_test.py 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1,55 @@ +#!/usr/bin/python +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unittest for utils.py module.""" + +from google_compute_engine.distro_lib.debian_8 import utils +from google_compute_engine.test_compat import mock +from google_compute_engine.test_compat import unittest + + +class UtilsTest(unittest.TestCase): + + def setUp(self): + self.mock_logger = mock.Mock() + self.mock_setup = mock.create_autospec(utils.Utils) + + @mock.patch('google_compute_engine.distro_lib.helpers.CallDhclient') + def testEnableNetworkInterfaces(self, mock_call): + mocks = mock.Mock() + mocks.attach_mock(mock_call, 'call') + + utils.Utils.EnableNetworkInterfaces( + self.mock_setup, ['A', 'B'], self.mock_logger) + expected_calls = [mock.call.call(['A', 'B'], mock.ANY)] + self.assertEqual(mocks.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.helpers.CallHwclock') + def testHandleClockSync(self, mock_call): + mocks = mock.Mock() + mocks.attach_mock(mock_call, 'call') + + utils.Utils.HandleClockSync(self.mock_setup, self.mock_logger) + expected_calls = [mock.call.call(mock.ANY)] + self.assertEqual(mocks.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.ip_forwarding_utils.IpForwardingUtilsIproute') + def testIpForwardingUtils(self, mock_call): + mocks = mock.Mock() + mocks.attach_mock(mock_call, 'call') + + utils.Utils.IpForwardingUtils(self.mock_setup, self.mock_logger, '66') + expected_calls = [mock.call.call(mock.ANY, '66')] + self.assertEqual(mocks.mock_calls, expected_calls) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/debian_8/utils.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/debian_8/utils.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/debian_8/utils.py 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/debian_8/utils.py 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1,52 @@ +#!/usr/bin/python +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Utilities that are distro specific for use on Debian 8.""" + +from google_compute_engine.distro_lib import helpers +from google_compute_engine.distro_lib import ip_forwarding_utils +from google_compute_engine.distro_lib import utils + + +class Utils(utils.Utils): + """Utilities used by Linux guest services on Debian 8.""" + + def EnableNetworkInterfaces( + self, interfaces, logger, dhclient_script=None): + """Enable the list of network interfaces. + + Args: + interfaces: list of string, the output device names to enable. + logger: logger object, used to write to SysLog and serial port. + dhclient_script: string, the path to a dhclient script used by dhclient. + """ + helpers.CallDhclient(interfaces, logger) + + def HandleClockSync(self, logger): + """Sync the software clock with the hypervisor clock. + + Args: + logger: logger object, used to write to SysLog and serial port. + """ + helpers.CallHwclock(logger) + + def IpForwardingUtils(self, logger, proto_id=None): + """Get system IP address configuration utilities. + + Args: + logger: logger object, used to write to SysLog and serial port. + proto_id: string, the routing protocol identifier for Google IP changes. + """ + return ip_forwarding_utils.IpForwardingUtilsIproute(logger, proto_id) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/debian_9/tests/utils_test.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/debian_9/tests/utils_test.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/debian_9/tests/utils_test.py 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/debian_9/tests/utils_test.py 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1,55 @@ +#!/usr/bin/python +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unittest for utils.py module.""" + +from google_compute_engine.distro_lib.debian_9 import utils +from google_compute_engine.test_compat import mock +from google_compute_engine.test_compat import unittest + + +class UtilsTest(unittest.TestCase): + + def setUp(self): + self.mock_logger = mock.Mock() + self.mock_setup = mock.create_autospec(utils.Utils) + + @mock.patch('google_compute_engine.distro_lib.helpers.CallDhclient') + def testEnableNetworkInterfaces(self, mock_call): + mocks = mock.Mock() + mocks.attach_mock(mock_call, 'call') + + utils.Utils.EnableNetworkInterfaces( + self.mock_setup, ['A', 'B'], self.mock_logger) + expected_calls = [mock.call.call(['A', 'B'], mock.ANY)] + self.assertEqual(mocks.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.helpers.CallHwclock') + def testHandleClockSync(self, mock_call): + mocks = mock.Mock() + mocks.attach_mock(mock_call, 'call') + + utils.Utils.HandleClockSync(self.mock_setup, self.mock_logger) + expected_calls = [mock.call.call(mock.ANY)] + self.assertEqual(mocks.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.ip_forwarding_utils.IpForwardingUtilsIproute') + def testIpForwardingUtils(self, mock_call): + mocks = mock.Mock() + mocks.attach_mock(mock_call, 'call') + + utils.Utils.IpForwardingUtils(self.mock_setup, self.mock_logger, '66') + expected_calls = [mock.call.call(mock.ANY, '66')] + self.assertEqual(mocks.mock_calls, expected_calls) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/debian_9/utils.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/debian_9/utils.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/debian_9/utils.py 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/debian_9/utils.py 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1,52 @@ +#!/usr/bin/python +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Utilities that are distro specific for use on Debian 9.""" + +from google_compute_engine.distro_lib import helpers +from google_compute_engine.distro_lib import ip_forwarding_utils +from google_compute_engine.distro_lib import utils + + +class Utils(utils.Utils): + """Utilities used by Linux guest services on Debian 9.""" + + def EnableNetworkInterfaces( + self, interfaces, logger, dhclient_script=None): + """Enable the list of network interfaces. + + Args: + interfaces: list of string, the output device names to enable. + logger: logger object, used to write to SysLog and serial port. + dhclient_script: string, the path to a dhclient script used by dhclient. + """ + helpers.CallDhclient(interfaces, logger) + + def HandleClockSync(self, logger): + """Sync the software clock with the hypervisor clock. + + Args: + logger: logger object, used to write to SysLog and serial port. + """ + helpers.CallHwclock(logger) + + def IpForwardingUtils(self, logger, proto_id=None): + """Get system IP address configuration utilities. + + Args: + logger: logger object, used to write to SysLog and serial port. + proto_id: string, the routing protocol identifier for Google IP changes. + """ + return ip_forwarding_utils.IpForwardingUtilsIproute(logger, proto_id) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/el_6/tests/utils_test.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/el_6/tests/utils_test.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/el_6/tests/utils_test.py 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/el_6/tests/utils_test.py 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1,61 @@ +#!/usr/bin/python +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unittest for utils.py module.""" + +from google_compute_engine.distro_lib.el_6 import utils +from google_compute_engine.test_compat import mock +from google_compute_engine.test_compat import unittest + + +class UtilsTest(unittest.TestCase): + + def setUp(self): + self.mock_logger = mock.Mock() + self.mock_setup = mock.create_autospec(utils.Utils) + + def tearDown(self): + pass + + @mock.patch('google_compute_engine.distro_lib.helpers.CallDhclient') + def testEnableNetworkInterfaces(self, mock_call): + mocks = mock.Mock() + mocks.attach_mock(mock_call, 'call') + + utils.Utils.EnableNetworkInterfaces( + self.mock_setup, ['A', 'B'], self.mock_logger, + dhclient_script='test_script') + expected_calls = [ + mock.call.call(['A', 'B'], mock.ANY, dhclient_script='test_script'), + ] + self.assertEqual(mocks.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.helpers.CallHwclock') + def testHandleClockSync(self, mock_call): + mocks = mock.Mock() + mocks.attach_mock(mock_call, 'call') + + utils.Utils.HandleClockSync(self.mock_setup, self.mock_logger) + expected_calls = [mock.call.call(mock.ANY)] + self.assertEqual(mocks.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.ip_forwarding_utils.IpForwardingUtilsIproute') + def testIpForwardingUtils(self, mock_call): + mocks = mock.Mock() + mocks.attach_mock(mock_call, 'call') + + utils.Utils.IpForwardingUtils(self.mock_setup, self.mock_logger, '66') + expected_calls = [mock.call.call(mock.ANY, '66')] + self.assertEqual(mocks.mock_calls, expected_calls) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/el_6/utils.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/el_6/utils.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/el_6/utils.py 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/el_6/utils.py 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1,52 @@ +#!/usr/bin/python +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Utilities that are distro specific for use on EL 6.""" + +from google_compute_engine.distro_lib import helpers +from google_compute_engine.distro_lib import ip_forwarding_utils +from google_compute_engine.distro_lib import utils + + +class Utils(utils.Utils): + """Utilities used by Linux guest services on Debian 8.""" + + def EnableNetworkInterfaces( + self, interfaces, logger, dhclient_script=None): + """Enable the list of network interfaces. + + Args: + interfaces: list of string, the output device names to enable. + logger: logger object, used to write to SysLog and serial port. + dhclient_script: string, the path to a dhclient script used by dhclient. + """ + helpers.CallDhclient(interfaces, logger, dhclient_script=dhclient_script) + + def HandleClockSync(self, logger): + """Sync the software clock with the hypervisor clock. + + Args: + logger: logger object, used to write to SysLog and serial port. + """ + helpers.CallHwclock(logger) + + def IpForwardingUtils(self, logger, proto_id=None): + """Get system IP address configuration utilities. + + Args: + logger: logger object, used to write to SysLog and serial port. + proto_id: string, the routing protocol identifier for Google IP changes. + """ + return ip_forwarding_utils.IpForwardingUtilsIproute(logger, proto_id) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/el_7/tests/utils_test.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/el_7/tests/utils_test.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/el_7/tests/utils_test.py 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/el_7/tests/utils_test.py 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1,140 @@ +#!/usr/bin/python +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unittest for utils.py module.""" + +import os +import shutil +import tempfile + +from google_compute_engine.distro_lib.el_7 import utils +from google_compute_engine.test_compat import builtin +from google_compute_engine.test_compat import mock +from google_compute_engine.test_compat import unittest + + +class UtilsTest(unittest.TestCase): + + def setUp(self): + # Create a temporary directory. + self.test_dir = tempfile.mkdtemp() + self.mock_logger = mock.Mock() + self.mock_setup = mock.create_autospec(utils.Utils) + self.mock_setup.network_path = '/etc/sysconfig/network-scripts' + + def tearDown(self): + # Remove the directory after the test. + shutil.rmtree(self.test_dir) + + def testModifyInterface(self): + config_file = os.path.join(self.test_dir, 'config.cfg') + config_content = [ + '# File comment.\n', + 'A="apple"\n', + 'B=banana\n', + 'B=banana\n', + ] + with open(config_file, 'w') as config: + for line in config_content: + config.write(line) + + # Write a value for an existing config without overriding it. + utils.Utils._ModifyInterface( + self.mock_setup, config_file, 'A', 'aardvark', replace=False) + self.assertEquals(open(config_file).readlines(), config_content) + # Write a value for a config that is not already set. + utils.Utils._ModifyInterface( + self.mock_setup, config_file, 'C', 'none', replace=False) + config_content.append('C=none\n') + self.assertEquals(open(config_file).readlines(), config_content) + # Write a value for an existing config with replacement. + utils.Utils._ModifyInterface( + self.mock_setup, config_file, 'A', 'aardvark', replace=True) + config_content[1] = 'A=aardvark\n' + self.assertEquals(open(config_file).readlines(), config_content) + # Write a value for an existing config with multiple occurrences. + utils.Utils._ModifyInterface( + self.mock_setup, config_file, 'B', '"banana"', replace=True) + config_content[2] = config_content[3] = 'B="banana"\n' + self.assertEquals(open(config_file).readlines(), config_content) + + @mock.patch('google_compute_engine.distro_lib.el_7.utils.os.path.exists') + def testDisableNetworkManager(self, mock_exists): + mock_open = mock.mock_open() + mocks = mock.Mock() + mocks.attach_mock(mock_exists, 'exists') + mocks.attach_mock(mock_open, 'open') + mocks.attach_mock(self.mock_logger, 'logger') + mocks.attach_mock(self.mock_setup._ModifyInterface, 'modify') + mock_exists.side_effect = [True, False] + + with mock.patch('%s.open' % builtin, mock_open, create=False): + utils.Utils._DisableNetworkManager( + self.mock_setup, ['eth0', 'eth1'], self.mock_logger) + expected_calls = [ + mock.call.exists('/etc/sysconfig/network-scripts/ifcfg-eth0'), + mock.call.modify(mock.ANY, 'DEVICE', 'eth0', replace=False), + mock.call.modify(mock.ANY, 'NM_CONTROLLED', 'no', replace=True), + mock.call.exists('/etc/sysconfig/network-scripts/ifcfg-eth1'), + mock.call.open('/etc/sysconfig/network-scripts/ifcfg-eth1', 'w'), + mock.call.open().__enter__(), + mock.call.open().write(mock.ANY), + mock.call.open().__exit__(None, None, None), + mock.call.logger.info(mock.ANY, 'eth1'), + ] + self.assertEqual(mocks.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.el_7.utils.os.path.exists') + @mock.patch('google_compute_engine.distro_lib.helpers.CallDhclient') + def testEnableNetworkInterfaces(self, mock_call, mock_exists): + mocks = mock.Mock() + mocks.attach_mock(mock_exists, 'exists') + mocks.attach_mock(self.mock_logger, 'logger') + mocks.attach_mock(self.mock_setup._DisableNetworkManager, 'disable') + mocks.attach_mock(mock_call, 'call_dhclient') + mock_exists.side_effect = [True, False] + + # Enable interfaces with network manager enabled. + utils.Utils.EnableNetworkInterfaces( + self.mock_setup, ['A', 'B'], self.mock_logger) + # Enable interfaces with network manager is not present. + utils.Utils.EnableNetworkInterfaces( + self.mock_setup, ['C', 'D'], self.mock_logger) + expected_calls = [ + mock.call.exists('/etc/sysconfig/network-scripts'), + mock.call.disable(['A', 'B'], mock.ANY), + mock.call.call_dhclient(['A', 'B'], mock.ANY), + mock.call.exists('/etc/sysconfig/network-scripts'), + mock.call.call_dhclient(['C', 'D'], mock.ANY), + ] + self.assertEqual(mocks.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.helpers.CallHwclock') + def testHandleClockSync(self, mock_call): + mocks = mock.Mock() + mocks.attach_mock(mock_call, 'call') + + utils.Utils.HandleClockSync(self.mock_setup, self.mock_logger) + expected_calls = [mock.call.call(mock.ANY)] + self.assertEqual(mocks.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.ip_forwarding_utils.IpForwardingUtilsIproute') + def testIpForwardingUtils(self, mock_call): + mocks = mock.Mock() + mocks.attach_mock(mock_call, 'call') + + utils.Utils.IpForwardingUtils(self.mock_setup, self.mock_logger, '66') + expected_calls = [mock.call.call(mock.ANY, '66')] + self.assertEqual(mocks.mock_calls, expected_calls) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/el_7/utils.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/el_7/utils.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/el_7/utils.py 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/el_7/utils.py 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1,110 @@ +#!/usr/bin/python +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Utilities that are distro specific for use on EL 7.""" + +import fileinput +import os +import re + +from google_compute_engine import constants +from google_compute_engine.distro_lib import helpers +from google_compute_engine.distro_lib import ip_forwarding_utils +from google_compute_engine.distro_lib import utils + + +class Utils(utils.Utils): + """Utilities used by Linux guest services on EL 7.""" + + network_path = constants.LOCALBASE + '/etc/sysconfig/network-scripts' + + def EnableNetworkInterfaces( + self, interfaces, logger, dhclient_script=None): + """Enable the list of network interfaces. + + Args: + interfaces: list of string, the output device names to enable. + logger: logger object, used to write to SysLog and serial port. + dhclient_script: string, the path to a dhclient script used by dhclient. + """ + # Should always exist in EL 7. + if os.path.exists(self.network_path): + self._DisableNetworkManager(interfaces, logger) + helpers.CallDhclient(interfaces, logger) + + def _DisableNetworkManager(self, interfaces, logger): + """Disable network manager management on a list of network interfaces. + + Args: + interfaces: list of string, the output device names enable. + logger: logger object, used to write to SysLog and serial port. + """ + for interface in interfaces: + interface_config = os.path.join( + self.network_path, 'ifcfg-%s' % interface) + if os.path.exists(interface_config): + self._ModifyInterface( + interface_config, 'DEVICE', interface, replace=False) + self._ModifyInterface( + interface_config, 'NM_CONTROLLED', 'no', replace=True) + else: + with open(interface_config, 'w') as interface_file: + interface_content = [ + '# Added by Google.', + 'BOOTPROTO=none', + 'DEFROUTE=no', + 'DEVICE=%s' % interface, + 'IPV6INIT=no', + 'NM_CONTROLLED=no', + 'NOZEROCONF=yes', + '', + ] + interface_file.write('\n'.join(interface_content)) + logger.info('Created config file for interface %s.', interface) + + def _ModifyInterface( + self, interface_config, config_key, config_value, replace=False): + """Write a value to a config file if not already present. + + Args: + interface_config: string, the path to a config file. + config_key: string, the configuration key to set. + config_value: string, the value to set for the configuration key. + replace: bool, replace the configuration option if already present. + """ + config_entry = '%s=%s' % (config_key, config_value) + if not open(interface_config).read().count(config_key): + with open(interface_config, 'a') as config: + config.write('%s\n' % config_entry) + elif replace: + for line in fileinput.input(interface_config, inplace=True): + print(re.sub(r'%s=.*' % config_key, config_entry, line.rstrip())) + + def HandleClockSync(self, logger): + """Sync the software clock with the hypervisor clock. + + Args: + logger: logger object, used to write to SysLog and serial port. + """ + helpers.CallHwclock(logger) + + def IpForwardingUtils(self, logger, proto_id=None): + """Get system IP address configuration utilities. + + Args: + logger: logger object, used to write to SysLog and serial port. + proto_id: string, the routing protocol identifier for Google IP changes. + """ + return ip_forwarding_utils.IpForwardingUtilsIproute(logger, proto_id) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/freebsd_11/tests/utils_test.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/freebsd_11/tests/utils_test.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/freebsd_11/tests/utils_test.py 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/freebsd_11/tests/utils_test.py 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1,55 @@ +#!/usr/bin/python +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unittest for utils.py module.""" + +from google_compute_engine.distro_lib.freebsd_11 import utils +from google_compute_engine.test_compat import mock +from google_compute_engine.test_compat import unittest + + +class UtilsTest(unittest.TestCase): + + def setUp(self): + self.mock_logger = mock.Mock() + self.mock_setup = mock.create_autospec(utils.Utils) + + @mock.patch('google_compute_engine.distro_lib.helpers.CallDhclient') + def testEnableNetworkInterfaces(self, mock_call): + mocks = mock.Mock() + mocks.attach_mock(mock_call, 'call') + + utils.Utils.EnableNetworkInterfaces( + self.mock_setup, ['A', 'B'], self.mock_logger) + expected_calls = [mock.call.call(['A', 'B'], mock.ANY)] + self.assertEqual(mocks.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.helpers.CallNtpdate') + def testHandleClockSync(self, mock_call): + mocks = mock.Mock() + mocks.attach_mock(mock_call, 'call') + + utils.Utils.HandleClockSync(self.mock_setup, self.mock_logger) + expected_calls = [mock.call.call(mock.ANY)] + self.assertEqual(mocks.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.ip_forwarding_utils.IpForwardingUtilsIfconfig') + def testIpForwardingUtils(self, mock_call): + mocks = mock.Mock() + mocks.attach_mock(mock_call, 'call') + + utils.Utils.IpForwardingUtils(self.mock_setup, self.mock_logger, 66) + expected_calls = [mock.call.call(mock.ANY)] + self.assertEqual(mocks.mock_calls, expected_calls) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/freebsd_11/utils.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/freebsd_11/utils.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/freebsd_11/utils.py 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/freebsd_11/utils.py 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1,52 @@ +#!/usr/bin/python +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Utilities that are distro specific for use on FreeBSD 11.""" + +from google_compute_engine.distro_lib import helpers +from google_compute_engine.distro_lib import ip_forwarding_utils +from google_compute_engine.distro_lib import utils + + +class Utils(utils.Utils): + """Utilities used by Linux guest services on FreeBSD 11.""" + + def EnableNetworkInterfaces( + self, interfaces, logger, dhclient_script=None): + """Enable the list of network interfaces. + + Args: + interfaces: list of string, the output device names to enable. + logger: logger object, used to write to SysLog and serial port. + dhclient_script: string, the path to a dhclient script used by dhclient. + """ + helpers.CallDhclient(interfaces, logger) + + def HandleClockSync(self, logger): + """Sync the software clock with the hypervisor clock. + + Args: + logger: logger object, used to write to SysLog and serial port. + """ + helpers.CallNtpdate(logger) + + def IpForwardingUtils(self, logger, proto_id=None): + """Get system IP address configuration utilities. + + Args: + logger: logger object, used to write to SysLog and serial port. + proto_id: string, the routing protocol identifier for Google IP changes. + """ + return ip_forwarding_utils.IpForwardingUtilsIfconfig(logger) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/helpers.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/helpers.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/helpers.py 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/helpers.py 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1,77 @@ +#!/usr/bin/python +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Distro helpers.""" + +import os +import subprocess + + +def CallDhclient( + interfaces, logger, dhclient_script=None): + """Configure the network interfaces using dhclient. + + Args: + interfaces: list of string, the output device names to enable. + logger: logger object, used to write to SysLog and serial port. + dhclient_script: string, the path to a dhclient script used by dhclient. + """ + logger.info('Enabling the Ethernet interfaces %s.', interfaces) + + dhclient_command = ['dhclient'] + + if dhclient_script and os.path.exists(dhclient_script): + dhclient_command += ['-sf', dhclient_script] + + try: + subprocess.check_call(dhclient_command + ['-x'] + interfaces) + subprocess.check_call(dhclient_command + interfaces) + except subprocess.CalledProcessError: + logger.warning('Could not enable interfaces %s.', interfaces) + + +def CallHwclock(logger): + """Sync clock using hwclock. + + Args: + logger: logger object, used to write to SysLog and serial port. + """ + command = ['/sbin/hwclock', '--hctosys'] + try: + subprocess.check_call(command) + except subprocess.CalledProcessError: + logger.warning('Failed to sync system time with hardware clock.') + else: + logger.info('Synced system time with hardware clock.') + + +def CallNtpdate(logger): + """Sync clock using ntpdate. + + Args: + logger: logger object, used to write to SysLog and serial port. + """ + ntpd_inactive = subprocess.call(['service', 'ntpd', 'status']) + try: + if not ntpd_inactive: + subprocess.check_call(['service', 'ntpd', 'stop']) + subprocess.check_call( + 'ntpdate `awk \'$1=="server" {print $2}\' /etc/ntp.conf`', shell=True) + if not ntpd_inactive: + subprocess.check_call(['service', 'ntpd', 'start']) + except subprocess.CalledProcessError: + logger.warning('Failed to sync system time with ntp server.') + else: + logger.info('Synced system time with ntp server.') diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/ip_forwarding_utils.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/ip_forwarding_utils.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/ip_forwarding_utils.py 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/ip_forwarding_utils.py 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1,302 @@ +#!/usr/bin/python +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Utilities for configuring IP address forwarding.""" + +import re +import subprocess +try: + # The following modules are required by IpForwardingUtilsIfconfig. + import netifaces + import netaddr +except ImportError: + netifaces = None + netaddr = None + + +IP_REGEX = re.compile(r'\A(\d{1,3}\.){3}\d{1,3}\Z') +IP_ALIAS_REGEX = re.compile(r'\A(\d{1,3}\.){3}\d{1,3}/\d{1,2}\Z') + + +class IpForwardingUtilsBase(object): + """System IP address configuration utilities.""" + + def ParseForwardedIps(self, forwarded_ips): + """Parse and validate forwarded IP addresses. + + Args: + forwarded_ips: list, the IP address strings to parse. + + Returns: + list, the valid IP address strings. + """ + pass + + def GetForwardedIps(self, interface, interface_ip=None): + """Retrieve the list of configured forwarded IP addresses. + + Args: + interface: string, the output device to query. + interface_ip: string, current interface ip address. + + Returns: + list, the IP address strings. + """ + pass + + def AddForwardedIp(self, address, interface): + """Configure a new IP address on the network interface. + + Args: + address: string, the IP address to configure. + interface: string, the output device to use. + """ + pass + + def RemoveForwardedIp(self, address, interface): + """Delete an IP address on the network interface. + + Args: + address: string, the IP address to configure. + interface: string, the output device to use. + """ + pass + + +class IpForwardingUtilsIproute(IpForwardingUtilsBase): + """System IP address configuration utilities. + + Command used to add IPs: + ip route add to local $IP/32 dev eth0 proto 66 + Command used to fetch list of configured IPs: + ip route ls table local type local dev eth0 scope host proto 66 + """ + + def __init__(self, logger, proto_id=None): + """Constructor. + + Args: + logger: logger object, used to write to SysLog and serial port. + proto_id: string, the routing protocol identifier for Google IP changes. + """ + self.logger = logger + self.proto_id = proto_id or '66' + + def _CreateRouteOptions(self, **kwargs): + """Create a dictionary of parameters to append to the ip route command. + + Args: + **kwargs: dict, the string parameters to update in the ip route command. + + Returns: + dict, the string parameters to append to the ip route command. + """ + options = { + 'proto': self.proto_id, + 'scope': 'host', + } + options.update(kwargs) + return options + + def _RunIpRoute(self, args=None, options=None): + """Run a command with ip route and return the response. + + Args: + args: list, the string ip route command args to execute. + options: dict, the string parameters to append to the ip route command. + + Returns: + string, the standard output from the ip route command execution. + """ + args = args or [] + options = options or {} + command = ['ip', 'route'] + command.extend(args) + for item in options.items(): + command.extend(item) + try: + process = subprocess.Popen( + command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = process.communicate() + except OSError as e: + self.logger.warning('Exception running %s. %s.', command, str(e)) + else: + if process.returncode: + message = 'Non-zero exit status running %s. %s.' + self.logger.warning(message, command, stderr.strip()) + else: + return stdout.decode('utf-8', 'replace') + return '' + + def ParseForwardedIps(self, forwarded_ips): + """Parse and validate forwarded IP addresses. + + Args: + forwarded_ips: list, the IP address strings to parse. + + Returns: + list, the valid IP address strings. + """ + addresses = [] + forwarded_ips = forwarded_ips or [] + for ip in forwarded_ips: + if ip and (IP_REGEX.match(ip) or IP_ALIAS_REGEX.match(ip)): + addresses.append(ip[:-3] if ip.endswith('/32') else ip) + else: + self.logger.warning('Could not parse IP address: "%s".', ip) + return addresses + + def GetForwardedIps(self, interface, interface_ip=None): + """Retrieve the list of configured forwarded IP addresses. + + Args: + interface: string, the output device to query. + interface_ip: string, current interface ip address. + + Returns: + list, the IP address strings. + """ + args = ['ls', 'table', 'local', 'type', 'local'] + options = self._CreateRouteOptions(dev=interface) + result = self._RunIpRoute(args=args, options=options) + result = re.sub(r'local\s', r'', result) + return self.ParseForwardedIps(result.split()) + + def AddForwardedIp(self, address, interface): + """Configure a new IP address on the network interface. + + Args: + address: string, the IP address to configure. + interface: string, the output device to use. + """ + address = address if IP_ALIAS_REGEX.match(address) else '%s/32' % address + args = ['add', 'to', 'local', address] + options = self._CreateRouteOptions(dev=interface) + self._RunIpRoute(args=args, options=options) + + def RemoveForwardedIp(self, address, interface): + """Delete an IP address on the network interface. + + Args: + address: string, the IP address to configure. + interface: string, the output device to use. + """ + address = address if IP_ALIAS_REGEX.match(address) else '%s/32' % address + args = ['delete', 'to', 'local', address] + options = self._CreateRouteOptions(dev=interface) + self._RunIpRoute(args=args, options=options) + + +class IpForwardingUtilsIfconfig(IpForwardingUtilsBase): + """System IP address configuration utilities.""" + + def __init__(self, logger): + """Constructor. + + Args: + logger: logger object, used to write to SysLog and serial port. + """ + + self.logger = logger + + def _RunIfconfig(self, args=None, options=None): + """Run a command with ifconfig and return the response. + + Args: + args: list, the string ip route command args to execute. + options: dict, the string parameters to append to the ip route command. + + Returns: + string, the standard output from the ip route command execution. + """ + args = args or [] + options = options or {} + command = ['ifconfig'] + command.extend(args) + for item in options.items(): + command.extend(item) + try: + process = subprocess.Popen( + command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = process.communicate() + except OSError as e: + self.logger.warning('Exception running %s. %s.', command, str(e)) + else: + if process.returncode: + message = 'Non-zero exit status running %s. %s.' + self.logger.warning(message, command, stderr.strip()) + else: + return stdout.decode('utf-8', 'replace') + return '' + + def ParseForwardedIps(self, forwarded_ips): + """Parse and validate forwarded IP addresses. + + Args: + forwarded_ips: list, the IP address strings to parse. + + Returns: + list, the valid IP address strings. + """ + addresses = [] + forwarded_ips = forwarded_ips or [] + for ip in forwarded_ips: + if ip and (IP_REGEX.match(ip) or IP_ALIAS_REGEX.match(ip)): + addresses.extend([str(addr) for addr in list(netaddr.IPNetwork(ip))]) + else: + self.logger.warning('Could not parse IP address: "%s".', ip) + return addresses + + def GetForwardedIps(self, interface, interface_ip=None): + """Retrieve the list of configured forwarded IP addresses. + + Args: + interface: string, the output device to query. + interface_ip: string, current interface ip address. + + Returns: + list, the IP address strings. + """ + try: + ips = netifaces.ifaddresses(interface) + ips = ips[netifaces.AF_INET] + except (ValueError, IndexError): + return [] + forwarded_ips = [] + for ip in ips: + if ip['addr'] != interface_ip: + full_addr = '%s/%d' % (ip['addr'], netaddr.IPAddress(ip['netmask']).netmask_bits()) + forwarded_ips.append(full_addr) + return self.ParseForwardedIps(forwarded_ips) + + def AddForwardedIp(self, address, interface): + """Configure a new IP address on the network interface. + + Args: + address: string, the IP address to configure. + interface: string, the output device to use. + """ + for ip in list(netaddr.IPNetwork(address)): + self._RunIfconfig(args=[interface, 'alias', '%s/32' % str(ip)]) + + def RemoveForwardedIp(self, address, interface): + """Delete an IP address on the network interface. + + Args: + address: string, the IP address to configure. + interface: string, the output device to use. + """ + ip = netaddr.IPNetwork(address) + self._RunIfconfig(args=[interface, '-alias', str(ip.ip)]) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/sles_11/tests/utils_test.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/sles_11/tests/utils_test.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/sles_11/tests/utils_test.py 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/sles_11/tests/utils_test.py 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1,91 @@ +#!/usr/bin/python +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Unittest for utils.py module.""" + +import subprocess + +from google_compute_engine.distro_lib.sles_11 import utils +from google_compute_engine.test_compat import mock +from google_compute_engine.test_compat import unittest + + +class UtilsTest(unittest.TestCase): + + def setUp(self): + self.mock_logger = mock.Mock() + self.mock_setup = mock.create_autospec(utils.Utils) + + def testEnableNetworkInterfacesWithSingleNic(self): + mocks = mock.Mock() + + utils.Utils.EnableNetworkInterfaces( + self.mock_setup, ['eth0'], self.mock_logger) + expected_calls = [] + self.assertEqual(mocks.mock_calls, expected_calls) + + def testEnableNetworkInterfacesWithMultipleNics(self): + mocks = mock.Mock() + mocks.attach_mock(self.mock_setup._Dhcpcd, 'dhcpcd') + + utils.Utils.EnableNetworkInterfaces( + self.mock_setup, ['eth0', 'eth1', 'eth2'], self.mock_logger) + expected_calls = [ + mock.call.dhcpcd(['eth1', 'eth2'], mock.ANY), + ] + self.assertEqual(mocks.mock_calls, expected_calls) + + @mock.patch( + 'google_compute_engine.distro_lib.sles_11.utils.subprocess.check_call') + def testDhcpcd(self, mock_call): + mocks = mock.Mock() + mocks.attach_mock(mock_call, 'call') + mocks.attach_mock(self.mock_logger, 'logger') + mock_call.side_effect = [ + None, None, None, None, + subprocess.CalledProcessError(1, 'Test'), + subprocess.CalledProcessError(1, 'Test'), + ] + + utils.Utils._Dhcpcd( + self.mock_setup, ['eth1', 'eth2', 'eth3'], self.mock_logger) + expected_calls = [ + mock.call.call(['/sbin/dhcpcd', '-x', 'eth1']), + mock.call.call(['/sbin/dhcpcd', 'eth1']), + mock.call.call(['/sbin/dhcpcd', '-x', 'eth2']), + mock.call.call(['/sbin/dhcpcd', 'eth2']), + mock.call.call(['/sbin/dhcpcd', '-x', 'eth3']), + mock.call.logger.info(mock.ANY, 'eth3'), + mock.call.call(['/sbin/dhcpcd','eth3']), + mock.call.logger.warning(mock.ANY, 'eth3'), + ] + self.assertEqual(mocks.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.helpers.CallHwclock') + def testHandleClockSync(self, mock_call): + mocks = mock.Mock() + mocks.attach_mock(mock_call, 'call') + + utils.Utils.HandleClockSync(self.mock_setup, self.mock_logger) + expected_calls = [mock.call.call(mock.ANY)] + self.assertEqual(mocks.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.ip_forwarding_utils.IpForwardingUtilsIproute') + def testIpForwardingUtils(self, mock_call): + mocks = mock.Mock() + mocks.attach_mock(mock_call, 'call') + + utils.Utils.IpForwardingUtils(self.mock_setup, self.mock_logger, '66') + expected_calls = [mock.call.call(mock.ANY, '66')] + self.assertEqual(mocks.mock_calls, expected_calls) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/sles_11/utils.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/sles_11/utils.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/sles_11/utils.py 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/sles_11/utils.py 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1,79 @@ +#!/usr/bin/python +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Utilities that are distro specific for use on SUSE 11.""" + +import os +import subprocess + +from google_compute_engine import constants +from google_compute_engine.distro_lib import helpers +from google_compute_engine.distro_lib import ip_forwarding_utils +from google_compute_engine.distro_lib import utils + + +class Utils(utils.Utils): + """Utilities used by Linux guest services on SUSE 11.""" + + def EnableNetworkInterfaces( + self, interfaces, logger, dhclient_script=None): + """Enable the list of network interfaces. + + Args: + interfaces: list of string, the output device names to enable. + logger: logger object, used to write to SysLog and serial port. + dhclient_script: string, the path to a dhclient script used by dhclient. + """ + interfaces_to_up = [i for i in interfaces if i != 'eth0'] + if interfaces_to_up: + logger.info('Enabling the Ethernet interfaces %s.', interfaces_to_up) + self._Dhcpcd(interfaces_to_up, logger) + + def _Dhcpcd(self, interfaces, logger): + """Use dhcpcd to activate the interfaces. + + Args: + interfaces: list of string, the output device names to enable. + logger: logger object, used to write to SysLog and serial port. + """ + for interface in interfaces: + dhcpcd = ['/sbin/dhcpcd'] + try: + subprocess.check_call(dhcpcd + ['-x', interface]) + except subprocess.CalledProcessError: + # Dhcpcd not yet running for this device. + logger.info('Dhcpcd not yet running for interface %s.', interface) + try: + subprocess.check_call(dhcpcd + [interface]) + except subprocess.CalledProcessError: + # The interface is already active. + logger.warning('Could not activate interface %s.', interface) + + def HandleClockSync(self, logger): + """Sync the software clock with the hypervisor clock. + + Args: + logger: logger object, used to write to SysLog and serial port. + """ + helpers.CallHwclock(logger) + + def IpForwardingUtils(self, logger, proto_id=None): + """Get system IP address configuration utilities. + + Args: + logger: logger object, used to write to SysLog and serial port. + proto_id: string, the routing protocol identifier for Google IP changes. + """ + return ip_forwarding_utils.IpForwardingUtilsIproute(logger, proto_id) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/sles_12/tests/utils_test.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/sles_12/tests/utils_test.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/sles_12/tests/utils_test.py 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/sles_12/tests/utils_test.py 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1,111 @@ +#!/usr/bin/python +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Unittest for utils.py module.""" + +import subprocess + +from google_compute_engine.distro_lib.sles_12 import utils +from google_compute_engine.test_compat import builtin +from google_compute_engine.test_compat import mock +from google_compute_engine.test_compat import unittest + + +class UtilsTest(unittest.TestCase): + + def setUp(self): + self.mock_logger = mock.Mock() + self.mock_setup = mock.create_autospec(utils.Utils) + self.mock_setup.network_path = '/etc/sysconfig/network' + + def testEnableNetworkInterfacesWithSingleNic(self): + mocks = mock.Mock() + + utils.Utils.EnableNetworkInterfaces( + self.mock_setup, ['eth0'], self.mock_logger) + expected_calls = [] + self.assertEqual(mocks.mock_calls, expected_calls) + + def testEnableNetworkInterfacesWithMultipleNics(self): + mocks = mock.Mock() + mocks.attach_mock(self.mock_setup._WriteIfcfg, 'writeIfcfg') + mocks.attach_mock(self.mock_setup._Ifup, 'ifup') + + utils.Utils.EnableNetworkInterfaces( + self.mock_setup, ['eth0', 'eth1', 'eth2'], self.mock_logger) + expected_calls = [ + mock.call.writeIfcfg(['eth1', 'eth2'], mock.ANY), + mock.call.ifup(['eth1', 'eth2'], mock.ANY), + ] + self.assertEqual(mocks.mock_calls, expected_calls) + + def testWriteIfcfg(self): + mocks = mock.Mock() + mock_open = mock.mock_open() + mocks.attach_mock(mock_open, 'open') + with mock.patch('%s.open' % builtin, mock_open, create=False): + + utils.Utils._WriteIfcfg( + self.mock_setup, ['eth1', 'eth2'], self.mock_logger) + expected_calls = [ + mock.call.open('/etc/sysconfig/network/ifcfg-eth1', 'w'), + mock.call.open().__enter__(), + mock.call.open().write(mock.ANY), + mock.call.open().__exit__(None, None, None), + mock.call.open('/etc/sysconfig/network/ifcfg-eth2', 'w'), + mock.call.open().__enter__(), + mock.call.open().write(mock.ANY), + mock.call.open().__exit__(None, None, None), + ] + self.assertEqual(mocks.mock_calls, expected_calls) + + @mock.patch( + 'google_compute_engine.distro_lib.sles_12.utils.subprocess.check_call') + def testIfup(self, mock_call): + mocks = mock.Mock() + mocks.attach_mock(mock_call, 'call') + mocks.attach_mock(self.mock_logger, 'logger') + mock_call.side_effect = [ + None, subprocess.CalledProcessError(1, 'Test'), + ] + + utils.Utils._Ifup(self.mock_setup, ['eth1', 'eth2'], self.mock_logger) + utils.Utils._Ifup(self.mock_setup, ['eth1', 'eth2'], self.mock_logger) + expectedIfupCall = [ + '/usr/sbin/wicked', 'ifup', '--timeout', '1', 'eth1', 'eth2', + ] + expected_calls = [ + mock.call.call(expectedIfupCall), + mock.call.call(expectedIfupCall), + mock.call.logger.warning(mock.ANY, ['eth1', 'eth2']), + ] + self.assertEqual(mocks.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.helpers.CallHwclock') + def testHandleClockSync(self, mock_call): + mocks = mock.Mock() + mocks.attach_mock(mock_call, 'call') + + utils.Utils.HandleClockSync(self.mock_setup, self.mock_logger) + expected_calls = [mock.call.call(mock.ANY)] + self.assertEqual(mocks.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.ip_forwarding_utils.IpForwardingUtilsIproute') + def testIpForwardingUtils(self, mock_call): + mocks = mock.Mock() + mocks.attach_mock(mock_call, 'call') + + utils.Utils.IpForwardingUtils(self.mock_setup, self.mock_logger, '66') + expected_calls = [mock.call.call(mock.ANY, '66')] + self.assertEqual(mocks.mock_calls, expected_calls) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/sles_12/utils.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/sles_12/utils.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/sles_12/utils.py 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/sles_12/utils.py 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1,101 @@ +#!/usr/bin/python +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Utilities that are distro specific for use on SUSE 12.""" + +import os +import subprocess + +from google_compute_engine import constants +from google_compute_engine.distro_lib import helpers +from google_compute_engine.distro_lib import ip_forwarding_utils +from google_compute_engine.distro_lib import utils + + +class Utils(utils.Utils): + """Utilities used by Linux guest services on SUSE 12.""" + + network_path = constants.LOCALBASE + '/etc/sysconfig/network' + + def EnableNetworkInterfaces( + self, interfaces, logger, dhclient_script=None): + """Enable the list of network interfaces. + + Args: + interfaces: list of string, the output device names to enable. + logger: logger object, used to write to SysLog and serial port. + dhclient_script: string, the path to a dhclient script used by dhclient. + """ + interfaces_to_up = [i for i in interfaces if i != 'eth0'] + if interfaces_to_up: + logger.info('Enabling the Ethernet interfaces %s.', interfaces_to_up) + self._WriteIfcfg(interfaces_to_up, logger) + self._Ifup(interfaces_to_up, logger) + + def _WriteIfcfg(self, interfaces, logger): + """Write ifcfg files for multi-NIC support. + + Overwrites the files. This allows us to update ifcfg-* in the future. + Disable the network setup to override this behavior and customize the + configurations. + + Args: + interfaces: list of string, the output device names to enable. + logger: logger object, used to write to SysLog and serial port. + """ + for interface in interfaces: + interface_config = os.path.join( + self.network_path, 'ifcfg-%s' % interface) + interface_content = [ + '# Added by Google.', + 'STARTMODE=hotplug', + 'BOOTPROTO=dhcp', + 'DHCLIENT_SET_DEFAULT_ROUTE=yes', + 'DHCLIENT_ROUTE_PRIORITY=10%s00' % interface, + '', + ] + with open(interface_config, 'w') as interface_file: + interface_file.write('\n'.join(interface_content)) + logger.info('Created ifcfg file for interface %s.', interface) + + def _Ifup(self, interfaces, logger): + """Activate network interfaces. + + Args: + interfaces: list of string, the output device names to enable. + logger: logger object, used to write to SysLog and serial port. + """ + ifup = ['/usr/sbin/wicked', 'ifup', '--timeout', '1'] + try: + subprocess.check_call(ifup + interfaces) + except subprocess.CalledProcessError: + logger.warning('Could not activate interfaces %s.', interfaces) + + def HandleClockSync(self, logger): + """Sync the software clock with the hypervisor clock. + + Args: + logger: logger object, used to write to SysLog and serial port. + """ + helpers.CallHwclock(logger) + + def IpForwardingUtils(self, logger, proto_id=None): + """Get system IP address configuration utilities. + + Args: + logger: logger object, used to write to SysLog and serial port. + proto_id: string, the routing protocol identifier for Google IP changes. + """ + return ip_forwarding_utils.IpForwardingUtilsIproute(logger, proto_id) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/tests/helpers_test.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/tests/helpers_test.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/tests/helpers_test.py 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/tests/helpers_test.py 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1,137 @@ +#!/usr/bin/python +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unittest for helpers.py module.""" + +import subprocess + +from google_compute_engine.distro_lib import helpers +from google_compute_engine.test_compat import mock +from google_compute_engine.test_compat import unittest + + +class HelpersTest(unittest.TestCase): + + def setUp(self): + self.mock_logger = mock.Mock() + + @mock.patch('google_compute_engine.distro_lib.helpers.os.path.exists') + @mock.patch('google_compute_engine.distro_lib.helpers.subprocess.check_call') + def testCallDhclient(self, mock_call, mock_exists): + mocks = mock.Mock() + mocks.attach_mock(mock_exists, 'exists') + mocks.attach_mock(mock_call, 'call') + mocks.attach_mock(self.mock_logger, 'logger') + + mock_exists.side_effect = [False, True] + mock_call.side_effect = [ + None, None, None, None, None, None, + subprocess.CalledProcessError(1, 'Test'), + ] + + helpers.CallDhclient(['a', 'b'], self.mock_logger, 'test_script') + helpers.CallDhclient(['c', 'd'], self.mock_logger, 'test_script') + helpers.CallDhclient(['e', 'f'], self.mock_logger, None) + helpers.CallDhclient(['g', 'h'], self.mock_logger, None) + + expected_calls = [ + mock.call.logger.info(mock.ANY, ['a', 'b']), + mock.call.exists('test_script'), + mock.call.call(['dhclient', '-x', 'a', 'b']), + mock.call.call(['dhclient', 'a', 'b']), + mock.call.logger.info(mock.ANY, ['c', 'd']), + mock.call.exists('test_script'), + mock.call.call(['dhclient', '-sf', 'test_script', '-x', 'c', 'd']), + mock.call.call(['dhclient', '-sf', 'test_script', 'c', 'd']), + mock.call.logger.info(mock.ANY, ['e', 'f']), + mock.call.call(['dhclient', '-x', 'e', 'f']), + mock.call.call(['dhclient', 'e', 'f']), + mock.call.logger.info(mock.ANY, ['g', 'h']), + mock.call.call(['dhclient', '-x', 'g', 'h']), + mock.call.logger.warning(mock.ANY, ['g', 'h']), + ] + + self.assertEqual(mocks.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.helpers.subprocess.check_call') + def testCallHwclock(self, mock_call): + command = ['/sbin/hwclock', '--hctosys'] + mock_logger = mock.Mock() + + helpers.CallHwclock(mock_logger) + mock_call.assert_called_once_with(command) + expected_calls = [mock.call.info(mock.ANY)] + self.assertEqual(mock_logger.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.helpers.subprocess.check_call') + def testCallHwclockError(self, mock_call): + command = ['/sbin/hwclock', '--hctosys'] + mock_logger = mock.Mock() + mock_call.side_effect = subprocess.CalledProcessError(1, 'Test') + + helpers.CallHwclock(mock_logger) + mock_call.assert_called_once_with(command) + expected_calls = [mock.call.warning(mock.ANY)] + self.assertEqual(mock_logger.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.helpers.subprocess.check_call') + @mock.patch('google_compute_engine.distro_lib.helpers.subprocess.call') + def testCallNtpdateActive(self, mock_call, mock_check_call): + command_status = ['service', 'ntpd', 'status'] + command_stop = ['service', 'ntpd', 'stop'] + command_start = ['service', 'ntpd', 'start'] + command_ntpdate = 'ntpdate `awk \'$1=="server" {print $2}\' /etc/ntp.conf`' + mock_logger = mock.Mock() + mock_call.return_value = 0 + mock_check_call.return_value = True + + helpers.CallNtpdate(mock_logger) + mock_call.assert_called_once_with(command_status) + expected_calls = [ + mock.call(command_stop), + mock.call(command_ntpdate, shell=True), + mock.call(command_start), + ] + self.assertEqual(mock_check_call.mock_calls, expected_calls) + expected_calls = [mock.call.info(mock.ANY)] + self.assertEqual(mock_logger.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.helpers.subprocess.check_call') + @mock.patch('google_compute_engine.distro_lib.helpers.subprocess.call') + def testCallNtpdateInactive(self, mock_call, mock_check_call): + command_status = ['service', 'ntpd', 'status'] + command_ntpdate = 'ntpdate `awk \'$1=="server" {print $2}\' /etc/ntp.conf`' + mock_logger = mock.Mock() + mock_call.return_value = 1 + + helpers.CallNtpdate(mock_logger) + mock_call.assert_called_once_with(command_status) + mock_check_call.assert_called_once_with(command_ntpdate, shell=True) + expected_calls = [mock.call.info(mock.ANY)] + self.assertEqual(mock_logger.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.helpers.subprocess.check_call') + @mock.patch('google_compute_engine.distro_lib.helpers.subprocess.call') + def testCallNtpdateError(self, mock_call, mock_check_call): + command_status = ['service', 'ntpd', 'status'] + command_ntpdate = 'ntpdate `awk \'$1=="server" {print $2}\' /etc/ntp.conf`' + mock_logger = mock.Mock() + mock_check_call.side_effect = subprocess.CalledProcessError(1, 'Test') + + helpers.CallNtpdate(mock_logger) + mock_call.assert_called_once_with(command_status) + mock_check_call.assert_called_once_with(command_ntpdate, shell=True) + expected_calls = [mock.call.warning(mock.ANY)] + self.assertEqual(mock_logger.mock_calls, expected_calls) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/tests/ip_forwarding_utils_test.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/tests/ip_forwarding_utils_test.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/tests/ip_forwarding_utils_test.py 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/tests/ip_forwarding_utils_test.py 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1,435 @@ +#!/usr/bin/python +# Copyright 2016 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unittest for ip_forwarding_utils.py module.""" + +from google_compute_engine.distro_lib import ip_forwarding_utils +from google_compute_engine.test_compat import mock +from google_compute_engine.test_compat import unittest + + +def _CreateMockProcess(returncode, stdout, stderr): + mock_process = mock.Mock() + mock_process.returncode = returncode + mock_process.communicate.return_value = (stdout, stderr) + return mock_process + + +class IpForwardingUtilsIprouteTest(unittest.TestCase): + + def setUp(self): + self.mock_logger = mock.Mock() + self.options = {'hello': 'world'} + with mock.patch( + 'google_compute_engine.distro_lib.ip_forwarding_utils' + '.subprocess') as mock_subprocess: + mock_subprocess.Popen.return_value = _CreateMockProcess( + 0, b'out', b'') + self.mock_utils = ip_forwarding_utils.IpForwardingUtilsIproute( + self.mock_logger) + self.mock_utils.proto_id = 'proto' + + def testCreateRouteOptions(self): + # Default options. + expected_options = { + 'proto': 'proto', + 'scope': 'host', + } + self.assertEqual(self.mock_utils._CreateRouteOptions(), expected_options) + + # Update dictionary when arguments are specified. + expected_options = { + 'proto': 'proto', + 'scope': 'host', + 'num': 1, + 'string': 'hello world', + } + self.assertEqual( + self.mock_utils._CreateRouteOptions(num=1, string='hello world'), + expected_options) + + # Update the default options. + expected_options = { + 'proto': 'test 1', + 'scope': 'test 2', + } + self.assertEqual( + self.mock_utils._CreateRouteOptions(proto='test 1', scope='test 2'), + expected_options) + + @mock.patch('google_compute_engine.distro_lib.ip_forwarding_utils.subprocess') + def testRunIpRoute(self, mock_subprocess): + mock_process = _CreateMockProcess(0, b'out', b'') + mock_subprocess.Popen.return_value = mock_process + args = ['foo', 'bar'] + options = {'one': 'two'} + + self.assertEqual( + self.mock_utils._RunIpRoute(args=args, options=options), 'out') + command = ['ip', 'route', 'foo', 'bar', 'one', 'two'] + mock_subprocess.Popen.assert_called_once_with( + command, stdout=mock_subprocess.PIPE, stderr=mock_subprocess.PIPE) + mock_process.communicate.assert_called_once_with() + self.mock_logger.warning.assert_not_called() + + @mock.patch('google_compute_engine.distro_lib.ip_forwarding_utils.subprocess') + def testRunIpRouteReturnCode(self, mock_subprocess): + mock_process = _CreateMockProcess(1, b'out', b'error\n') + mock_subprocess.Popen.return_value = mock_process + + self.assertEqual( + self.mock_utils._RunIpRoute(args=['foo', 'bar'], options=self.options), + '') + command = ['ip', 'route', 'foo', 'bar', 'hello', 'world'] + self.mock_logger.warning.assert_called_once_with( + mock.ANY, command, b'error') + + @mock.patch('google_compute_engine.distro_lib.ip_forwarding_utils.subprocess') + def testRunIpRouteException(self, mock_subprocess): + mock_subprocess.Popen.side_effect = OSError('Test Error') + + self.assertEqual( + self.mock_utils._RunIpRoute(args=['foo', 'bar'], options=self.options), + '') + command = ['ip', 'route', 'foo', 'bar', 'hello', 'world'] + self.mock_logger.warning.assert_called_once_with( + mock.ANY, command, 'Test Error') + + def testParseForwardedIps(self): + self.assertEqual(self.mock_utils.ParseForwardedIps(None), []) + self.assertEqual(self.mock_utils.ParseForwardedIps([]), []) + self.assertEqual(self.mock_utils.ParseForwardedIps([None]), []) + self.assertEqual(self.mock_utils.ParseForwardedIps(['invalid']), []) + self.assertEqual(self.mock_utils.ParseForwardedIps(['1a1a1a1']), []) + self.assertEqual(self.mock_utils.ParseForwardedIps(['1.1.1.1.1']), []) + self.assertEqual(self.mock_utils.ParseForwardedIps(['1111.1.1.1']), []) + self.assertEqual(self.mock_utils.ParseForwardedIps(['1.1.1.1111']), []) + expected_calls = [ + mock.call.warning(mock.ANY, None), + mock.call.warning(mock.ANY, 'invalid'), + mock.call.warning(mock.ANY, '1a1a1a1'), + mock.call.warning(mock.ANY, '1.1.1.1.1'), + mock.call.warning(mock.ANY, '1111.1.1.1'), + mock.call.warning(mock.ANY, '1.1.1.1111'), + ] + self.assertEqual(self.mock_logger.mock_calls, expected_calls) + + def testParseForwardedIpsComplex(self): + forwarded_ips = { + '{{}}\n\"hello\"\n!@#$%^&*()\n\n': False, + '1111.1.1.1': False, + '1.1.1.1': True, + 'hello': False, + '123.123.123.123': True, + '1.1.1.': False, + '1.1.1.a': False, + None: False, + '1.0.0.0': True, + '1.1.1.1/1': True, + '1.1.1.1/11': True, + '123.123.123.123/1': True, + '123.123.123.123/123': False, + '123.123.123.123/a': False, + '123.123.123.123/': False, + } + input_ips = forwarded_ips.keys() + valid_ips = [ip for ip, valid in forwarded_ips.items() if valid] + invalid_ips = [ip for ip, valid in forwarded_ips.items() if not valid] + + self.assertEqual(self.mock_utils.ParseForwardedIps(input_ips), valid_ips) + expected_calls = [mock.call.warning(mock.ANY, ip) for ip in invalid_ips] + self.assertEqual(self.mock_logger.mock_calls, expected_calls) + + def testParseForwardedIpsSubnet(self): + forwarded_ips = { + '1.1.1.1': '1.1.1.1', + '1.1.1.1/32': '1.1.1.1', + '1.1.1.1/1': '1.1.1.1/1', + '1.1.1.1/10': '1.1.1.1/10', + '1.1.1.1/24': '1.1.1.1/24', + } + for ip, value in forwarded_ips.items(): + self.assertEqual(self.mock_utils.ParseForwardedIps([ip]), [value]) + + def testGetForwardedIps(self): + mock_options = mock.Mock() + mock_options.return_value = self.options + mock_run = mock.Mock() + mock_run.return_value = 'a\n b \nlocal c' + mock_parse = mock.Mock() + mock_parse.return_value = ['Test'] + self.mock_utils._CreateRouteOptions = mock_options + self.mock_utils._RunIpRoute = mock_run + self.mock_utils.ParseForwardedIps = mock_parse + + self.assertEqual( + self.mock_utils.GetForwardedIps('interface', 'ip'), ['Test']) + mock_options.assert_called_once_with(dev='interface') + mock_run.assert_called_once_with( + args=['ls', 'table', 'local', 'type', 'local'], options=self.options) + mock_parse.assert_called_once_with(['a', 'b', 'c']) + + def testAddForwardedIp(self): + mock_options = mock.Mock() + mock_options.return_value = self.options + mock_run = mock.Mock() + self.mock_utils._CreateRouteOptions = mock_options + self.mock_utils._RunIpRoute = mock_run + + self.mock_utils.AddForwardedIp('1.1.1.1', 'interface') + mock_options.assert_called_once_with(dev='interface') + mock_run.assert_called_once_with( + args=['add', 'to', 'local', '1.1.1.1/32'], options=self.options) + + def testAddIpAlias(self): + mock_options = mock.Mock() + mock_options.return_value = self.options + mock_run = mock.Mock() + self.mock_utils._CreateRouteOptions = mock_options + self.mock_utils._RunIpRoute = mock_run + + self.mock_utils.AddForwardedIp('1.1.1.1/24', 'interface') + mock_options.assert_called_once_with(dev='interface') + mock_run.assert_called_once_with( + args=['add', 'to', 'local', '1.1.1.1/24'], options=self.options) + + def testRemoveForwardedIp(self): + mock_options = mock.Mock() + mock_options.return_value = self.options + mock_run = mock.Mock() + self.mock_utils._CreateRouteOptions = mock_options + self.mock_utils._RunIpRoute = mock_run + + self.mock_utils.RemoveForwardedIp('1.1.1.1', 'interface') + mock_options.assert_called_once_with(dev='interface') + mock_run.assert_called_once_with( + args=['delete', 'to', 'local', '1.1.1.1/32'], options=self.options) + + def testRemoveAliasIp(self): + mock_options = mock.Mock() + mock_options.return_value = self.options + mock_run = mock.Mock() + self.mock_utils._CreateRouteOptions = mock_options + self.mock_utils._RunIpRoute = mock_run + + self.mock_utils.RemoveForwardedIp('1.1.1.1/24', 'interface') + mock_options.assert_called_once_with(dev='interface') + mock_run.assert_called_once_with( + args=['delete', 'to', 'local', '1.1.1.1/24'], options=self.options) + + +class IpForwardingUtilsIfconfigTest(unittest.TestCase): + + def setUp(self): + self.mock_logger = mock.Mock() + with mock.patch( + 'google_compute_engine.distro_lib.ip_forwarding_utils' + '.subprocess') as mock_subprocess: + mock_subprocess.Popen.return_value = _CreateMockProcess( + 0, b'out', b'') + self.mock_utils = ip_forwarding_utils.IpForwardingUtilsIfconfig( + self.mock_logger) + + @mock.patch('google_compute_engine.distro_lib.ip_forwarding_utils.subprocess') + def testRunIfconfig(self, mock_subprocess): + mock_process = _CreateMockProcess(0, b'out', b'') + mock_subprocess.Popen.return_value = mock_process + args = ['foo', 'bar'] + options = {'one': 'two'} + + self.assertEqual( + self.mock_utils._RunIfconfig(args=args, options=options), 'out') + command = ['ifconfig', 'foo', 'bar', 'one', 'two'] + mock_subprocess.Popen.assert_called_once_with( + command, stdout=mock_subprocess.PIPE, stderr=mock_subprocess.PIPE) + mock_process.communicate.assert_called_once_with() + self.mock_logger.warning.assert_not_called() + + @mock.patch('google_compute_engine.distro_lib.ip_forwarding_utils.subprocess') + def testRunIfconfigReturnCode(self, mock_subprocess): + mock_process = _CreateMockProcess(1, b'out', b'error\n') + mock_subprocess.Popen.return_value = mock_process + + self.assertEqual( + self.mock_utils._RunIfconfig(args=['foo', 'bar']), + '') + command = ['ifconfig', 'foo', 'bar'] + self.mock_logger.warning.assert_called_once_with( + mock.ANY, command, b'error') + + @mock.patch('google_compute_engine.distro_lib.ip_forwarding_utils.subprocess') + def testRunIfconfigException(self, mock_subprocess): + mock_subprocess.Popen.side_effect = OSError('Test Error') + + self.assertEqual( + self.mock_utils._RunIfconfig(args=['foo', 'bar']), + '') + command = ['ifconfig', 'foo', 'bar'] + self.mock_logger.warning.assert_called_once_with( + mock.ANY, command, 'Test Error') + + @mock.patch('google_compute_engine.distro_lib.ip_forwarding_utils.netaddr') + def testParseForwardedIps(self, mock_netaddr): + self.assertEqual(self.mock_utils.ParseForwardedIps(None), []) + self.assertEqual(self.mock_utils.ParseForwardedIps([]), []) + self.assertEqual(self.mock_utils.ParseForwardedIps([None]), []) + self.assertEqual(self.mock_utils.ParseForwardedIps(['invalid']), []) + self.assertEqual(self.mock_utils.ParseForwardedIps(['1a1a1a1']), []) + self.assertEqual(self.mock_utils.ParseForwardedIps(['1.1.1.1.1']), []) + self.assertEqual(self.mock_utils.ParseForwardedIps(['1111.1.1.1']), []) + self.assertEqual(self.mock_utils.ParseForwardedIps(['1.1.1.1111']), []) + expected_calls = [ + mock.call.warning(mock.ANY, None), + mock.call.warning(mock.ANY, 'invalid'), + mock.call.warning(mock.ANY, '1a1a1a1'), + mock.call.warning(mock.ANY, '1.1.1.1.1'), + mock.call.warning(mock.ANY, '1111.1.1.1'), + mock.call.warning(mock.ANY, '1.1.1.1111'), + ] + self.assertEqual(self.mock_logger.mock_calls, expected_calls) + self.assertEqual(mock_netaddr.IPNetwork.mock_calls, []) + + @mock.patch('google_compute_engine.distro_lib.ip_forwarding_utils.netaddr') + def testParseForwardedIpsComplex(self, mock_netaddr): + def side_effect(arg): + return [arg] + mock_netaddr.IPNetwork.side_effect = side_effect + forwarded_ips = { + '{{}}\n\"hello\"\n!@#$%^&*()\n\n': False, + '1111.1.1.1': False, + '1.1.1.1': True, + 'hello': False, + '123.123.123.123': True, + '1.1.1.': False, + '1.1.1.a': False, + None: False, + '1.0.0.0': True, + '1.1.1.1/1': True, + '1.1.1.1/11': True, + '123.123.123.123/1': True, + '123.123.123.123/123': False, + '123.123.123.123/a': False, + '123.123.123.123/': False, + } + input_ips = forwarded_ips.keys() + valid_ips = [ip for ip, valid in forwarded_ips.items() if valid] + invalid_ips = [ip for ip, valid in forwarded_ips.items() if not valid] + + self.assertEqual(self.mock_utils.ParseForwardedIps(input_ips), valid_ips) + expected_calls = [mock.call.warning(mock.ANY, ip) for ip in invalid_ips] + self.assertEqual(self.mock_logger.mock_calls, expected_calls) + expected_calls = [mock.call.IPNetwork(ip) for ip in valid_ips] + self.assertEqual(mock_netaddr.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.ip_forwarding_utils.netaddr') + def testParseForwardedIpsSubnet(self, mock_netaddr): + mock_netaddr.IPNetwork.return_value = ['1.1.1.1'] + forwarded_ips = [ + '1.1.1.1', + '1.1.1.1/32', + '1.1.1.1/1', + '1.1.1.1/10', + '1.1.1.1/24' + ] + for ip in forwarded_ips: + self.assertEqual(self.mock_utils.ParseForwardedIps([ip]), ['1.1.1.1']) + expected_calls = [mock.call.IPNetwork(ip) for ip in forwarded_ips] + self.assertEqual(mock_netaddr.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.ip_forwarding_utils.netifaces') + @mock.patch('google_compute_engine.distro_lib.ip_forwarding_utils.netaddr') + def testGetForwardedIps(self, mock_netaddr, mock_netifaces): + mock_netifaces.AF_INET = 0 + mock_netifaces.ifaddresses.return_value = [[ + {'addr': 'a', 'netmask': 'a mask'}, + {'addr': 'b', 'netmask': 'b mask'}, + {'addr': 'c', 'netmask': 'c mask'}, + ]] + mock_netaddr.IPAddress().netmask_bits.return_value = 32 + mock_parse = mock.Mock() + mock_parse.return_value = ['Test'] + self.mock_utils.ParseForwardedIps = mock_parse + + self.assertEqual( + self.mock_utils.GetForwardedIps('interface', 'ip'), ['Test']) + mock_netifaces.ifaddresses.assert_called_once_with('interface') + mock_parse.assert_called_once_with(['a/32', 'b/32', 'c/32']) + + @mock.patch('google_compute_engine.distro_lib.ip_forwarding_utils.netifaces') + def testGetForwardedIpsEmpty(self, mock_netifaces): + mock_netifaces.AF_INET = 0 + mock_netifaces.ifaddresses.return_value = [] + + self.assertEqual( + self.mock_utils.GetForwardedIps('interface', 'ip'), []) + + @mock.patch('google_compute_engine.distro_lib.ip_forwarding_utils.netaddr') + def testAddForwardedIp(self, mock_netaddr): + mock_netaddr.IPNetwork.return_value = ['1.1.1.1'] + mock_run = mock.Mock() + self.mock_utils._RunIfconfig = mock_run + + self.mock_utils.AddForwardedIp('1.1.1.1', 'interface') + mock_netaddr.IPNetwork.assert_called_once_with('1.1.1.1') + mock_run.assert_called_once_with( + args=['interface', 'alias', '1.1.1.1/32']) + + @mock.patch('google_compute_engine.distro_lib.ip_forwarding_utils.netaddr') + def testAddIpAlias(self, mock_netaddr): + mock_netaddr.IPNetwork.return_value = [ + '1.1.1.0', '1.1.1.1', '1.1.1.2', '1.1.1.3' + ] + mock_run = mock.Mock() + self.mock_utils._RunIfconfig = mock_run + + self.mock_utils.AddForwardedIp('1.1.1.1/30', 'interface') + mock_netaddr.IPNetwork.assert_called_once_with('1.1.1.1/30') + expected_calls = [ + mock.call(args=['interface', 'alias', '1.1.1.0/32']), + mock.call(args=['interface', 'alias', '1.1.1.1/32']), + mock.call(args=['interface', 'alias', '1.1.1.2/32']), + mock.call(args=['interface', 'alias', '1.1.1.3/32']) + ] + self.assertEqual(mock_run.mock_calls, expected_calls) + + @mock.patch('google_compute_engine.distro_lib.ip_forwarding_utils.netaddr') + def testRemoveForwardedIp(self, mock_netaddr): + mock_ip = mock.Mock() + mock_ip.ip = '1.1.1.1' + mock_netaddr.IPNetwork.return_value = mock_ip + mock_run = mock.Mock() + self.mock_utils._RunIfconfig = mock_run + + self.mock_utils.RemoveForwardedIp('1.1.1.1', 'interface') + mock_netaddr.IPNetwork.assert_called_once_with('1.1.1.1') + mock_run.assert_called_once_with( + args=['interface', '-alias', '1.1.1.1']) + + @mock.patch('google_compute_engine.distro_lib.ip_forwarding_utils.netaddr') + def testRemoveAliasIp(self, mock_netaddr): + mock_ip = mock.Mock() + mock_ip.ip = '1.1.1.1' + mock_netaddr.IPNetwork.return_value = mock_ip + mock_run = mock.Mock() + self.mock_utils._RunIfconfig = mock_run + + self.mock_utils.RemoveForwardedIp('1.1.1.1/24', 'interface') + mock_netaddr.IPNetwork.assert_called_once_with('1.1.1.1/24') + mock_run.assert_called_once_with( + args=['interface', '-alias', '1.1.1.1']) + + +if __name__ == '__main__': + unittest.main() diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/utils.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/utils.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/distro_lib/utils.py 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/distro_lib/utils.py 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1,56 @@ +#!/usr/bin/python +# Copyright 2018 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Utilities that are distro specific.""" + + +class Utils(object): + """Utilities used by Linux guest services.""" + + def __init__(self, debug=False): + """Constructor. + + Args: + debug: bool, True if debug output should write to the console. + """ + self.debug = debug + + def EnableNetworkInterfaces( + self, interfaces, logger, dhclient_script=None): + """Enable the list of network interfaces. + + Args: + interfaces: list of string, the output device names to enable. + logger: logger object, used to write to SysLog and serial port. + dhclient_script: string, the path to a dhclient script used by dhclient. + """ + pass + + def HandleClockSync(self, logger): + """Sync the software clock with the hypervisor clock. + + Args: + logger: logger object, used to write to SysLog and serial port. + """ + pass + + def IpForwardingUtils(self, logger, proto_id=None): + """Get system IP address configuration utilities. + + Args: + logger: logger object, used to write to SysLog and serial port. + proto_id: string, the routing protocol identifier for Google IP changes. + """ + pass diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/instance_setup/instance_config.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/instance_setup/instance_config.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/instance_setup/instance_config.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/instance_setup/instance_config.py 2018-09-05 21:51:39.000000000 +0000 @@ -89,6 +89,7 @@ 'run_dir': '', 'startup': 'true', 'shutdown': 'true', + 'default_shell': '/bin/bash', }, 'NetworkInterfaces': { 'setup': 'true', diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/instance_setup/instance_setup.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/instance_setup/instance_setup.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/instance_setup/instance_setup.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/instance_setup/instance_setup.py 2018-09-05 21:51:39.000000000 +0000 @@ -152,8 +152,7 @@ subprocess.call(['service', 'ssh', 'start']) subprocess.call(['service', 'ssh', 'reload']) elif (os.path.exists('/etc/init.d/sshd') or - os.path.exists('/etc/init/sshd.conf') or - os.path.exists('/etc/rc.d/sshd')): + os.path.exists('/etc/init/sshd.conf')): subprocess.call(['service', 'sshd', 'start']) subprocess.call(['service', 'sshd', 'reload']) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/instance_setup/tests/instance_setup_test.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/instance_setup/tests/instance_setup_test.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/instance_setup/tests/instance_setup_test.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/instance_setup/tests/instance_setup_test.py 2018-09-05 21:51:39.000000000 +0000 @@ -299,27 +299,6 @@ @mock.patch('google_compute_engine.instance_setup.instance_setup.subprocess.call') @mock.patch('google_compute_engine.instance_setup.instance_setup.os.path.exists') - def testStartSshdRcFreebsd(self, mock_exists, mock_call): - mocks = mock.Mock() - mocks.attach_mock(mock_exists, 'exists') - mocks.attach_mock(mock_call, 'call') - mock_exists.side_effect = [False, False, False, False, False, True] - - instance_setup.InstanceSetup._StartSshd(self.mock_setup) - expected_calls = [ - mock.call.exists('/bin/systemctl'), - mock.call.exists('/etc/init.d/ssh'), - mock.call.exists('/etc/init/ssh.conf'), - mock.call.exists('/etc/init.d/sshd'), - mock.call.exists('/etc/init/sshd.conf'), - mock.call.exists('/etc/rc.d/sshd'), - mock.call.call(['service', 'sshd', 'start']), - mock.call.call(['service', 'sshd', 'reload']), - ] - self.assertEqual(mocks.mock_calls, expected_calls) - - @mock.patch('google_compute_engine.instance_setup.instance_setup.subprocess.call') - @mock.patch('google_compute_engine.instance_setup.instance_setup.os.path.exists') def testStartSshdSystemd(self, mock_exists, mock_call): mocks = mock.Mock() mocks.attach_mock(mock_exists, 'exists') diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/metadata_scripts/script_executor.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/metadata_scripts/script_executor.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/metadata_scripts/script_executor.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/metadata_scripts/script_executor.py 2018-09-05 21:51:39.000000000 +0000 @@ -25,15 +25,17 @@ class ScriptExecutor(object): """A class for executing user provided metadata scripts.""" - def __init__(self, logger, script_type): + def __init__(self, logger, script_type, default_shell=None): """Constructor. Args: logger: logger object, used to write to SysLog and serial port. script_type: string, the type of the script we are running. + default_shell: string, the default shell to execute the script. """ self.logger = logger self.script_type = script_type + self.default_shell = default_shell or '/bin/bash' def _MakeExecutable(self, metadata_script): """Add executable permissions to a file. @@ -53,7 +55,7 @@ """ process = subprocess.Popen( metadata_script, shell=True, - executable=constants.LOCALBASE + '/bin/bash', + executable=self.default_shell, stderr=subprocess.STDOUT, stdout=subprocess.PIPE) while True: for line in iter(process.stdout.readline, b''): diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/metadata_scripts/script_manager.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/metadata_scripts/script_manager.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/metadata_scripts/script_manager.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/metadata_scripts/script_manager.py 2018-09-05 21:51:39.000000000 +0000 @@ -48,20 +48,24 @@ class ScriptManager(object): """A class for retrieving and executing metadata scripts.""" - def __init__(self, script_type, run_dir=None, debug=False): + def __init__( + self, script_type, default_shell=None, run_dir=None, debug=False): """Constructor. Args: script_type: string, the metadata script type to run. + default_shell: string, the default shell to execute the script. run_dir: string, the base directory location of the temporary directory. debug: bool, True if debug output should write to the console. """ self.script_type = script_type + self.default_shell = default_shell name = '%s-script' % self.script_type facility = logging.handlers.SysLogHandler.LOG_DAEMON self.logger = logger.Logger(name=name, debug=debug, facility=facility) self.retriever = script_retriever.ScriptRetriever(self.logger, script_type) - self.executor = script_executor.ScriptExecutor(self.logger, script_type) + self.executor = script_executor.ScriptExecutor( + self.logger, script_type, default_shell=default_shell) self._RunScripts(run_dir=run_dir) def _RunScripts(self, run_dir=None): @@ -99,6 +103,8 @@ if instance_config.GetOptionBool('MetadataScripts', script_type): ScriptManager( script_type, + default_shell=instance_config.GetOptionString( + 'MetadataScripts', 'default_shell'), run_dir=instance_config.GetOptionString('MetadataScripts', 'run_dir'), debug=bool(options.debug)) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/metadata_scripts/tests/script_manager_test.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/metadata_scripts/tests/script_manager_test.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/metadata_scripts/tests/script_manager_test.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/metadata_scripts/tests/script_manager_test.py 2018-09-05 21:51:39.000000000 +0000 @@ -54,7 +54,8 @@ mock.call.logger.Logger( name=script_name, debug=False, facility=mock.ANY), mock.call.retriever.ScriptRetriever(mock_logger_instance, script_type), - mock.call.executor.ScriptExecutor(mock_logger_instance, script_type), + mock.call.executor.ScriptExecutor( + mock_logger_instance, script_type, default_shell=None), mock.call.mkdir(prefix=script_prefix, dir=run_dir), mock.call.logger.Logger().info(mock.ANY, script_type), mock.call.retriever.ScriptRetriever().GetScripts(test_dir), diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/networking/ip_forwarding/ip_forwarding.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/networking/ip_forwarding/ip_forwarding.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/networking/ip_forwarding/ip_forwarding.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/networking/ip_forwarding/ip_forwarding.py 2018-09-05 21:51:39.000000000 +0000 @@ -17,12 +17,7 @@ When given a list of public endpoint IPs, compare it with the IPs configured for the associated interfaces, and add or remove addresses from the interfaces -to make them match. Only remove those which match our proto code. - -Command used to add IPs: - ip route add to local $IP/32 dev eth0 proto 66 -Command used to fetch list of configured IPs: - ip route ls table local type local dev eth0 scope host proto 66 +to make them match. """ import logging.handlers @@ -85,15 +80,17 @@ for address in forwarded_ips: self.ip_forwarding_utils.RemoveForwardedIp(address, interface) - def HandleForwardedIps(self, interface, forwarded_ips): + def HandleForwardedIps(self, interface, forwarded_ips, interface_ip=None): """Handle changes to the forwarded IPs on a network interface. Args: interface: string, the output device to configure. forwarded_ips: list, the forwarded IP address strings desired. + interface_ip: string, current interface ip address. """ desired = self.ip_forwarding_utils.ParseForwardedIps(forwarded_ips) - configured = self.ip_forwarding_utils.GetForwardedIps(interface) + configured = self.ip_forwarding_utils.GetForwardedIps( + interface, interface_ip) to_add = sorted(set(desired) - set(configured)) to_remove = sorted(set(configured) - set(desired)) self._LogForwardedIpChanges( diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/networking/ip_forwarding/ip_forwarding_utils.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/networking/ip_forwarding/ip_forwarding_utils.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/networking/ip_forwarding/ip_forwarding_utils.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/networking/ip_forwarding/ip_forwarding_utils.py 2018-09-05 21:51:39.000000000 +0000 @@ -15,124 +15,11 @@ """Utilities for configuring IP address forwarding.""" -import re -import subprocess - -IP_REGEX = re.compile(r'\A(\d{1,3}\.){3}\d{1,3}\Z') -IP_ALIAS_REGEX = re.compile(r'\A(\d{1,3}\.){3}\d{1,3}/\d{1,2}\Z') +from google_compute_engine.compat import distro_utils class IpForwardingUtils(object): - """System IP address configuration utilities.""" - - def __init__(self, logger, proto_id=None): - """Constructor. - - Args: - logger: logger object, used to write to SysLog and serial port. - proto_id: string, the routing protocol identifier for Google IP changes. - """ - self.logger = logger - self.proto_id = proto_id or '66' - - def _CreateRouteOptions(self, **kwargs): - """Create a dictionary of parameters to append to the ip route command. - - Args: - **kwargs: dict, the string parameters to update in the ip route command. - - Returns: - dict, the string parameters to append to the ip route command. - """ - options = { - 'proto': self.proto_id, - 'scope': 'host', - } - options.update(kwargs) - return options - - def _RunIpRoute(self, args=None, options=None): - """Run a command with ip route and return the response. - - Args: - args: list, the string ip route command args to execute. - options: dict, the string parameters to append to the ip route command. - - Returns: - string, the standard output from the ip route command execution. - """ - args = args or [] - options = options or {} - command = ['ip', 'route'] - command.extend(args) - for item in options.items(): - command.extend(item) - try: - process = subprocess.Popen( - command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = process.communicate() - except OSError as e: - self.logger.warning('Exception running %s. %s.', command, str(e)) - else: - if process.returncode: - message = 'Non-zero exit status running %s. %s.' - self.logger.warning(message, command, stderr.strip()) - else: - return stdout.decode('utf-8', 'replace') - return '' - - def ParseForwardedIps(self, forwarded_ips): - """Parse and validate forwarded IP addresses. - - Args: - forwarded_ips: list, the IP address strings to parse. - - Returns: - list, the valid IP address strings. - """ - addresses = [] - forwarded_ips = forwarded_ips or [] - for ip in forwarded_ips: - if ip and (IP_REGEX.match(ip) or IP_ALIAS_REGEX.match(ip)): - addresses.append(ip[:-3] if ip.endswith('/32') else ip) - else: - self.logger.warning('Could not parse IP address: "%s".', ip) - return addresses - - def GetForwardedIps(self, interface): - """Retrieve the list of configured forwarded IP addresses. - - Args: - interface: string, the output device to query. - - Returns: - list, the IP address strings. - """ - args = ['ls', 'table', 'local', 'type', 'local'] - options = self._CreateRouteOptions(dev=interface) - result = self._RunIpRoute(args=args, options=options) - return self.ParseForwardedIps(result.split()) - - def AddForwardedIp(self, address, interface): - """Configure a new IP address on the network interface. - - Args: - address: string, the IP address to configure. - interface: string, the output device to use. - """ - address = address if IP_ALIAS_REGEX.match(address) else '%s/32' % address - args = ['add', 'to', 'local', address] - options = self._CreateRouteOptions(dev=interface) - self._RunIpRoute(args=args, options=options) - - def RemoveForwardedIp(self, address, interface): - """Delete an IP address on the network interface. + """Deprecated. Overridden for backwards compatibility.""" - Args: - address: string, the IP address to configure. - interface: string, the output device to use. - """ - address = address if IP_ALIAS_REGEX.match(address) else '%s/32' % address - args = ['delete', 'to', 'local', address] - options = self._CreateRouteOptions(dev=interface) - self._RunIpRoute(args=args, options=options) + def __new__(self, logger, proto_id=None): + return distro_utils.Utils().IpForwardingUtils(logger, proto_id) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/networking/ip_forwarding/tests/ip_forwarding_test.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/networking/ip_forwarding/tests/ip_forwarding_test.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/networking/ip_forwarding/tests/ip_forwarding_test.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/networking/ip_forwarding/tests/ip_forwarding_test.py 2018-09-05 21:51:39.000000000 +0000 @@ -103,15 +103,16 @@ self.mock_ip_forwarding_utils.ParseForwardedIps.return_value = desired self.mock_ip_forwarding_utils.GetForwardedIps.return_value = configured forwarded_ips = 'forwarded ips' + interface_ip = 'interface ip' interface = 'interface' expected_add = ['d'] expected_remove = ['a', 'b'] ip_forwarding.IpForwarding.HandleForwardedIps( - self.mock_setup, interface, forwarded_ips) + self.mock_setup, interface, forwarded_ips, interface_ip) expected_calls = [ mock.call.forwarding.ParseForwardedIps(forwarded_ips), - mock.call.forwarding.GetForwardedIps(interface), + mock.call.forwarding.GetForwardedIps(interface, interface_ip), mock.call.setup._LogForwardedIpChanges( configured, desired, expected_add, expected_remove, interface), mock.call.setup._AddForwardedIps(expected_add, interface), diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/networking/ip_forwarding/tests/ip_forwarding_utils_test.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/networking/ip_forwarding/tests/ip_forwarding_utils_test.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/networking/ip_forwarding/tests/ip_forwarding_utils_test.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/networking/ip_forwarding/tests/ip_forwarding_utils_test.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,233 +0,0 @@ -#!/usr/bin/python -# Copyright 2016 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Unittest for ip_forwarding_utils.py module.""" - -from google_compute_engine.networking.ip_forwarding import ip_forwarding_utils -from google_compute_engine.test_compat import mock -from google_compute_engine.test_compat import unittest - - -def _CreateMockProcess(returncode, stdout, stderr): - mock_process = mock.Mock() - mock_process.returncode = returncode - mock_process.communicate.return_value = (stdout, stderr) - return mock_process - - -class IpForwardingUtilsTest(unittest.TestCase): - - def setUp(self): - self.mock_logger = mock.Mock() - self.options = {'hello': 'world'} - with mock.patch( - 'google_compute_engine.networking.ip_forwarding.ip_forwarding_utils' - '.subprocess') as mock_subprocess: - mock_subprocess.Popen.return_value = _CreateMockProcess( - 0, b'out', b'') - self.mock_utils = ip_forwarding_utils.IpForwardingUtils( - self.mock_logger) - self.mock_utils.proto_id = 'proto' - - def testCreateRouteOptions(self): - # Default options. - expected_options = { - 'proto': 'proto', - 'scope': 'host', - } - self.assertEqual(self.mock_utils._CreateRouteOptions(), expected_options) - - # Update dictionary when arguments are specified. - expected_options = { - 'proto': 'proto', - 'scope': 'host', - 'num': 1, - 'string': 'hello world', - } - self.assertEqual( - self.mock_utils._CreateRouteOptions(num=1, string='hello world'), - expected_options) - - # Update the default options. - expected_options = { - 'proto': 'test 1', - 'scope': 'test 2', - } - self.assertEqual( - self.mock_utils._CreateRouteOptions(proto='test 1', scope='test 2'), - expected_options) - - @mock.patch('google_compute_engine.networking.ip_forwarding.ip_forwarding_utils.subprocess') - def testRunIpRoute(self, mock_subprocess): - mock_process = _CreateMockProcess(0, b'out', b'') - mock_subprocess.Popen.return_value = mock_process - args = ['foo', 'bar'] - options = {'one': 'two'} - - self.assertEqual( - self.mock_utils._RunIpRoute(args=args, options=options), 'out') - command = ['ip', 'route', 'foo', 'bar', 'one', 'two'] - mock_subprocess.Popen.assert_called_once_with( - command, stdout=mock_subprocess.PIPE, stderr=mock_subprocess.PIPE) - mock_process.communicate.assert_called_once_with() - self.mock_logger.warning.assert_not_called() - - @mock.patch('google_compute_engine.networking.ip_forwarding.ip_forwarding_utils.subprocess') - def testRunIpRouteReturnCode(self, mock_subprocess): - mock_process = _CreateMockProcess(1, b'out', b'error\n') - mock_subprocess.Popen.return_value = mock_process - - self.assertEqual( - self.mock_utils._RunIpRoute(args=['foo', 'bar'], options=self.options), - '') - command = ['ip', 'route', 'foo', 'bar', 'hello', 'world'] - self.mock_logger.warning.assert_called_once_with( - mock.ANY, command, b'error') - - @mock.patch('google_compute_engine.networking.ip_forwarding.ip_forwarding_utils.subprocess') - def testRunIpRouteException(self, mock_subprocess): - mock_subprocess.Popen.side_effect = OSError('Test Error') - - self.assertEqual( - self.mock_utils._RunIpRoute(args=['foo', 'bar'], options=self.options), - '') - command = ['ip', 'route', 'foo', 'bar', 'hello', 'world'] - self.mock_logger.warning.assert_called_once_with( - mock.ANY, command, 'Test Error') - - def testParseForwardedIps(self): - self.assertEqual(self.mock_utils.ParseForwardedIps(None), []) - self.assertEqual(self.mock_utils.ParseForwardedIps([]), []) - self.assertEqual(self.mock_utils.ParseForwardedIps([None]), []) - self.assertEqual(self.mock_utils.ParseForwardedIps(['invalid']), []) - self.assertEqual(self.mock_utils.ParseForwardedIps(['1a1a1a1']), []) - self.assertEqual(self.mock_utils.ParseForwardedIps(['1.1.1.1.1']), []) - self.assertEqual(self.mock_utils.ParseForwardedIps(['1111.1.1.1']), []) - self.assertEqual(self.mock_utils.ParseForwardedIps(['1.1.1.1111']), []) - expected_calls = [ - mock.call.warning(mock.ANY, None), - mock.call.warning(mock.ANY, 'invalid'), - mock.call.warning(mock.ANY, '1a1a1a1'), - mock.call.warning(mock.ANY, '1.1.1.1.1'), - mock.call.warning(mock.ANY, '1111.1.1.1'), - mock.call.warning(mock.ANY, '1.1.1.1111'), - ] - self.assertEqual(self.mock_logger.mock_calls, expected_calls) - - def testParseForwardedIpsComplex(self): - forwarded_ips = { - '{{}}\n\"hello\"\n!@#$%^&*()\n\n': False, - '1111.1.1.1': False, - '1.1.1.1': True, - 'hello': False, - '123.123.123.123': True, - '1.1.1.': False, - '1.1.1.a': False, - None: False, - '1.0.0.0': True, - '1.1.1.1/1': True, - '1.1.1.1/11': True, - '123.123.123.123/1': True, - '123.123.123.123/123': False, - '123.123.123.123/a': False, - '123.123.123.123/': False, - } - input_ips = forwarded_ips.keys() - valid_ips = [ip for ip, valid in forwarded_ips.items() if valid] - invalid_ips = [ip for ip, valid in forwarded_ips.items() if not valid] - - self.assertEqual(self.mock_utils.ParseForwardedIps(input_ips), valid_ips) - expected_calls = [mock.call.warning(mock.ANY, ip) for ip in invalid_ips] - self.assertEqual(self.mock_logger.mock_calls, expected_calls) - - def testParseForwardedIpsSubnet(self): - forwarded_ips = { - '1.1.1.1': '1.1.1.1', - '1.1.1.1/32': '1.1.1.1', - '1.1.1.1/1': '1.1.1.1/1', - '1.1.1.1/10': '1.1.1.1/10', - '1.1.1.1/24': '1.1.1.1/24', - } - for ip, value in forwarded_ips.items(): - self.assertEqual(self.mock_utils.ParseForwardedIps([ip]), [value]) - - def testGetForwardedIps(self): - mock_options = mock.Mock() - mock_options.return_value = self.options - mock_run = mock.Mock() - mock_run.return_value = 'a\nb\n' - mock_parse = mock.Mock() - mock_parse.return_value = ['Test'] - self.mock_utils._CreateRouteOptions = mock_options - self.mock_utils._RunIpRoute = mock_run - self.mock_utils.ParseForwardedIps = mock_parse - - self.assertEqual(self.mock_utils.GetForwardedIps('interface'), ['Test']) - mock_options.assert_called_once_with(dev='interface') - mock_run.assert_called_once_with( - args=['ls', 'table', 'local', 'type', 'local'], options=self.options) - mock_parse.assert_called_once_with(['a', 'b']) - - def testAddForwardedIp(self): - mock_options = mock.Mock() - mock_options.return_value = self.options - mock_run = mock.Mock() - self.mock_utils._CreateRouteOptions = mock_options - self.mock_utils._RunIpRoute = mock_run - - self.mock_utils.AddForwardedIp('1.1.1.1', 'interface') - mock_options.assert_called_once_with(dev='interface') - mock_run.assert_called_once_with( - args=['add', 'to', 'local', '1.1.1.1/32'], options=self.options) - - def testAddIpAlias(self): - mock_options = mock.Mock() - mock_options.return_value = self.options - mock_run = mock.Mock() - self.mock_utils._CreateRouteOptions = mock_options - self.mock_utils._RunIpRoute = mock_run - - self.mock_utils.AddForwardedIp('1.1.1.1/24', 'interface') - mock_options.assert_called_once_with(dev='interface') - mock_run.assert_called_once_with( - args=['add', 'to', 'local', '1.1.1.1/24'], options=self.options) - - def testRemoveForwardedIp(self): - mock_options = mock.Mock() - mock_options.return_value = self.options - mock_run = mock.Mock() - self.mock_utils._CreateRouteOptions = mock_options - self.mock_utils._RunIpRoute = mock_run - - self.mock_utils.RemoveForwardedIp('1.1.1.1', 'interface') - mock_options.assert_called_once_with(dev='interface') - mock_run.assert_called_once_with( - args=['delete', 'to', 'local', '1.1.1.1/32'], options=self.options) - - def testRemoveAliasIp(self): - mock_options = mock.Mock() - mock_options.return_value = self.options - mock_run = mock.Mock() - self.mock_utils._CreateRouteOptions = mock_options - self.mock_utils._RunIpRoute = mock_run - - self.mock_utils.RemoveForwardedIp('1.1.1.1/24', 'interface') - mock_options.assert_called_once_with(dev='interface') - mock_run.assert_called_once_with( - args=['delete', 'to', 'local', '1.1.1.1/24'], options=self.options) - - -if __name__ == '__main__': - unittest.main() diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/networking/network_daemon.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/networking/network_daemon.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/networking/network_daemon.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/networking/network_daemon.py 2018-09-05 21:51:39.000000000 +0000 @@ -91,12 +91,12 @@ if self.network_setup_enabled: self.network_setup.EnableNetworkInterfaces( - [interface.name for interface in network_interfaces]) + [interface.name for interface in network_interfaces[1:]]) for interface in network_interfaces: if self.ip_forwarding_enabled: self.ip_forwarding.HandleForwardedIps( - interface.name, interface.forwarded_ips) + interface.name, interface.forwarded_ips, interface.ip) def _ExtractInterfaceMetadata(self, metadata): """Extracts network interface metadata. @@ -119,7 +119,7 @@ if self.target_instance_ips: ip_addresses.extend(network_interface.get('targetInstanceIps', [])) interfaces.append(NetworkDaemon.NetworkInterface( - interface, ip_addresses)) + interface, ip_addresses, network_interface.get('ip', []))) else: message = 'Network interface not found for MAC address: %s.' self.logger.warning(message, mac_address) @@ -128,9 +128,10 @@ class NetworkInterface(object): """Network interface information extracted from metadata.""" - def __init__(self, name, forwarded_ips=None): + def __init__(self, name, forwarded_ips=None, ip=None): self.name = name self.forwarded_ips = forwarded_ips + self.ip = ip def main(): diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/networking/network_setup/network_setup.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/networking/network_setup/network_setup.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/networking/network_setup/network_setup.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/networking/network_setup/network_setup.py 2018-09-05 21:51:39.000000000 +0000 @@ -56,8 +56,6 @@ self.logger.info('Ethernet interfaces: %s.', interfaces) self.interfaces = set(interfaces) - if len(interfaces) <= 1: - return if self.dhcp_command: try: diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/networking/network_setup/tests/network_setup_test.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/networking/network_setup/tests/network_setup_test.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/networking/network_setup/tests/network_setup_test.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/networking/network_setup/tests/network_setup_test.py 2018-09-05 21:51:39.000000000 +0000 @@ -72,29 +72,25 @@ network_setup.NetworkSetup.EnableNetworkInterfaces( self.mock_setup, ['A', 'B', 'C']) self.assertEqual(self.mock_setup.interfaces, set(['A', 'B', 'C'])) - # A single interface is enabled by default. - network_setup.NetworkSetup.EnableNetworkInterfaces(self.mock_setup, ['D']) - self.assertEqual(self.mock_setup.interfaces, set(['D'])) # Run a user supplied command successfully. self.mock_setup.dhcp_command = 'success' network_setup.NetworkSetup.EnableNetworkInterfaces( - self.mock_setup, ['E', 'F']) - self.assertEqual(self.mock_setup.interfaces, set(['E', 'F'])) + self.mock_setup, ['D', 'E']) + self.assertEqual(self.mock_setup.interfaces, set(['D', 'E'])) # Run a user supplied command and logger error messages. self.mock_setup.dhcp_command = 'failure' network_setup.NetworkSetup.EnableNetworkInterfaces( - self.mock_setup, ['G', 'H']) - self.assertEqual(self.mock_setup.interfaces, set(['G', 'H'])) + self.mock_setup, ['F', 'G']) + self.assertEqual(self.mock_setup.interfaces, set(['F', 'G'])) expected_calls = [ mock.call.logger.info(mock.ANY, ['A', 'B']), mock.call.enable(['A', 'B'], mock.ANY, dhclient_script='/bin/script'), mock.call.logger.info(mock.ANY, ['A', 'B', 'C']), mock.call.enable( ['A', 'B', 'C'], mock.ANY, dhclient_script='/bin/script'), - mock.call.logger.info(mock.ANY, ['D']), - mock.call.logger.info(mock.ANY, ['E', 'F']), + mock.call.logger.info(mock.ANY, ['D', 'E']), mock.call.call(['success']), - mock.call.logger.info(mock.ANY, ['G', 'H']), + mock.call.logger.info(mock.ANY, ['F', 'G']), mock.call.call(['failure']), mock.call.logger.warning(mock.ANY), ] diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/networking/tests/network_daemon_test.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/networking/tests/network_daemon_test.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/networking/tests/network_daemon_test.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/networking/tests/network_daemon_test.py 2018-09-05 21:51:39.000000000 +0000 @@ -148,9 +148,9 @@ self.mock_setup, result) expected_calls = [ mock.call.setup._ExtractInterfaceMetadata(result), - mock.call.network_setup.EnableNetworkInterfaces(['a', 'b']), - mock.call.forwarding.HandleForwardedIps('a', None), - mock.call.forwarding.HandleForwardedIps('b', None), + mock.call.network_setup.EnableNetworkInterfaces(['b']), + mock.call.forwarding.HandleForwardedIps('a', None, None), + mock.call.forwarding.HandleForwardedIps('b', None, None), ] self.assertEqual(mocks.mock_calls, expected_calls) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine/tests/compat_test.py gce-compute-image-packages-20180905+dfsg1/google_compute_engine/tests/compat_test.py --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine/tests/compat_test.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine/tests/compat_test.py 2018-09-05 21:51:39.000000000 +0000 @@ -69,36 +69,36 @@ pass @mock.patch('google_compute_engine.compat.distro.linux_distribution') - def testDistroCompat(self, mock_call): + def testDistroCompatLinux(self, mock_call): test_cases = { ('debian', '8.10', ''): - google_compute_engine.distro.debian_8.utils, + google_compute_engine.distro_lib.debian_8.utils, ('debian', '9.3', ''): - google_compute_engine.distro.debian_9.utils, + google_compute_engine.distro_lib.debian_9.utils, ('debian', '10.3', ''): - google_compute_engine.distro.debian_9.utils, + google_compute_engine.distro_lib.debian_9.utils, ('SUSE Linux Enterprise Server', '11', 'x86_64'): - google_compute_engine.distro.sles_11.utils, + google_compute_engine.distro_lib.sles_11.utils, ('SUSE Linux Enterprise Server', '12', 'x86_64'): - google_compute_engine.distro.sles_12.utils, + google_compute_engine.distro_lib.sles_12.utils, ('SUSE Linux Enterprise Server', '13', 'x86_64'): - google_compute_engine.distro.sles_12.utils, + google_compute_engine.distro_lib.sles_12.utils, ('CentOS Linux', '6.4.3', 'Core'): - google_compute_engine.distro.el_6.utils, + google_compute_engine.distro_lib.el_6.utils, ('CentOS Linux', '7.4.1708', 'Core'): - google_compute_engine.distro.el_7.utils, + google_compute_engine.distro_lib.el_7.utils, ('CentOS Linux', '8.4.3', 'Core'): - google_compute_engine.distro.el_7.utils, + google_compute_engine.distro_lib.el_7.utils, ('Red Hat Enterprise Linux Server', '6.3.2', ''): - google_compute_engine.distro.el_6.utils, + google_compute_engine.distro_lib.el_6.utils, ('Red Hat Enterprise Linux Server', '7.4', ''): - google_compute_engine.distro.el_7.utils, + google_compute_engine.distro_lib.el_7.utils, ('Red Hat Enterprise Linux Server', '8.5.1', ''): - google_compute_engine.distro.el_7.utils, + google_compute_engine.distro_lib.el_7.utils, ('', '', ''): - google_compute_engine.distro.debian_9.utils, + google_compute_engine.distro_lib.debian_9.utils, ('xxxx', 'xxxx', 'xxxx'): - google_compute_engine.distro.debian_9.utils, + google_compute_engine.distro_lib.debian_9.utils, } for distro in test_cases: @@ -107,6 +107,15 @@ self.assertEqual( test_cases[distro], google_compute_engine.compat.distro_utils) + @mock.patch('google_compute_engine.compat.sys.platform', 'freebsd') + @mock.patch('google_compute_engine.compat.distro.version') + def testDistroCompatFreeBSD(self, mock_call): + mock_call.return_value = 'FreeBSD 11.1-RELEASE-p4 #0: Tue Nov 14 06:12:40' + reload_import(google_compute_engine.compat) + self.assertEqual( + google_compute_engine.distro_lib.freebsd_11.utils, + google_compute_engine.compat.distro_utils) + if __name__ == '__main__': unittest.main() diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine_init/systemd/google-network-daemon.service gce-compute-image-packages-20180905+dfsg1/google_compute_engine_init/systemd/google-network-daemon.service --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine_init/systemd/google-network-daemon.service 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine_init/systemd/google-network-daemon.service 2018-09-05 21:51:39.000000000 +0000 @@ -4,6 +4,7 @@ After=google-instance-setup.service Wants=local-fs.target network-online.target network.target Requires=network.target +PartOf=network.service [Service] Type=simple diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/Makefile gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/Makefile --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/Makefile 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/Makefile 2018-09-05 21:51:39.000000000 +0000 @@ -4,7 +4,7 @@ NAME = google-compute-engine-$(BASENAME) MAJOR = 1 MINOR = 3 -REVISION = 0 +REVISION = 1 LIBNSS_CACHE_OSLOGIN = libnss_cache_$(BASENAME) LIBNSS_CACHE_OSLOGIN_NAME = libnss_cache_$(NAME)-$(MAJOR).$(MINOR).$(REVISION).so @@ -96,7 +96,7 @@ $(NSS_SRC) $(UTILS) $(LIBS) $(NSS_CACHE_BIN): $(NSS_CACHE_SRC) $(UTILS_SRC) - $(CXX) $(LDFLAGS) $(INCLUDE_FLAGS) -o $(NSS_CACHE_BIN) $(NSS_CACHE_SRC) $(UTILS_SRC) $(LIBS) + $(CXX) $(CXXFLAGS) $(LDFLAGS) $(INCLUDE_FLAGS) -o $(NSS_CACHE_BIN) $(NSS_CACHE_SRC) $(UTILS_SRC) $(LIBS) $(LIBNSS_CACHE_OSLOGIN_NAME): $(LIBNSS_CACHE_OBJ) $(LIBNSS_COMPAT_OBJ) $(CXX) $(LIBNSS_SO_FLAGS) -o $(LIBNSS_CACHE_OSLOGIN_NAME) $(LIBNSS_CACHE_OBJ) $(LIBNSS_COMPAT_OBJ) @@ -122,7 +122,7 @@ $(CXX) $(CXXFLAGS) -c $(PAM_ADMIN_SRC) -o $(PAM_ADMIN_OBJ) $(AUTHKEYS_BIN): $(AUTHKEYS_SRC) $(UTILS_SRC) - $(CXX) $(LDFLAGS) $(INCLUDE_FLAGS) -o $(AUTHKEYS_BIN) $(AUTHKEYS_SRC) $(UTILS_SRC) $(LIBS) + $(CXX) $(CXXFLAGS) $(LDFLAGS) $(INCLUDE_FLAGS) -o $(AUTHKEYS_BIN) $(AUTHKEYS_SRC) $(UTILS_SRC) $(LIBS) $(UTILS): $(UTILS_SRC) $(CXX) $(CXXFLAGS) $(INCLUDE_FLAGS) -c $(UTILS_SRC) -o $(UTILS) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/packaging/debian10/changelog gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/packaging/debian10/changelog --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/packaging/debian10/changelog 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/packaging/debian10/changelog 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1,12 @@ +google-compute-engine-oslogin (1.3.1-1+deb10) unstable; urgency=low + + * Add user name validation to pam modules. + * Return false on failed final load. + + -- Google Cloud Team Wed, 05 Sep 2018 12:00:00 -0700 + +google-compute-engine-oslogin (1.3.0-1+deb10) unstable; urgency=low + + * Include libnss cache as part of the OS Login package. + + -- Google Cloud Team Tue, 01 May 2018 12:00:00 -0700 diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/packaging/debian10/compat gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/packaging/debian10/compat --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/packaging/debian10/compat 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/packaging/debian10/compat 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1 @@ +9 diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/packaging/debian10/control gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/packaging/debian10/control --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/packaging/debian10/control 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/packaging/debian10/control 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1,13 @@ +Source: google-compute-engine-oslogin +Maintainer: Google Compute Engine +Section: misc +Priority: optional +Standards-Version: 3.9.6.1 +Build-Depends: debhelper (>= 9), libcurl4-openssl-dev, libjson-c-dev | libjson0-dev, libpam-dev + +Package: google-compute-engine-oslogin +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: Google Compute Engine OS Login + Contains libraries, applications and configurations for using OS Login + on Google Compute Engine Virtual Machine Instances. diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/packaging/debian10/copyright gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/packaging/debian10/copyright --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/packaging/debian10/copyright 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/packaging/debian10/copyright 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1,27 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: google-compute-engine-oslogin +Upstream-Contact: gc-team@google.com + +Files: * +Copyright: Copyright 2017 Google Inc. +License: Apache-2.0 + +Files: debian/* +Copyright: Copyright 2017 Google Inc. +License: Apache-2.0 + +License: Apache-2.0 + 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. + . + On Debian systems, the complete text of the Apache version 2.0 license + can be found in "/usr/share/common-licenses/Apache-2.0". diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/packaging/debian10/google-compute-engine-oslogin.links gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/packaging/debian10/google-compute-engine-oslogin.links --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/packaging/debian10/google-compute-engine-oslogin.links 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/packaging/debian10/google-compute-engine-oslogin.links 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1,2 @@ +/lib/libnss_google-compute-engine-oslogin-1.3.1.so /lib/libnss_oslogin.so.2 +/lib/libnss_cache_google-compute-engine-oslogin-1.3.1.so /lib/libnss_cache_oslogin.so.2 diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/packaging/debian10/rules gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/packaging/debian10/rules --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/packaging/debian10/rules 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/packaging/debian10/rules 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1,6 @@ +#!/usr/bin/make -f +%: + dh $@ + +override_dh_auto_install: + dh_auto_install -- PAM_INSTALL_PATH=/lib/x86_64-linux-gnu/security BIN_INSTALL_PATH=/usr/bin diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/packaging/debian10/shlibs.local gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/packaging/debian10/shlibs.local --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/packaging/debian10/shlibs.local 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/packaging/debian10/shlibs.local 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1 @@ +libcurl 4 libcurl4 diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/packaging/debian10/source/format gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/packaging/debian10/source/format --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/packaging/debian10/source/format 1970-01-01 00:00:00.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/packaging/debian10/source/format 2018-09-05 21:51:39.000000000 +0000 @@ -0,0 +1 @@ +3.0 (quilt) diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/packaging/debian9/changelog gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/packaging/debian9/changelog --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/packaging/debian9/changelog 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/packaging/debian9/changelog 2018-09-05 21:51:39.000000000 +0000 @@ -1,3 +1,10 @@ +google-compute-engine-oslogin (1.3.1-1+deb9) unstable; urgency=low + + * Add user name validation to pam modules. + * Return false on failed final load. + + -- Google Cloud Team Wed, 05 Sep 2018 12:00:00 -0700 + google-compute-engine-oslogin (1.3.0-1+deb9) unstable; urgency=low * Include libnss cache as part of the OS Login package. diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/packaging/debian9/google-compute-engine-oslogin.links gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/packaging/debian9/google-compute-engine-oslogin.links --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/packaging/debian9/google-compute-engine-oslogin.links 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/packaging/debian9/google-compute-engine-oslogin.links 2018-09-05 21:51:39.000000000 +0000 @@ -1,2 +1,2 @@ -/lib/libnss_google-compute-engine-oslogin-1.3.0.so /lib/libnss_oslogin.so.2 -/lib/libnss_cache_google-compute-engine-oslogin-1.3.0.so /lib/libnss_cache_oslogin.so.2 +/lib/libnss_google-compute-engine-oslogin-1.3.1.so /lib/libnss_oslogin.so.2 +/lib/libnss_cache_google-compute-engine-oslogin-1.3.1.so /lib/libnss_cache_oslogin.so.2 diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/packaging/rpmbuild/SPECS/google-compute-engine-oslogin.spec gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/packaging/rpmbuild/SPECS/google-compute-engine-oslogin.spec --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/packaging/rpmbuild/SPECS/google-compute-engine-oslogin.spec 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/packaging/rpmbuild/SPECS/google-compute-engine-oslogin.spec 2018-09-05 21:51:39.000000000 +0000 @@ -18,19 +18,21 @@ %endif Name: google-compute-engine-oslogin -Version: 1.3.0 +Version: 1.3.1 Release: 1%{?dist} Summary: OS Login Functionality for Google Compute Engine License: ASL 2.0 Source0: %{name}_%{version}.orig.tar.gz +BuildRequires: boost-devel BuildRequires: gcc-c++ BuildRequires: make BuildRequires: libcurl BuildRequires: json-c BuildRequires: pam-devel BuildRequires: policycoreutils-python +Requires: boost-regex Requires: policycoreutils-python %define pam_install_path /%{_lib}/security @@ -43,7 +45,7 @@ %setup %build -make %{?_smp_mflags} LIBS="-lcurl -ljson-c" +make %{?_smp_mflags} LIBS="-lcurl -ljson-c -lboost_regex" %install rm -rf %{buildroot} diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/packaging/setup_deb.sh gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/packaging/setup_deb.sh --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/packaging/setup_deb.sh 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/packaging/setup_deb.sh 2018-09-05 21:51:39.000000000 +0000 @@ -20,7 +20,7 @@ # Run from the top of the source directory. NAME="google-compute-engine-oslogin" -VERSION="1.3.0" +VERSION="1.3.1" working_dir=${PWD} diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/packaging/setup_rpm.sh gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/packaging/setup_rpm.sh --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/packaging/setup_rpm.sh 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/packaging/setup_rpm.sh 2018-09-05 21:51:39.000000000 +0000 @@ -20,13 +20,13 @@ # Run from the top of the source directory. NAME="google-compute-engine-oslogin" -VERSION="1.3.0" +VERSION="1.3.1" working_dir=${PWD} rpm_working_dir=/tmp/rpmpackage/${NAME}-${VERSION} # Build dependencies. -sudo yum -y install make gcc-c++ libcurl-devel json-c json-c-devel pam-devel policycoreutils-python +sudo yum -y install make gcc-c++ libcurl-devel json-c json-c-devel pam-devel policycoreutils-python boost-devel # .rpm creation tools. sudo yum -y install rpmdevtools diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/pam_module/pam_oslogin_admin.cc gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/pam_module/pam_oslogin_admin.cc --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/pam_module/pam_oslogin_admin.cc 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/pam_module/pam_oslogin_admin.cc 2018-09-05 21:51:39.000000000 +0000 @@ -34,6 +34,7 @@ using oslogin_utils::ParseJsonToAuthorizeResponse; using oslogin_utils::ParseJsonToEmail; using oslogin_utils::UrlEncode; +using oslogin_utils::ValidateUserName; using oslogin_utils::kMetadataServerUrl; static const char kSudoersDir[] = "/var/google-sudoers.d/"; @@ -51,6 +52,10 @@ return pam_result; } string str_user_name(user_name); + if (!ValidateUserName(user_name)) { + // If the user name is not a valid oslogin user, don't bother continuing. + return PAM_SUCCESS; + } std::stringstream url; url << kMetadataServerUrl diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/pam_module/pam_oslogin_login.cc gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/pam_module/pam_oslogin_login.cc --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/pam_module/pam_oslogin_login.cc 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/pam_module/pam_oslogin_login.cc 2018-09-05 21:51:39.000000000 +0000 @@ -34,6 +34,7 @@ using oslogin_utils::ParseJsonToAuthorizeResponse; using oslogin_utils::ParseJsonToEmail; using oslogin_utils::UrlEncode; +using oslogin_utils::ValidateUserName; using oslogin_utils::kMetadataServerUrl; static const char kUsersDir[] = "/var/google-users.d/"; @@ -49,6 +50,10 @@ return pam_result; } string str_user_name(user_name); + if (!ValidateUserName(user_name)) { + // If the user name is not a valid oslogin user, don't bother continuing. + return PAM_SUCCESS; + } string users_filename = kUsersDir; users_filename.append(user_name); struct stat buffer; diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/README.md gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/README.md --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/README.md 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/README.md 2018-09-05 21:51:39.000000000 +0000 @@ -232,10 +232,13 @@ * `Makefile` Update the MAJOR, MINOR, and REVISION variables. * `packaging/debian8/changelog` Add a new entry with the new version. * `packaging/debian9/changelog` Add a new entry with the new version. +* `packaging/debian10/changelog` Add a new entry with the new version. * `packaging/debian8/google-compute-engine-oslogin.links` Update the libnss version string. * `packaging/debian9/google-compute-engine-oslogin.links` Update the libnss version string. +* `packaging/debian10/google-compute-engine-oslogin.links` Update the libnss + version string. * `packaging/rpmbuild/SPECS/google-compute-engine-oslogin.spec` Update the Version field. * `packaging/setup_deb.sh` Update VERSION variable. diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/utils/oslogin_utils.cc gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/utils/oslogin_utils.cc --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/utils/oslogin_utils.cc 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/utils/oslogin_utils.cc 2018-09-05 21:51:39.000000000 +0000 @@ -23,6 +23,19 @@ #include #include +#ifdef __GNUC__ +#if __GNUC__ > 4 || \ + (__GNUC__ == 4 && (__GNUC_MINOR__ > 9 || \ + (__GNUC_MINOR__ == 9 && \ + __GNUC_PATCHLEVEL__ > 0))) +#include +#define Regex std +#else +#include +#define Regex boost +#endif +#endif + #include "oslogin_utils.h" using std::string; @@ -30,6 +43,9 @@ // Maximum number of retries for HTTP requests. const int kMaxRetries = 1; +// Regex for validating user names. +const char kUserNameRegex[] = "^[a-zA-Z0-9._][a-zA-Z0-9._-]{0,31}$"; + namespace oslogin_utils { BufferManager::BufferManager(char* buf, size_t buflen) @@ -154,9 +170,12 @@ string response; long http_code = 0; if (!HttpGet(url.str(), &response, &http_code) || http_code != 200 || - response.empty() || - (!LoadJsonArrayToCache(response) && !on_last_page_)) { - *errnop = ENOENT; + response.empty() || !LoadJsonArrayToCache(response)) { + // It is possible this to be true after LoadJsonArrayToCache(), so we + // must check it again. + if(!OnLastPage()) { + *errnop = ENOENT; + } return false; } } @@ -219,6 +238,11 @@ return true; } +bool ValidateUserName(const string& user_name) { + Regex::regex r(kUserNameRegex); + return Regex::regex_match(user_name, r); +} + string UrlEncode(const string& param) { CURL* curl = curl_easy_init(); char* encoded = curl_easy_escape(curl, param.c_str(), param.length()); diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/utils/oslogin_utils.h gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/utils/oslogin_utils.h --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/utils/oslogin_utils.h 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/utils/oslogin_utils.h 2018-09-05 21:51:39.000000000 +0000 @@ -139,6 +139,9 @@ // stored in response, and the HTTP response code will be stored in http_code. bool HttpGet(const string& url, string* response, long* http_code); +// Returns whether user_name is a valid OsLogin user name. +bool ValidateUserName(const string& user_name); + // URL encodes the given parameter. Returns the encoded parameter. std::string UrlEncode(const string& param); diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/utils/oslogin_utils_test.cc gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/utils/oslogin_utils_test.cc --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/utils/oslogin_utils_test.cc 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/utils/oslogin_utils_test.cc 2018-09-05 21:51:39.000000000 +0000 @@ -152,7 +152,7 @@ string response = "{\"nextPageToken\": \"0\"}"; - ASSERT_TRUE(nss_cache.LoadJsonArrayToCache(response)); + ASSERT_FALSE(nss_cache.LoadJsonArrayToCache(response)); ASSERT_STREQ(nss_cache.GetPageToken().c_str(), ""); size_t buflen = 500; @@ -164,6 +164,7 @@ // Verify that there are no more users stored. ASSERT_FALSE(nss_cache.HasNextPasswd()); + ASSERT_TRUE(nss_cache.OnLastPage()); ASSERT_FALSE(nss_cache.GetNextPasswd(&buf, &result, &test_errno)); EXPECT_EQ(test_errno, ENOENT); } @@ -428,6 +429,41 @@ ASSERT_TRUE(ParseJsonToAuthorizeResponse(response)); } +TEST(ValidateUserNameTest, ValidateValidUserNames) { + string cases[] = { + "user", + "_", + ".", + ".abc_", + "_abc-", + "ABC", + "A_.-", + "ausernamethirtytwocharacterslong" + }; + for (auto test_user : cases) { + ASSERT_TRUE(ValidateUserName(test_user)); + } +} + +TEST(ValidateUserNameTest, ValidateInvalidUserNames) { + string cases[] = { + "", + "!#$%^", + "-abc", + "#abc", + "^abc", + "abc*xyz", + "abc xyz", + "xyz*", + "xyz$", + "usernamethirtythreecharacterslong", + "../../etc/shadow", + }; + for (auto test_user : cases) { + ASSERT_FALSE(ValidateUserName(test_user)); + } +} + } // namespace oslogin_utils int main(int argc, char **argv) { diff -Nru gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/utils/run_tests.sh gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/utils/run_tests.sh --- gce-compute-image-packages-20180510+dfsg1/google_compute_engine_oslogin/utils/run_tests.sh 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/google_compute_engine_oslogin/utils/run_tests.sh 2018-09-05 21:51:39.000000000 +0000 @@ -14,6 +14,6 @@ # limitations under the License. # Unit tests require gtest to be installed. -g++ -o test_runner oslogin_utils_test.cc oslogin_utils.cc -I/usr/include/json-c -lcurl -ljson -lgtest -lpthread +g++ -o test_runner oslogin_utils_test.cc oslogin_utils.cc -I/usr/include/json-c -lcurl -ljson-c -lgtest -lpthread ./test_runner rm ./test_runner diff -Nru gce-compute-image-packages-20180510+dfsg1/README.md gce-compute-image-packages-20180905+dfsg1/README.md --- gce-compute-image-packages-20180510+dfsg1/README.md 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/README.md 2018-09-05 21:51:39.000000000 +0000 @@ -253,6 +253,7 @@ IpForwarding | ethernet\_proto\_id | Protocol ID string for daemon added routes. IpForwarding | ip\_aliases | `false` disables setting up alias IP routes. IpForwarding | target\_instance\_ips | `false` disables internal IP address load balancing. +MetadataScripts | default\_shell | String with the default shell to execute scripts. MetadataScripts | run\_dir | String base directory where metadata scripts are executed. MetadataScripts | startup | `false` disables startup script execution. MetadataScripts | shutdown | `false` disables shutdown script execution. @@ -268,7 +269,7 @@ ## Packaging The guest Python code is packaged as a -[compliant PyPI Python package](http://python-packaging-user-guide.readthedocs.io/en/latest/) +[compliant PyPI Python package](https://packaging.python.org/) that can be used as a library or run independently. In addition to the Python package, deb and rpm packages are created with appropriate init configuration for supported GCE distros. The packages are targeted towards distribution diff -Nru gce-compute-image-packages-20180510+dfsg1/setup.py gce-compute-image-packages-20180905+dfsg1/setup.py --- gce-compute-image-packages-20180510+dfsg1/setup.py 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/setup.py 2018-09-05 21:51:39.000000000 +0000 @@ -36,7 +36,7 @@ packages=setuptools.find_packages(), scripts=glob.glob('scripts/*'), url='https://github.com/GoogleCloudPlatform/compute-image-packages', - version='2.8.2', + version='2.8.4', # Entry points create scripts in /usr/bin that call a function. entry_points={ 'console_scripts': [ @@ -64,6 +64,7 @@ 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', 'Topic :: Internet', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: System :: Installation/Setup', diff -Nru gce-compute-image-packages-20180510+dfsg1/specs/google-compute-engine.spec gce-compute-image-packages-20180905+dfsg1/specs/google-compute-engine.spec --- gce-compute-image-packages-20180510+dfsg1/specs/google-compute-engine.spec 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/specs/google-compute-engine.spec 2018-09-05 21:51:39.000000000 +0000 @@ -18,7 +18,7 @@ %endif Name: google-compute-engine -Version: 2.8.2 +Version: 2.8.4 Release: 1%{?dist} Summary: Google Compute Engine guest environment. License: ASL 2.0 @@ -33,7 +33,6 @@ Requires: curl Requires: google-compute-engine-oslogin -Requires: ntp Requires: python-google-compute-engine = %{version} Requires: python-setuptools Requires: rsyslog diff -Nru gce-compute-image-packages-20180510+dfsg1/specs/python-google-compute-engine.spec gce-compute-image-packages-20180905+dfsg1/specs/python-google-compute-engine.spec --- gce-compute-image-packages-20180510+dfsg1/specs/python-google-compute-engine.spec 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/specs/python-google-compute-engine.spec 2018-09-05 21:51:39.000000000 +0000 @@ -18,7 +18,7 @@ %endif Name: python-google-compute-engine -Version: 2.8.2 +Version: 2.8.4 Release: 1%{?dist} Summary: Google Compute Engine python library License: ASL 2.0 diff -Nru gce-compute-image-packages-20180510+dfsg1/tox.ini gce-compute-image-packages-20180905+dfsg1/tox.ini --- gce-compute-image-packages-20180510+dfsg1/tox.ini 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/tox.ini 2018-09-05 21:51:39.000000000 +0000 @@ -1,9 +1,9 @@ [tox] -envlist = py26,py27,py32,py33,py34,py35,pypy,pypy3 +envlist = py26,py27,py32,py33,py34,py35,py36,py37,pypy,pypy3 [testenv] deps = - py{35,36}: distro + py{35,py36,py37}: distro setuptools>=20 pytest pytest-cov @@ -64,4 +64,4 @@ # See https://github.com/ryanhiebert/tox-travis#advanced-configuration [travis] python = - 2.7: py27, lint + 3.6: py36, lint diff -Nru gce-compute-image-packages-20180510+dfsg1/.travis.yml gce-compute-image-packages-20180905+dfsg1/.travis.yml --- gce-compute-image-packages-20180510+dfsg1/.travis.yml 2018-05-10 22:57:22.000000000 +0000 +++ gce-compute-image-packages-20180905+dfsg1/.travis.yml 2018-09-05 21:51:39.000000000 +0000 @@ -2,11 +2,14 @@ sudo: true python: - 2.7 -- 3.3 - 3.4 - 3.5 - 3.6 - pypy +matrix: + include: + - python: 3.7 + dist: xenial # required for Python 3.7 (travis-ci/travis-ci#9069) os: - linux install: