From 17b97ab3d27b766e64b8868019b7914c97d08fe1 Mon Sep 17 00:00:00 2001 From: Michael Hanselmann <hansmi@google.com> Date: Tue, 11 Jan 2011 16:01:48 +0100 Subject: [PATCH] utils: Move code manipulating /etc/hosts to separate file Signed-off-by: Michael Hanselmann <hansmi@google.com> Reviewed-by: Iustin Pop <iustin@google.com> --- Makefile.am | 2 + lib/utils/__init__.py | 101 +----------------- lib/utils/nodesetup.py | 130 ++++++++++++++++++++++++ test/ganeti.utils.nodesetup_unittest.py | 116 +++++++++++++++++++++ test/ganeti.utils_unittest.py | 84 +-------------- 5 files changed, 250 insertions(+), 183 deletions(-) create mode 100644 lib/utils/nodesetup.py create mode 100755 test/ganeti.utils.nodesetup_unittest.py diff --git a/Makefile.am b/Makefile.am index 9f261b434..35c0a20c0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -219,6 +219,7 @@ utils_PYTHON = \ lib/utils/io.py \ lib/utils/log.py \ lib/utils/mlock.py \ + lib/utils/nodesetup.py \ lib/utils/retry.py \ lib/utils/text.py \ lib/utils/wrapper.py \ @@ -493,6 +494,7 @@ python_tests = \ test/ganeti.utils.hash_unittest.py \ test/ganeti.utils.io_unittest.py \ test/ganeti.utils.mlock_unittest.py \ + test/ganeti.utils.nodesetup_unittest.py \ test/ganeti.utils.retry_unittest.py \ test/ganeti.utils.text_unittest.py \ test/ganeti.utils.wrapper_unittest.py \ diff --git a/lib/utils/__init__.py b/lib/utils/__init__.py index 582b4ef49..23d8a25be 100644 --- a/lib/utils/__init__.py +++ b/lib/utils/__init__.py @@ -62,6 +62,7 @@ from ganeti.utils.wrapper import * # pylint: disable-msg=W0401 from ganeti.utils.filelock import * # pylint: disable-msg=W0401 from ganeti.utils.io import * # pylint: disable-msg=W0401 from ganeti.utils.x509 import * # pylint: disable-msg=W0401 +from ganeti.utils.nodesetup import * # pylint: disable-msg=W0401 #: when set to True, L{RunCmd} is disabled @@ -1076,106 +1077,6 @@ def ParseCpuMask(cpu_mask): return cpu_list -def SetEtcHostsEntry(file_name, ip, hostname, aliases): - """Sets the name of an IP address and hostname in /etc/hosts. - - @type file_name: str - @param file_name: path to the file to modify (usually C{/etc/hosts}) - @type ip: str - @param ip: the IP address - @type hostname: str - @param hostname: the hostname to be added - @type aliases: list - @param aliases: the list of aliases to add for the hostname - - """ - # Ensure aliases are unique - aliases = UniqueSequence([hostname] + aliases)[1:] - - def _WriteEtcHosts(fd): - # Duplicating file descriptor because os.fdopen's result will automatically - # close the descriptor, but we would still like to have its functionality. - out = os.fdopen(os.dup(fd), "w") - try: - for line in ReadFile(file_name).splitlines(True): - fields = line.split() - if fields and not fields[0].startswith("#") and ip == fields[0]: - continue - out.write(line) - - out.write("%s\t%s" % (ip, hostname)) - if aliases: - out.write(" %s" % " ".join(aliases)) - out.write("\n") - out.flush() - finally: - out.close() - - WriteFile(file_name, fn=_WriteEtcHosts, mode=0644) - - -def AddHostToEtcHosts(hostname, ip): - """Wrapper around SetEtcHostsEntry. - - @type hostname: str - @param hostname: a hostname that will be resolved and added to - L{constants.ETC_HOSTS} - @type ip: str - @param ip: The ip address of the host - - """ - SetEtcHostsEntry(constants.ETC_HOSTS, ip, hostname, [hostname.split(".")[0]]) - - -def RemoveEtcHostsEntry(file_name, hostname): - """Removes a hostname from /etc/hosts. - - IP addresses without names are removed from the file. - - @type file_name: str - @param file_name: path to the file to modify (usually C{/etc/hosts}) - @type hostname: str - @param hostname: the hostname to be removed - - """ - def _WriteEtcHosts(fd): - # Duplicating file descriptor because os.fdopen's result will automatically - # close the descriptor, but we would still like to have its functionality. - out = os.fdopen(os.dup(fd), "w") - try: - for line in ReadFile(file_name).splitlines(True): - fields = line.split() - if len(fields) > 1 and not fields[0].startswith("#"): - names = fields[1:] - if hostname in names: - while hostname in names: - names.remove(hostname) - if names: - out.write("%s %s\n" % (fields[0], " ".join(names))) - continue - - out.write(line) - - out.flush() - finally: - out.close() - - WriteFile(file_name, fn=_WriteEtcHosts, mode=0644) - - -def RemoveHostFromEtcHosts(hostname): - """Wrapper around RemoveEtcHostsEntry. - - @type hostname: str - @param hostname: hostname that will be resolved and its - full and shot name will be removed from - L{constants.ETC_HOSTS} - - """ - RemoveEtcHostsEntry(constants.ETC_HOSTS, hostname) - RemoveEtcHostsEntry(constants.ETC_HOSTS, hostname.split(".")[0]) - - def GetHomeDir(user, default=None): """Try to get the homedir of the given user. diff --git a/lib/utils/nodesetup.py b/lib/utils/nodesetup.py new file mode 100644 index 000000000..80e2b9807 --- /dev/null +++ b/lib/utils/nodesetup.py @@ -0,0 +1,130 @@ +# +# + +# Copyright (C) 2006, 2007, 2010, 2011 Google Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +"""Utility functions for manipulating /etc/hosts. + +""" + +import os + +from ganeti import constants + +from ganeti.utils import algo +from ganeti.utils import io + + +def SetEtcHostsEntry(file_name, ip, hostname, aliases): + """Sets the name of an IP address and hostname in /etc/hosts. + + @type file_name: str + @param file_name: path to the file to modify (usually C{/etc/hosts}) + @type ip: str + @param ip: the IP address + @type hostname: str + @param hostname: the hostname to be added + @type aliases: list + @param aliases: the list of aliases to add for the hostname + + """ + # Ensure aliases are unique + aliases = algo.UniqueSequence([hostname] + aliases)[1:] + + def _WriteEtcHosts(fd): + # Duplicating file descriptor because os.fdopen's result will automatically + # close the descriptor, but we would still like to have its functionality. + out = os.fdopen(os.dup(fd), "w") + try: + for line in io.ReadFile(file_name).splitlines(True): + fields = line.split() + if fields and not fields[0].startswith("#") and ip == fields[0]: + continue + out.write(line) + + out.write("%s\t%s" % (ip, hostname)) + if aliases: + out.write(" %s" % " ".join(aliases)) + out.write("\n") + out.flush() + finally: + out.close() + + io.WriteFile(file_name, fn=_WriteEtcHosts, mode=0644) + + +def AddHostToEtcHosts(hostname, ip): + """Wrapper around SetEtcHostsEntry. + + @type hostname: str + @param hostname: a hostname that will be resolved and added to + L{constants.ETC_HOSTS} + @type ip: str + @param ip: The ip address of the host + + """ + SetEtcHostsEntry(constants.ETC_HOSTS, ip, hostname, [hostname.split(".")[0]]) + + +def RemoveEtcHostsEntry(file_name, hostname): + """Removes a hostname from /etc/hosts. + + IP addresses without names are removed from the file. + + @type file_name: str + @param file_name: path to the file to modify (usually C{/etc/hosts}) + @type hostname: str + @param hostname: the hostname to be removed + + """ + def _WriteEtcHosts(fd): + # Duplicating file descriptor because os.fdopen's result will automatically + # close the descriptor, but we would still like to have its functionality. + out = os.fdopen(os.dup(fd), "w") + try: + for line in io.ReadFile(file_name).splitlines(True): + fields = line.split() + if len(fields) > 1 and not fields[0].startswith("#"): + names = fields[1:] + if hostname in names: + while hostname in names: + names.remove(hostname) + if names: + out.write("%s %s\n" % (fields[0], " ".join(names))) + continue + + out.write(line) + + out.flush() + finally: + out.close() + + io.WriteFile(file_name, fn=_WriteEtcHosts, mode=0644) + + +def RemoveHostFromEtcHosts(hostname): + """Wrapper around RemoveEtcHostsEntry. + + @type hostname: str + @param hostname: hostname that will be resolved and its + full and shot name will be removed from + L{constants.ETC_HOSTS} + + """ + RemoveEtcHostsEntry(constants.ETC_HOSTS, hostname) + RemoveEtcHostsEntry(constants.ETC_HOSTS, hostname.split(".")[0]) diff --git a/test/ganeti.utils.nodesetup_unittest.py b/test/ganeti.utils.nodesetup_unittest.py new file mode 100755 index 000000000..3d386a465 --- /dev/null +++ b/test/ganeti.utils.nodesetup_unittest.py @@ -0,0 +1,116 @@ +#!/usr/bin/python +# + +# Copyright (C) 2006, 2007, 2010 Google Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + + +"""Script for testing ganeti.utils.nodesetup""" + +import os +import tempfile +import unittest + +from ganeti import constants +from ganeti import utils + +import testutils + + +class TestEtcHosts(testutils.GanetiTestCase): + """Test functions modifying /etc/hosts""" + + def setUp(self): + testutils.GanetiTestCase.setUp(self) + self.tmpname = self._CreateTempFile() + handle = open(self.tmpname, "w") + try: + handle.write("# This is a test file for /etc/hosts\n") + handle.write("127.0.0.1\tlocalhost\n") + handle.write("192.0.2.1 router gw\n") + finally: + handle.close() + + def testSettingNewIp(self): + utils.SetEtcHostsEntry(self.tmpname, "198.51.100.4", "myhost.example.com", + ["myhost"]) + + self.assertFileContent(self.tmpname, + "# This is a test file for /etc/hosts\n" + "127.0.0.1\tlocalhost\n" + "192.0.2.1 router gw\n" + "198.51.100.4\tmyhost.example.com myhost\n") + self.assertFileMode(self.tmpname, 0644) + + def testSettingExistingIp(self): + utils.SetEtcHostsEntry(self.tmpname, "192.0.2.1", "myhost.example.com", + ["myhost"]) + + self.assertFileContent(self.tmpname, + "# This is a test file for /etc/hosts\n" + "127.0.0.1\tlocalhost\n" + "192.0.2.1\tmyhost.example.com myhost\n") + self.assertFileMode(self.tmpname, 0644) + + def testSettingDuplicateName(self): + utils.SetEtcHostsEntry(self.tmpname, "198.51.100.4", "myhost", ["myhost"]) + + self.assertFileContent(self.tmpname, + "# This is a test file for /etc/hosts\n" + "127.0.0.1\tlocalhost\n" + "192.0.2.1 router gw\n" + "198.51.100.4\tmyhost\n") + self.assertFileMode(self.tmpname, 0644) + + def testRemovingExistingHost(self): + utils.RemoveEtcHostsEntry(self.tmpname, "router") + + self.assertFileContent(self.tmpname, + "# This is a test file for /etc/hosts\n" + "127.0.0.1\tlocalhost\n" + "192.0.2.1 gw\n") + self.assertFileMode(self.tmpname, 0644) + + def testRemovingSingleExistingHost(self): + utils.RemoveEtcHostsEntry(self.tmpname, "localhost") + + self.assertFileContent(self.tmpname, + "# This is a test file for /etc/hosts\n" + "192.0.2.1 router gw\n") + self.assertFileMode(self.tmpname, 0644) + + def testRemovingNonExistingHost(self): + utils.RemoveEtcHostsEntry(self.tmpname, "myhost") + + self.assertFileContent(self.tmpname, + "# This is a test file for /etc/hosts\n" + "127.0.0.1\tlocalhost\n" + "192.0.2.1 router gw\n") + self.assertFileMode(self.tmpname, 0644) + + def testRemovingAlias(self): + utils.RemoveEtcHostsEntry(self.tmpname, "gw") + + self.assertFileContent(self.tmpname, + "# This is a test file for /etc/hosts\n" + "127.0.0.1\tlocalhost\n" + "192.0.2.1 router\n") + self.assertFileMode(self.tmpname, 0644) + + +if __name__ == "__main__": + testutils.GanetiTestProgram() diff --git a/test/ganeti.utils_unittest.py b/test/ganeti.utils_unittest.py index 1d5805767..ea136f3b5 100755 --- a/test/ganeti.utils_unittest.py +++ b/test/ganeti.utils_unittest.py @@ -45,8 +45,7 @@ from ganeti import utils from ganeti import errors from ganeti.utils import RunCmd, \ FirstFree, \ - RunParts, \ - SetEtcHostsEntry, RemoveEtcHostsEntry + RunParts class TestIsProcessAlive(unittest.TestCase): @@ -575,87 +574,6 @@ class TestParseCpuMask(unittest.TestCase): self.assertRaises(errors.ParseError, utils.ParseCpuMask, data) -class TestEtcHosts(testutils.GanetiTestCase): - """Test functions modifying /etc/hosts""" - - def setUp(self): - testutils.GanetiTestCase.setUp(self) - self.tmpname = self._CreateTempFile() - handle = open(self.tmpname, 'w') - try: - handle.write('# This is a test file for /etc/hosts\n') - handle.write('127.0.0.1\tlocalhost\n') - handle.write('192.0.2.1 router gw\n') - finally: - handle.close() - - def testSettingNewIp(self): - SetEtcHostsEntry(self.tmpname, '198.51.100.4', 'myhost.example.com', - ['myhost']) - - self.assertFileContent(self.tmpname, - "# This is a test file for /etc/hosts\n" - "127.0.0.1\tlocalhost\n" - "192.0.2.1 router gw\n" - "198.51.100.4\tmyhost.example.com myhost\n") - self.assertFileMode(self.tmpname, 0644) - - def testSettingExistingIp(self): - SetEtcHostsEntry(self.tmpname, '192.0.2.1', 'myhost.example.com', - ['myhost']) - - self.assertFileContent(self.tmpname, - "# This is a test file for /etc/hosts\n" - "127.0.0.1\tlocalhost\n" - "192.0.2.1\tmyhost.example.com myhost\n") - self.assertFileMode(self.tmpname, 0644) - - def testSettingDuplicateName(self): - SetEtcHostsEntry(self.tmpname, '198.51.100.4', 'myhost', ['myhost']) - - self.assertFileContent(self.tmpname, - "# This is a test file for /etc/hosts\n" - "127.0.0.1\tlocalhost\n" - "192.0.2.1 router gw\n" - "198.51.100.4\tmyhost\n") - self.assertFileMode(self.tmpname, 0644) - - def testRemovingExistingHost(self): - RemoveEtcHostsEntry(self.tmpname, 'router') - - self.assertFileContent(self.tmpname, - "# This is a test file for /etc/hosts\n" - "127.0.0.1\tlocalhost\n" - "192.0.2.1 gw\n") - self.assertFileMode(self.tmpname, 0644) - - def testRemovingSingleExistingHost(self): - RemoveEtcHostsEntry(self.tmpname, 'localhost') - - self.assertFileContent(self.tmpname, - "# This is a test file for /etc/hosts\n" - "192.0.2.1 router gw\n") - self.assertFileMode(self.tmpname, 0644) - - def testRemovingNonExistingHost(self): - RemoveEtcHostsEntry(self.tmpname, 'myhost') - - self.assertFileContent(self.tmpname, - "# This is a test file for /etc/hosts\n" - "127.0.0.1\tlocalhost\n" - "192.0.2.1 router gw\n") - self.assertFileMode(self.tmpname, 0644) - - def testRemovingAlias(self): - RemoveEtcHostsEntry(self.tmpname, 'gw') - - self.assertFileContent(self.tmpname, - "# This is a test file for /etc/hosts\n" - "127.0.0.1\tlocalhost\n" - "192.0.2.1 router\n") - self.assertFileMode(self.tmpname, 0644) - - class TestGetMounts(unittest.TestCase): """Test case for GetMounts().""" -- GitLab