summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Illfelder <illfelder@google.com>2016-09-06 22:44:12 -0700
committerMax Illfelder <illfelder@google.com>2016-09-06 22:44:12 -0700
commite843cc6919bbed8203ef8ce87b5a3120d4e86800 (patch)
treebf5886e17dce4bb631a9518c3aa66b45f4f14d29
parent9eea7221a41a938730aa6985f72a97ee1f2e1334 (diff)
parent04289b3dc3fd0b29d7c72ba7f6d4794de33b7231 (diff)
downloadgoogle-compute-image-packages-e843cc6919bbed8203ef8ce87b5a3120d4e86800.tar.gz
Merge branch 'development'20160906
-rw-r--r--.gitignore3
-rw-r--r--README.md11
-rwxr-xr-xbuild_packages.sh61
-rw-r--r--google_compute_engine/instance_setup/instance_config.py2
-rwxr-xr-xgoogle_compute_engine/network_setup/network_setup.py43
-rw-r--r--google_compute_engine/network_setup/tests/network_setup_test.py89
-rw-r--r--google_compute_engine/network_utils.py18
-rw-r--r--google_compute_engine/tests/network_utils_test.py20
-rwxr-xr-xgoogle_compute_engine_init/build_packages.sh62
-rwxr-xr-xgoogle_config/build_packages.sh81
-rwxr-xr-xsetup.py2
11 files changed, 271 insertions, 121 deletions
diff --git a/.gitignore b/.gitignore
index f484f40..85ff480 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,9 @@
.tox
.coverage
*.egg-info
+build
+*.deb
+*.rpm
# emacs backup files
*.*~
diff --git a/README.md b/README.md
index 603df3d..17925a2 100644
--- a/README.md
+++ b/README.md
@@ -136,13 +136,8 @@ The library provides the following functions:
A network utilities library retrieves information about a network interface. The
library is used for IP forwarding and for setting up an Ethernet interface on
-boot.
-
-The library exposes the following functions:
-
-* **GetNetworkInterface** retrieves the network interface name associated
- with a MAC address.
-* **IsEnabled** checks whether a network interface is enabled.
+boot. The library exposes a `GetNetworkInterface` function that retrieves the
+network interface name associated with a MAC address.
## Daemons
@@ -249,7 +244,7 @@ InstanceSetup | set_multiqueue | `false` skips multiqueue driver suppo
IpForwarding | ethernet_proto_id | Protocol ID string for daemon added routes.
MetadataScripts | startup | `false` disables startup script execution.
MetadataScripts | shutdown | `false` disables shutdown script execution.
-NetworkInterfaces | dhcp_binary | DHCP binary string that enables a network interface parameter.
+NetworkInterfaces | dhcp_command | String to execute to enable network interfaces.
NetworkInterfaces | setup | `false` disables network interface setup.
Setting `network_enabled` to `false` will skip setting up host keys and the
diff --git a/build_packages.sh b/build_packages.sh
index e669ca1..4298995 100755
--- a/build_packages.sh
+++ b/build_packages.sh
@@ -13,9 +13,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Build the Linux guest environment deb and rpm packages.
+#/ Usage: build_packages.sh [options]
+#/
+#/ Build the Python package for Linux daemons, scripts, and libraries.
+#/
+#/ OPTIONS:
+#/ -h Show this message
+#/ -o DISTRO,... Build only specified distros
-TIMESTAMP="$(date +%s)"
+function usage() {
+ grep '^#/' < "$0" | cut -c 4-
+}
function build_distro() {
declare -r distro="$1"
@@ -48,10 +56,47 @@ function build_distro() {
setup.py
}
-# RHEL/CentOS
-build_distro 'el6' 'rpm' '/usr/lib/python2.6/site-packages'
-build_distro 'el7' 'rpm' '/usr/lib/python2.7/site-packages'
+TIMESTAMP="$(date +%s)"
+
+while getopts 'ho:' OPTION; do
+ case "$OPTION" in
+ h)
+ usage
+ exit 2
+ ;;
+ o)
+ set -f
+ IFS=','
+ BUILD=($OPTARG)
+ set +f
+ ;;
+ ?)
+ usage
+ exit
+ ;;
+ esac
+done
+
+if [ -z "$BUILD" ]; then
+ BUILD=('el6' 'el7' 'wheezy' 'jessie')
+fi
-# Debian
-build_distro 'wheezy' 'deb' '/usr/lib/python2.7/dist-packages'
-build_distro 'jessie' 'deb' '/usr/lib/python2.7/dist-packages'
+for build in "${BUILD[@]}"; do
+ case "$build" in
+ el6) # RHEL/CentOS 6
+ build_distro 'el6' 'rpm' '/usr/lib/python2.6/site-packages'
+ ;;
+ el7) # RHEL/CentOS 7
+ build_distro 'el7' 'rpm' '/usr/lib/python2.7/site-packages'
+ ;;
+ wheezy) # Debian 7
+ build_distro 'wheezy' 'deb' '/usr/lib/python2.7/dist-packages'
+ ;;
+ jessie) # Debian 8
+ build_distro 'jessie' 'deb' '/usr/lib/python2.7/dist-packages'
+ ;;
+ *)
+ echo "Invalid build '${build}'. Use 'el6', 'el7', 'wheezy', or 'jessie'."
+ ;;
+ esac
+done
diff --git a/google_compute_engine/instance_setup/instance_config.py b/google_compute_engine/instance_setup/instance_config.py
index 9d37875..f13351b 100644
--- a/google_compute_engine/instance_setup/instance_config.py
+++ b/google_compute_engine/instance_setup/instance_config.py
@@ -67,7 +67,7 @@ class InstanceConfig(config_manager.ConfigManager):
},
'NetworkInterfaces': {
'setup': 'true',
- 'dhcp_binary': 'dhclient',
+ 'dhcp_command': '',
},
}
diff --git a/google_compute_engine/network_setup/network_setup.py b/google_compute_engine/network_setup/network_setup.py
index 4c6e3a7..52c6399 100755
--- a/google_compute_engine/network_setup/network_setup.py
+++ b/google_compute_engine/network_setup/network_setup.py
@@ -30,14 +30,14 @@ class NetworkSetup(object):
network_interfaces = 'instance/network-interfaces'
- def __init__(self, dhcp_binary=None, debug=False):
+ def __init__(self, dhcp_command=None, debug=False):
"""Constructor.
Args:
- dhcp_binary: string, an executable to enable an Ethernet interface.
+ dhcp_command: string, a command to enable Ethernet interfaces.
debug: bool, True if debug output should write to the console.
"""
- self.dhcp_binary = dhcp_binary or 'dhclient'
+ self.dhcp_command = dhcp_command
facility = logging.handlers.SysLogHandler.LOG_DAEMON
self.logger = logger.Logger(
name='network-setup', debug=debug, facility=facility)
@@ -45,36 +45,47 @@ class NetworkSetup(object):
self.network_utils = network_utils.NetworkUtils(logger=self.logger)
self._SetupNetworkInterfaces()
- def _EnableNetworkInterface(self, interface):
- """Enable the network interface.
+ def _EnableNetworkInterfaces(self, interfaces):
+ """Enable the list of network interfaces.
Args:
- interface: string, the output device to enable.
+ interface: list of string, the output devices to enable.
"""
- if self.network_utils.IsEnabled(interface):
- return
-
- command = [self.dhcp_binary, interface]
try:
- self.logger.info('Enabling the Ethernet interface %s.', interface)
- subprocess.check_call(command)
+ self.logger.info('Enabling the Ethernet interface %s.', interfaces)
+ subprocess.check_call(['dhclient', '-r'] + interfaces)
+ subprocess.check_call(['dhclient'] + interfaces)
except subprocess.CalledProcessError:
- self.logger.warning('Could not enable the interface %s.', interface)
+ self.logger.warning('Could not enable interfaces %s.', interfaces)
def _SetupNetworkInterfaces(self):
"""Get network interfaces metadata and enable each Ethernet interface."""
result = self.watcher.GetMetadata(
metadata_key=self.network_interfaces, recursive=True)
+ interfaces = []
for network_interface in result:
mac_address = network_interface.get('mac')
interface = self.network_utils.GetNetworkInterface(mac_address)
if interface:
- self._EnableNetworkInterface(interface)
+ interfaces.append(interface)
else:
message = 'Network interface not found for MAC address: %s.'
self.logger.warning(message, mac_address)
+ # The default Ethernet interface is enabled by default. Do not attempt to
+ # enable interfaces if only one interface is specified in metadata.
+ if len(interfaces) <= 1:
+ return
+
+ if self.dhcp_command:
+ try:
+ subprocess.check_call([self.dhcp_command])
+ except subprocess.CalledProcessError:
+ self.logger.warning('Could not enable Ethernet interfaces.')
+ else:
+ self._EnableNetworkInterfaces(interfaces)
+
def main():
parser = optparse.OptionParser()
@@ -85,8 +96,8 @@ def main():
instance_config = config_manager.ConfigManager()
if instance_config.GetOptionBool('NetworkInterfaces', 'setup'):
NetworkSetup(
- dhcp_binary=instance_config.GetOptionString(
- 'NetworkInterfaces', 'dhcp_binary'),
+ dhcp_command=instance_config.GetOptionString(
+ 'NetworkInterfaces', 'dhcp_command'),
debug=bool(options.debug))
diff --git a/google_compute_engine/network_setup/tests/network_setup_test.py b/google_compute_engine/network_setup/tests/network_setup_test.py
index 12b2588..4c3f01e 100644
--- a/google_compute_engine/network_setup/tests/network_setup_test.py
+++ b/google_compute_engine/network_setup/tests/network_setup_test.py
@@ -30,14 +30,13 @@ class NetworkSetupTest(unittest.TestCase):
self.mock_ip_forwarding_utils = mock.Mock()
self.mock_network_utils = mock.Mock()
self.metadata_key = 'metadata_key'
- self.dhcp_binary = 'binary'
self.mock_setup = mock.create_autospec(network_setup.NetworkSetup)
self.mock_setup.logger = self.mock_logger
self.mock_setup.watcher = self.mock_watcher
self.mock_setup.network_utils = self.mock_network_utils
self.mock_setup.network_interfaces = self.metadata_key
- self.mock_setup.dhcp_binary = self.dhcp_binary
+ self.mock_setup.dhcp_command = ''
@mock.patch('google_compute_engine.network_setup.network_setup.network_utils')
@mock.patch('google_compute_engine.network_setup.network_setup.metadata_watcher')
@@ -51,7 +50,7 @@ class NetworkSetupTest(unittest.TestCase):
mocks.attach_mock(mock_network_utils, 'network')
with mock.patch.object(
network_setup.NetworkSetup, '_SetupNetworkInterfaces'):
- network_setup.NetworkSetup(dhcp_binary='binary', debug=True)
+ network_setup.NetworkSetup(debug=True)
expected_calls = [
mock.call.logger.Logger(name=mock.ANY, debug=True, facility=mock.ANY),
mock.call.watcher.MetadataWatcher(logger=mock_logger_instance),
@@ -60,30 +59,28 @@ class NetworkSetupTest(unittest.TestCase):
self.assertEqual(mocks.mock_calls, expected_calls)
@mock.patch('google_compute_engine.network_setup.network_setup.subprocess.check_call')
- def testEnableNetworkInterface(self, mock_call):
+ def testEnableNetworkInterfaces(self, mock_call):
mocks = mock.Mock()
mocks.attach_mock(mock_call, 'call')
mocks.attach_mock(self.mock_logger, 'logger')
mocks.attach_mock(self.mock_watcher, 'watcher')
mocks.attach_mock(self.mock_network_utils, 'network')
- mock_call.side_effect = [None, subprocess.CalledProcessError(1, 'Test')]
- self.mock_network_utils.IsEnabled.side_effect = [True, False, False]
+ mock_call.side_effect = [
+ None, None, subprocess.CalledProcessError(1, 'Test')]
- network_setup.NetworkSetup._EnableNetworkInterface(self.mock_setup, 'a')
- network_setup.NetworkSetup._EnableNetworkInterface(self.mock_setup, 'b')
- network_setup.NetworkSetup._EnableNetworkInterface(self.mock_setup, 'c')
+ network_setup.NetworkSetup._EnableNetworkInterfaces(
+ self.mock_setup, ['a', 'b', 'c'])
+ network_setup.NetworkSetup._EnableNetworkInterfaces(
+ self.mock_setup, [])
expected_calls = [
- # The network interface is already enabled.
- mock.call.network.IsEnabled('a'),
# Successfully enable the network interface.
- mock.call.network.IsEnabled('b'),
- mock.call.logger.info(mock.ANY, 'b'),
- mock.call.call([self.dhcp_binary, 'b']),
+ mock.call.logger.info(mock.ANY, ['a', 'b', 'c']),
+ mock.call.call(['dhclient', '-r', 'a', 'b', 'c']),
+ mock.call.call(['dhclient', 'a', 'b', 'c']),
# Exception while enabling the network interface.
- mock.call.network.IsEnabled('c'),
- mock.call.logger.info(mock.ANY, 'c'),
- mock.call.call([self.dhcp_binary, 'c']),
- mock.call.logger.warning(mock.ANY, 'c'),
+ mock.call.logger.info(mock.ANY, []),
+ mock.call.call(['dhclient', '-r']),
+ mock.call.logger.warning(mock.ANY, []),
]
self.assertEqual(mocks.mock_calls, expected_calls)
@@ -94,21 +91,67 @@ class NetworkSetupTest(unittest.TestCase):
mocks.attach_mock(self.mock_network_utils, 'network')
mocks.attach_mock(self.mock_setup, 'setup')
self.mock_watcher.GetMetadata.return_value = [
- {'mac': '1'}, {'mac': '2'}, {}]
+ {'mac': '1'}, {'mac': '2'}, {'mac': '3'}, {}]
self.mock_network_utils.GetNetworkInterface.side_effect = [
- 'eth0', None, None]
+ 'eth0', 'eth1', None, None]
with mock.patch.object(
- network_setup.NetworkSetup, '_EnableNetworkInterface'):
+ network_setup.NetworkSetup, '_EnableNetworkInterfaces'):
network_setup.NetworkSetup._SetupNetworkInterfaces(self.mock_setup)
expected_calls = [
mock.call.watcher.GetMetadata(
metadata_key=self.metadata_key, recursive=True),
mock.call.network.GetNetworkInterface('1'),
- mock.call.setup._EnableNetworkInterface('eth0'),
mock.call.network.GetNetworkInterface('2'),
- mock.call.logger.warning(mock.ANY, '2'),
+ mock.call.network.GetNetworkInterface('3'),
+ mock.call.logger.warning(mock.ANY, '3'),
mock.call.network.GetNetworkInterface(None),
mock.call.logger.warning(mock.ANY, None),
+ mock.call.setup._EnableNetworkInterfaces(['eth0', 'eth1']),
+ ]
+ self.assertEqual(mocks.mock_calls, expected_calls)
+
+ def testSetupNetworkInterfacesSkip(self):
+ mocks = mock.Mock()
+ mocks.attach_mock(self.mock_logger, 'logger')
+ mocks.attach_mock(self.mock_watcher, 'watcher')
+ mocks.attach_mock(self.mock_network_utils, 'network')
+ mocks.attach_mock(self.mock_setup, 'setup')
+ self.mock_watcher.GetMetadata.return_value = [{'mac': '1'}]
+
+ with mock.patch.object(
+ network_setup.NetworkSetup, '_EnableNetworkInterfaces'):
+ network_setup.NetworkSetup._SetupNetworkInterfaces(self.mock_setup)
+ expected_calls = [
+ mock.call.watcher.GetMetadata(
+ metadata_key=self.metadata_key, recursive=True),
+ mock.call.network.GetNetworkInterface('1'),
+ ]
+ self.assertEqual(mocks.mock_calls, expected_calls)
+
+ @mock.patch('google_compute_engine.network_setup.network_setup.subprocess.check_call')
+ def testSetupNetworkInterfacesCommand(self, mock_call):
+ mocks = mock.Mock()
+ mocks.attach_mock(mock_call, 'call')
+ mocks.attach_mock(self.mock_logger, 'logger')
+ mocks.attach_mock(self.mock_watcher, 'watcher')
+ mocks.attach_mock(self.mock_network_utils, 'network')
+ mocks.attach_mock(self.mock_setup, 'setup')
+ self.mock_watcher.GetMetadata.return_value = [
+ {'mac': '1'}, {'mac': '2'}]
+ self.mock_network_utils.GetNetworkInterface.side_effect = ['eth0', 'eth1']
+ mock_call.side_effect = subprocess.CalledProcessError(1, 'Test')
+
+ with mock.patch.object(
+ network_setup.NetworkSetup, '_EnableNetworkInterfaces'):
+ self.mock_setup.dhcp_command = 'command'
+ network_setup.NetworkSetup._SetupNetworkInterfaces(self.mock_setup)
+ expected_calls = [
+ mock.call.watcher.GetMetadata(
+ metadata_key=self.metadata_key, recursive=True),
+ mock.call.network.GetNetworkInterface('1'),
+ mock.call.network.GetNetworkInterface('2'),
+ mock.call.call(['command']),
+ mock.call.logger.warning(mock.ANY),
]
self.assertEqual(mocks.mock_calls, expected_calls)
diff --git a/google_compute_engine/network_utils.py b/google_compute_engine/network_utils.py
index 3c94deb..3ed36bb 100644
--- a/google_compute_engine/network_utils.py
+++ b/google_compute_engine/network_utils.py
@@ -59,21 +59,3 @@ class NetworkUtils(object):
string, the network interface associated with a MAC address or None.
"""
return self.interfaces.get(mac_address)
-
- def IsEnabled(self, interface):
- """Check whether a network interface is enabled.
-
- Args:
- interface: string, the network interface.
-
- Returns:
- bool, True if the network interface is enabled.
- """
- try:
- state = open('/sys/class/net/%s/operstate' % interface).read().strip()
- except (IOError, OSError) as e:
- message = 'Unable to determine the state for %s. %s.'
- self.logger.warning(message, interface, str(e))
- return False
- else:
- return 'up' == state
diff --git a/google_compute_engine/tests/network_utils_test.py b/google_compute_engine/tests/network_utils_test.py
index 1c0cb7c..60386b3 100644
--- a/google_compute_engine/tests/network_utils_test.py
+++ b/google_compute_engine/tests/network_utils_test.py
@@ -64,23 +64,3 @@ class NetworkUtilsTest(unittest.TestCase):
self.assertIsNone(self.mock_utils.GetNetworkInterface('invalid'))
self.assertEqual(
self.mock_utils.GetNetworkInterface('address'), 'interface')
-
- def testIsEnabled(self):
- mock_open = mock.mock_open()
-
- with mock.patch('%s.open' % builtin, mock_open, create=False):
- mock_open().read.side_effect = ['up', 'down', 'up\n', '', 'Garbage']
- self.assertEqual(self.mock_utils.IsEnabled('a'), True)
- self.assertEqual(self.mock_utils.IsEnabled('a'), False)
- self.assertEqual(self.mock_utils.IsEnabled('a'), True)
- self.assertEqual(self.mock_utils.IsEnabled('a'), False)
- self.assertEqual(self.mock_utils.IsEnabled('a'), False)
-
- def testIsEnabledError(self):
- mock_open = mock.mock_open()
-
- with mock.patch('%s.open' % builtin, mock_open, create=False):
- mock_open().read.side_effect = [OSError('OSError')]
- self.assertEqual(self.mock_utils.IsEnabled('a'), False)
- self.mock_logger.warning.assert_called_once_with(
- mock.ANY, 'a', 'OSError'),
diff --git a/google_compute_engine_init/build_packages.sh b/google_compute_engine_init/build_packages.sh
index ce119ad..4d8c20b 100755
--- a/google_compute_engine_init/build_packages.sh
+++ b/google_compute_engine_init/build_packages.sh
@@ -13,7 +13,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-TIMESTAMP="$(date +%s)"
+#/ Usage: build_packages.sh [options]
+#/
+#/ Build the package that contains init configuration for the
+#/ google-compute-engine Python package.
+#/
+#/ OPTIONS:
+#/ -h Show this message
+#/ -o DISTRO,... Build only specified distros
+
+function usage() {
+ grep '^#/' < "$0" | cut -c 4-
+}
function build_distro() {
declare -r distro="$1"
@@ -63,10 +74,47 @@ function build_distro() {
"${init_files[@]}"
}
-# RHEL/CentOS
-build_distro 'el6' 'rpm' 'upstart' '/etc/init'
-build_distro 'el7' 'rpm' 'systemd' '/usr/lib/systemd/system'
+TIMESTAMP="$(date +%s)"
+
+while getopts 'ho:' OPTION; do
+ case "$OPTION" in
+ h)
+ usage
+ exit 2
+ ;;
+ o)
+ set -f
+ IFS=','
+ BUILD=($OPTARG)
+ set +f
+ ;;
+ ?)
+ usage
+ exit
+ ;;
+ esac
+done
+
+if [ -z "$BUILD" ]; then
+ BUILD=('el6' 'el7' 'wheezy' 'jessie')
+fi
-# Debian
-build_distro 'wheezy' 'deb' 'sysvinit' '/etc/init.d'
-build_distro 'jessie' 'deb' 'systemd' '/usr/lib/systemd/system'
+for build in "${BUILD[@]}"; do
+ case "$build" in
+ el6) # RHEL/CentOS 6
+ build_distro 'el6' 'rpm' 'upstart' '/etc/init'
+ ;;
+ el7) # RHEL/CentOS 7
+ build_distro 'el7' 'rpm' 'systemd' '/usr/lib/systemd/system'
+ ;;
+ wheezy) # Debian 7
+ build_distro 'wheezy' 'deb' 'sysvinit' '/etc/init.d'
+ ;;
+ jessie) # Debian 8
+ build_distro 'jessie' 'deb' 'systemd' '/usr/lib/systemd/system'
+ ;;
+ *)
+ echo "Invalid build '${build}'. Use 'el6', 'el7', 'wheezy', or 'jessie'."
+ ;;
+ esac
+done
diff --git a/google_config/build_packages.sh b/google_config/build_packages.sh
index af35e7d..00acc2a 100755
--- a/google_config/build_packages.sh
+++ b/google_config/build_packages.sh
@@ -13,12 +13,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-COMMON_FILES=(
- 'rsyslog/90-google.conf=/etc/rsyslog.d/90-google.conf'
- 'sysctl/11-gce-network-security.conf=/etc/sysctl.d/11-gce-network-security.conf'
- 'udev/64-gce-disk-removal.rules=/etc/udev/rules.d/64-gce-disk-removal.rules'
- 'udev/65-gce-disk-naming.rules=/etc/udev/rules.d/65-gce-disk-naming.rules')
-TIMESTAMP="$(date +%s)"
+#/ Usage: build_packages.sh [options]
+#/
+#/ Build the package containing non-Python scripts and guest configuration.
+#/
+#/ OPTIONS:
+#/ -h Show this message
+#/ -o DISTRO,... Build only specified distros
+
+function usage() {
+ grep '^#/' < "$0" | cut -c 4-
+}
function build_distro() {
declare -r distro="$1"
@@ -46,19 +51,57 @@ function build_distro() {
"${files[@]:2}"
}
-# RHEL/CentOS 6
-build_distro 'el6' 'rpm' \
- 'bin/set_hostname=/etc/dhcp/dhclient-exit-hooks'
+COMMON_FILES=(
+ 'rsyslog/90-google.conf=/etc/rsyslog.d/90-google.conf'
+ 'sysctl/11-gce-network-security.conf=/etc/sysctl.d/11-gce-network-security.conf'
+ 'udev/64-gce-disk-removal.rules=/etc/udev/rules.d/64-gce-disk-removal.rules'
+ 'udev/65-gce-disk-naming.rules=/etc/udev/rules.d/65-gce-disk-naming.rules')
+TIMESTAMP="$(date +%s)"
-# RHEL/CentOS 7
-build_distro 'el7' 'rpm' \
- 'bin/set_hostname=/usr/bin/set_hostname' \
- 'dhcp/google_hostname.sh=/etc/dhcp/dhclient.d/google_hostname.sh'
+while getopts 'ho:' OPTION; do
+ case "$OPTION" in
+ h)
+ usage
+ exit 2
+ ;;
+ o)
+ set -f
+ IFS=','
+ BUILD=($OPTARG)
+ set +f
+ ;;
+ ?)
+ usage
+ exit
+ ;;
+ esac
+done
-# Debian 7
-build_distro 'wheezy' 'deb' \
- 'bin/set_hostname=/etc/dhcp/dhclient-exit-hooks.d/set_hostname'
+if [ -z "$BUILD" ]; then
+ BUILD=('el6' 'el7' 'wheezy' 'jessie')
+fi
-# Debian 8
-build_distro 'jessie' 'deb' \
- 'bin/set_hostname=/etc/dhcp/dhclient-exit-hooks.d/set_hostname'
+for build in "${BUILD[@]}"; do
+ case "$build" in
+ el6) # RHEL/CentOS 6
+ build_distro 'el6' 'rpm' \
+ 'bin/set_hostname=/etc/dhcp/dhclient-exit-hooks'
+ ;;
+ el7) # RHEL/CentOS 7
+ build_distro 'el7' 'rpm' \
+ 'bin/set_hostname=/usr/bin/set_hostname' \
+ 'dhcp/google_hostname.sh=/etc/dhcp/dhclient.d/google_hostname.sh'
+ ;;
+ wheezy) # Debian 7
+ build_distro 'wheezy' 'deb' \
+ 'bin/set_hostname=/etc/dhcp/dhclient-exit-hooks.d/set_hostname'
+ ;;
+ jessie) # Debian 8
+ build_distro 'jessie' 'deb' \
+ 'bin/set_hostname=/etc/dhcp/dhclient-exit-hooks.d/set_hostname'
+ ;;
+ *)
+ echo "Invalid build '${build}'. Use 'el6', 'el7', 'wheezy', or 'jessie'."
+ ;;
+ esac
+done
diff --git a/setup.py b/setup.py
index e8459f4..a2d650d 100755
--- a/setup.py
+++ b/setup.py
@@ -32,7 +32,7 @@ setuptools.setup(
packages=setuptools.find_packages(),
scripts=glob.glob('scripts/*'),
url='https://github.com/GoogleCloudPlatform/compute-image-packages',
- version='2.2.2',
+ version='2.2.3',
# Entry points create scripts in /usr/bin that call a function.
entry_points={
'console_scripts': [