Merge branch 'devel-2.1'
authorMichael Hanselmann <hansmi@google.com>
Mon, 17 May 2010 11:00:07 +0000 (13:00 +0200)
committerMichael Hanselmann <hansmi@google.com>
Mon, 17 May 2010 11:17:23 +0000 (13:17 +0200)
* devel-2.1:
  RAPI: /2/{nodes,instances}/$name should return 404 for unknown items
  ganeti-masterd: Improve error logging for client requests
  Return disk_template from LUQueryInstanceData
  RAPI client: Rename Get{Node,Instance}Info, add new GetInstanceInfo
  RAPI client: Log request to be made
  Add missing documentation for RAPI instance creation mode
  Add checks for master IP in cluster verify
  Remove unused import from daemon.py
  utils.IgnoreSignals
  AsyncUDPSocket.handle_error
  Add a forgotten comment about overriding a method
  ganeti-noded: add the --no-mlock option
  Describe more ganeti-noded options in the manpage

Conflicts:
daemons/ganeti-masterd: Trivial
test/ganeti.backend_unittest.py: Trivial
test/ganeti.utils_unittest.py: Trivial

Signed-off-by: Michael Hanselmann <hansmi@google.com>
Reviewed-by: Iustin Pop <iustin@google.com>

1  2 
daemons/ganeti-masterd
daemons/ganeti-noded
lib/backend.py
lib/cmdlib.py
lib/constants.py
lib/daemon.py
lib/rapi/baserlib.py
lib/rapi/rlib2.py
lib/utils.py
test/ganeti.backend_unittest.py
test/ganeti.utils_unittest.py

@@@ -170,12 -181,20 +170,13 @@@ class ClientRqHandler(SocketServer.Base
          result = self._ops.handle_request(method, args)
          success = True
        except errors.GenericError, err:
+         logging.exception("Unexpected exception")
 -        success = False
          result = errors.EncodeException(err)
        except:
-         logging.error("Unexpected exception", exc_info=True)
+         logging.exception("Unexpected exception")
 -        err = sys.exc_info()
 -        result = "Caught exception: %s" % str(err[1])
 +        result = "Caught exception: %s" % str(sys.exc_info()[1])
  
 -      response = {
 -        luxi.KEY_SUCCESS: success,
 -        luxi.KEY_RESULT: result,
 -        }
 -      logging.debug("response: %s", response)
 -      self.send_message(serializer.DumpJson(response))
 +      self.send_message(luxi.FormatResponse(success, result))
  
    def read_message(self):
      while not self._msgs:
Simple merge
diff --cc lib/backend.py
Simple merge
diff --cc lib/cmdlib.py
Simple merge
Simple merge
diff --cc lib/daemon.py
Simple merge
Simple merge
Simple merge
diff --cc lib/utils.py
Simple merge
index f1aae63,2c94a75..756aed9
mode 100755,100644..100755
  # 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
 -# 0.0510-1301, USA.
 +# 02110-1301, USA.
  
  
 -"""Script for unittesting the backend module"""
 -
 +"""Script for testing ganeti.backend"""
  
  import os
 -import unittest
 -import time
 -import tempfile
 +import sys
  import shutil
 +import tempfile
 +import unittest
  
 -from ganeti import backend
 -from ganeti import constants
  from ganeti import utils
++from ganeti import constants
 +from ganeti import backend
  
  import testutils
  
  
 +class TestX509Certificates(unittest.TestCase):
 +  def setUp(self):
 +    self.tmpdir = tempfile.mkdtemp()
 +
 +  def tearDown(self):
 +    shutil.rmtree(self.tmpdir)
 +
 +  def test(self):
 +    (name, cert_pem) = backend.CreateX509Certificate(300, cryptodir=self.tmpdir)
 +
 +    self.assertEqual(utils.ReadFile(os.path.join(self.tmpdir, name,
 +                                                 backend._X509_CERT_FILE)),
 +                     cert_pem)
 +    self.assert_(0 < os.path.getsize(os.path.join(self.tmpdir, name,
 +                                                  backend._X509_KEY_FILE)))
 +
 +    (name2, cert_pem2) = \
 +      backend.CreateX509Certificate(300, cryptodir=self.tmpdir)
 +
 +    backend.RemoveX509Certificate(name, cryptodir=self.tmpdir)
 +    backend.RemoveX509Certificate(name2, cryptodir=self.tmpdir)
 +
 +    self.assertEqual(utils.ListVisibleFiles(self.tmpdir), [])
 +
 +  def testNonEmpty(self):
 +    (name, _) = backend.CreateX509Certificate(300, cryptodir=self.tmpdir)
 +
 +    utils.WriteFile(utils.PathJoin(self.tmpdir, name, "hello-world"),
 +                    data="Hello World")
 +
 +    self.assertRaises(backend.RPCFail, backend.RemoveX509Certificate,
 +                      name, cryptodir=self.tmpdir)
 +
 +    self.assertEqual(utils.ListVisibleFiles(self.tmpdir), [name])
 +
 +
+ class TestNodeVerify(testutils.GanetiTestCase):
+   def testMasterIPLocalhost(self):
+     # this a real functional test, but requires localhost to be reachable
+     local_data = (utils.HostInfo().name, constants.LOCALHOST_IP_ADDRESS)
+     result = backend.VerifyNode({constants.NV_MASTERIP: local_data}, None)
+     self.failUnless(constants.NV_MASTERIP in result,
+                     "Master IP data not returned")
+     self.failUnless(result[constants.NV_MASTERIP], "Cannot reach localhost")
+   def testMasterIPUnreachable(self):
+     # Network 192.0.2.0/24 is reserved for test/documentation as per
+     # RFC 5735
+     bad_data =  ("master.example.com", "192.0.2.1")
+     # we just test that whatever TcpPing returns, VerifyNode returns too
+     utils.TcpPing = lambda a, b, source=None: False
+     result = backend.VerifyNode({constants.NV_MASTERIP: bad_data}, None)
+     self.failUnless(constants.NV_MASTERIP in result,
+                     "Master IP data not returned")
+     self.failIf(result[constants.NV_MASTERIP],
+                 "Result from utils.TcpPing corrupted")
  if __name__ == "__main__":
    testutils.GanetiTestProgram()
@@@ -2070,145 -1823,41 +2071,181 @@@ class TestLineSplitter(unittest.TestCas
                               "", "x"])
  
  
 +class TestReadLockedPidFile(unittest.TestCase):
 +  def setUp(self):
 +    self.tmpdir = tempfile.mkdtemp()
 +
 +  def tearDown(self):
 +    shutil.rmtree(self.tmpdir)
 +
 +  def testNonExistent(self):
 +    path = utils.PathJoin(self.tmpdir, "nonexist")
 +    self.assert_(utils.ReadLockedPidFile(path) is None)
 +
 +  def testUnlocked(self):
 +    path = utils.PathJoin(self.tmpdir, "pid")
 +    utils.WriteFile(path, data="123")
 +    self.assert_(utils.ReadLockedPidFile(path) is None)
 +
 +  def testLocked(self):
 +    path = utils.PathJoin(self.tmpdir, "pid")
 +    utils.WriteFile(path, data="123")
 +
 +    fl = utils.FileLock.Open(path)
 +    try:
 +      fl.Exclusive(blocking=True)
 +
 +      self.assertEqual(utils.ReadLockedPidFile(path), 123)
 +    finally:
 +      fl.Close()
 +
 +    self.assert_(utils.ReadLockedPidFile(path) is None)
 +
 +  def testError(self):
 +    path = utils.PathJoin(self.tmpdir, "foobar", "pid")
 +    utils.WriteFile(utils.PathJoin(self.tmpdir, "foobar"), data="")
 +    # open(2) should return ENOTDIR
 +    self.assertRaises(EnvironmentError, utils.ReadLockedPidFile, path)
 +
 +
 +class TestCertVerification(testutils.GanetiTestCase):
 +  def setUp(self):
 +    testutils.GanetiTestCase.setUp(self)
 +
 +    self.tmpdir = tempfile.mkdtemp()
 +
 +  def tearDown(self):
 +    shutil.rmtree(self.tmpdir)
 +
 +  def testVerifyCertificate(self):
 +    cert_pem = utils.ReadFile(self._TestDataFilename("cert1.pem"))
 +    cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
 +                                           cert_pem)
 +
 +    # Not checking return value as this certificate is expired
 +    utils.VerifyX509Certificate(cert, 30, 7)
 +
 +
 +class TestVerifyCertificateInner(unittest.TestCase):
 +  def test(self):
 +    vci = utils._VerifyCertificateInner
 +
 +    # Valid
 +    self.assertEqual(vci(False, 1263916313, 1298476313, 1266940313, 30, 7),
 +                     (None, None))
 +
 +    # Not yet valid
 +    (errcode, msg) = vci(False, 1266507600, 1267544400, 1266075600, 30, 7)
 +    self.assertEqual(errcode, utils.CERT_WARNING)
 +
 +    # Expiring soon
 +    (errcode, msg) = vci(False, 1266507600, 1267544400, 1266939600, 30, 7)
 +    self.assertEqual(errcode, utils.CERT_ERROR)
 +
 +    (errcode, msg) = vci(False, 1266507600, 1267544400, 1266939600, 30, 1)
 +    self.assertEqual(errcode, utils.CERT_WARNING)
 +
 +    (errcode, msg) = vci(False, 1266507600, None, 1266939600, 30, 7)
 +    self.assertEqual(errcode, None)
 +
 +    # Expired
 +    (errcode, msg) = vci(True, 1266507600, 1267544400, 1266939600, 30, 7)
 +    self.assertEqual(errcode, utils.CERT_ERROR)
 +
 +    (errcode, msg) = vci(True, None, 1267544400, 1266939600, 30, 7)
 +    self.assertEqual(errcode, utils.CERT_ERROR)
 +
 +    (errcode, msg) = vci(True, 1266507600, None, 1266939600, 30, 7)
 +    self.assertEqual(errcode, utils.CERT_ERROR)
 +
 +    (errcode, msg) = vci(True, None, None, 1266939600, 30, 7)
 +    self.assertEqual(errcode, utils.CERT_ERROR)
 +
 +
 +class TestHmacFunctions(unittest.TestCase):
 +  # Digests can be checked with "openssl sha1 -hmac $key"
 +  def testSha1Hmac(self):
 +    self.assertEqual(utils.Sha1Hmac("", ""),
 +                     "fbdb1d1b18aa6c08324b7d64b71fb76370690e1d")
 +    self.assertEqual(utils.Sha1Hmac("3YzMxZWE", "Hello World"),
 +                     "ef4f3bda82212ecb2f7ce868888a19092481f1fd")
 +    self.assertEqual(utils.Sha1Hmac("TguMTA2K", ""),
 +                     "f904c2476527c6d3e6609ab683c66fa0652cb1dc")
 +
 +    longtext = 1500 * "The quick brown fox jumps over the lazy dog\n"
 +    self.assertEqual(utils.Sha1Hmac("3YzMxZWE", longtext),
 +                     "35901b9a3001a7cdcf8e0e9d7c2e79df2223af54")
 +
 +  def testSha1HmacSalt(self):
 +    self.assertEqual(utils.Sha1Hmac("TguMTA2K", "", salt="abc0"),
 +                     "4999bf342470eadb11dfcd24ca5680cf9fd7cdce")
 +    self.assertEqual(utils.Sha1Hmac("TguMTA2K", "", salt="abc9"),
 +                     "17a4adc34d69c0d367d4ffbef96fd41d4df7a6e8")
 +    self.assertEqual(utils.Sha1Hmac("3YzMxZWE", "Hello World", salt="xyz0"),
 +                     "7f264f8114c9066afc9bb7636e1786d996d3cc0d")
 +
 +  def testVerifySha1Hmac(self):
 +    self.assert_(utils.VerifySha1Hmac("", "", ("fbdb1d1b18aa6c08324b"
 +                                               "7d64b71fb76370690e1d")))
 +    self.assert_(utils.VerifySha1Hmac("TguMTA2K", "",
 +                                      ("f904c2476527c6d3e660"
 +                                       "9ab683c66fa0652cb1dc")))
 +
 +    digest = "ef4f3bda82212ecb2f7ce868888a19092481f1fd"
 +    self.assert_(utils.VerifySha1Hmac("3YzMxZWE", "Hello World", digest))
 +    self.assert_(utils.VerifySha1Hmac("3YzMxZWE", "Hello World",
 +                                      digest.lower()))
 +    self.assert_(utils.VerifySha1Hmac("3YzMxZWE", "Hello World",
 +                                      digest.upper()))
 +    self.assert_(utils.VerifySha1Hmac("3YzMxZWE", "Hello World",
 +                                      digest.title()))
 +
 +  def testVerifySha1HmacSalt(self):
 +    self.assert_(utils.VerifySha1Hmac("TguMTA2K", "",
 +                                      ("17a4adc34d69c0d367d4"
 +                                       "ffbef96fd41d4df7a6e8"),
 +                                      salt="abc9"))
 +    self.assert_(utils.VerifySha1Hmac("3YzMxZWE", "Hello World",
 +                                      ("7f264f8114c9066afc9b"
 +                                       "b7636e1786d996d3cc0d"),
 +                                      salt="xyz0"))
 +
 +
+ class TestIgnoreSignals(unittest.TestCase):
+   """Test the IgnoreSignals decorator"""
+   @staticmethod
+   def _Raise(exception):
+     raise exception
+   @staticmethod
+   def _Return(rval):
+     return rval
+   def testIgnoreSignals(self):
+     sock_err_intr = socket.error(errno.EINTR, "Message")
+     sock_err_intr.errno = errno.EINTR
+     sock_err_inval = socket.error(errno.EINVAL, "Message")
+     sock_err_inval.errno = errno.EINVAL
+     env_err_intr = EnvironmentError(errno.EINTR, "Message")
+     env_err_inval = EnvironmentError(errno.EINVAL, "Message")
+     self.assertRaises(socket.error, self._Raise, sock_err_intr)
+     self.assertRaises(socket.error, self._Raise, sock_err_inval)
+     self.assertRaises(EnvironmentError, self._Raise, env_err_intr)
+     self.assertRaises(EnvironmentError, self._Raise, env_err_inval)
+     self.assertEquals(utils.IgnoreSignals(self._Raise, sock_err_intr), None)
+     self.assertEquals(utils.IgnoreSignals(self._Raise, env_err_intr), None)
+     self.assertRaises(socket.error, utils.IgnoreSignals, self._Raise,
+                       sock_err_inval)
+     self.assertRaises(EnvironmentError, utils.IgnoreSignals, self._Raise,
+                       env_err_inval)
+     self.assertEquals(utils.IgnoreSignals(self._Return, True), True)
+     self.assertEquals(utils.IgnoreSignals(self._Return, 33), 33)
  if __name__ == '__main__':
    testutils.GanetiTestProgram()