Merge branch 'stable-2.12' into stable-2.13
authorHrvoje Ribicic <riba@google.com>
Fri, 8 May 2015 14:05:17 +0000 (14:05 +0000)
committerHrvoje Ribicic <riba@google.com>
Fri, 8 May 2015 14:55:30 +0000 (16:55 +0200)
* stable-2.12
  Expand orphan volume test
  Restrict Ganeti's orphan volume checks to the single VG
  Modify UDS server startup to set permissions for sockets
  Add wheezy chroot files to gitignore file
  Makefile.am: Don't use -dynamic-too for .hpc_o files
  Makefile.am: Don't use dots in -osuf
  Fix compiler invocation for GHC >= 7.8
  Makefile.am: Fix wrong -dep-suffix for GHC 7.8
  Only upgrade configs not upgraded
  Only unlock config if we did lock it
  Mention preferred DRBD module settings when using Xen
  Avoid assertIn
  Test presence of public and private parameters
  Put private parameters into the environment
  Always close pipe on job forking
  Clean up pipes early on failed forks

Conflicts:
test/py/ganeti.backend_unittest.py

Resolution:
        Merge all test changes.

Signed-off-by: Hrvoje Ribicic <riba@google.com>
Reviewed-by: Klaus Aehlig <aehlig@google.com>

13 files changed:
1  2 
.gitignore
Makefile.am
configure.ac
lib/backend.py
lib/cmdlib/cluster.py
lib/config.py
src/Ganeti/JQueue.hs
src/Ganeti/Luxi.hs
src/Ganeti/Query/Exec.hs
src/Ganeti/Utils.hs
src/Ganeti/WConfd/Server.hs
test/py/cmdlib/cluster_unittest.py
test/py/ganeti.backend_unittest.py

diff --cc .gitignore
Simple merge
diff --cc Makefile.am
Simple merge
diff --cc configure.ac
Simple merge
diff --cc lib/backend.py
Simple merge
Simple merge
diff --cc lib/config.py
Simple merge
Simple merge
@@@ -77,11 -76,11 +77,12 @@@ import Ganeti.Object
  import Ganeti.OpParams (pTagsObject)
  import Ganeti.OpCodes
  import qualified Ganeti.Query.Language as Qlang
- import Ganeti.Runtime (GanetiDaemon(..))
+ import Ganeti.Runtime (GanetiDaemon(..), GanetiGroup(..), MiscGroup(..))
  import Ganeti.THH
  import Ganeti.THH.Field
 +import Ganeti.THH.Types (getOneTuple)
  import Ganeti.Types
+ import Ganeti.Utils
  
  
  -- | Currently supported Luxi operations and JSON serialization.
Simple merge
Simple merge
Simple merge
Simple merge
@@@ -46,8 -44,9 +46,10 @@@ from ganeti import hyperviso
  from ganeti import netutils
  from ganeti import objects
  from ganeti import pathutils
+ from ganeti import serializer
 +from ganeti import ssh
  from ganeti import utils
+ from cmdlib.testsupport.config_mock import ConfigMock
  
  
  class TestX509Certificates(unittest.TestCase):
@@@ -951,810 -950,38 +953,843 @@@ class TestSpaceReportingConstants(unitt
        self.assertEqual(None, backend._STORAGE_TYPE_INFO_FN[storage_type])
  
  
 +class TestAddRemoveGenerateNodeSshKey(testutils.GanetiTestCase):
 +
 +  _CLUSTER_NAME = "mycluster"
 +  _SSH_PORT = 22
 +
 +  def setUp(self):
 +    self._ssh_file_manager = testutils_ssh.FakeSshFileManager()
 +    testutils.GanetiTestCase.setUp(self)
 +    self._ssh_add_authorized_patcher = testutils \
 +      .patch_object(ssh, "AddAuthorizedKeys")
 +    self._ssh_remove_authorized_patcher = testutils \
 +      .patch_object(ssh, "RemoveAuthorizedKeys")
 +    self._ssh_add_authorized_mock = self._ssh_add_authorized_patcher.start()
 +    self._ssh_add_authorized_mock.side_effect = \
 +        self._ssh_file_manager.AddAuthorizedKeys
 +
 +    self._ssconf_mock = mock.Mock()
 +    self._ssconf_mock.GetNodeList = mock.Mock()
 +    self._ssconf_mock.GetMasterNode = mock.Mock()
 +    self._ssconf_mock.GetClusterName = mock.Mock()
 +    self._ssconf_mock.GetOnlineNodeList = mock.Mock()
 +
 +    self._run_cmd_mock = mock.Mock()
 +    self._run_cmd_mock.side_effect = self._ssh_file_manager.RunCommand
 +
 +    self._ssh_remove_authorized_mock = \
 +      self._ssh_remove_authorized_patcher.start()
 +    self._ssh_remove_authorized_mock.side_effect = \
 +        self._ssh_file_manager.RemoveAuthorizedKeys
 +
 +    self._ssh_add_public_key_patcher = testutils \
 +      .patch_object(ssh, "AddPublicKey")
 +    self._ssh_add_public_key_mock = \
 +      self._ssh_add_public_key_patcher.start()
 +    self._ssh_add_public_key_mock.side_effect = \
 +      self._ssh_file_manager.AddPublicKey
 +
 +    self._ssh_remove_public_key_patcher = testutils \
 +      .patch_object(ssh, "RemovePublicKey")
 +    self._ssh_remove_public_key_mock = \
 +      self._ssh_remove_public_key_patcher.start()
 +    self._ssh_remove_public_key_mock.side_effect = \
 +      self._ssh_file_manager.RemovePublicKey
 +
 +    self._ssh_query_pub_key_file_patcher = testutils \
 +      .patch_object(ssh, "QueryPubKeyFile")
 +    self._ssh_query_pub_key_file_mock = \
 +      self._ssh_query_pub_key_file_patcher.start()
 +    self._ssh_query_pub_key_file_mock.side_effect = \
 +      self._ssh_file_manager.QueryPubKeyFile
 +
 +    self._ssh_replace_name_by_uuid_patcher = testutils \
 +      .patch_object(ssh, "ReplaceNameByUuid")
 +    self._ssh_replace_name_by_uuid_mock = \
 +      self._ssh_replace_name_by_uuid_patcher.start()
 +    self._ssh_replace_name_by_uuid_mock.side_effect = \
 +      self._ssh_file_manager.ReplaceNameByUuid
 +
 +    self.noded_cert_file = testutils.TestDataFilename("cert1.pem")
 +
 +    self._SetupTestData()
 +
 +  def tearDown(self):
 +    super(testutils.GanetiTestCase, self).tearDown()
 +    self._ssh_add_authorized_patcher.stop()
 +    self._ssh_remove_authorized_patcher.stop()
 +    self._ssh_add_public_key_patcher.stop()
 +    self._ssh_remove_public_key_patcher.stop()
 +    self._ssh_query_pub_key_file_patcher.stop()
 +    self._ssh_replace_name_by_uuid_patcher.stop()
 +    self._TearDownTestData()
 +
 +  def _SetupTestData(self, number_of_nodes=15, number_of_pot_mcs=5,
 +                     number_of_mcs=5):
 +    """Sets up consistent test data for a cluster with a couple of nodes.
 +
 +    """
 +    self._pub_key_file = self._CreateTempFile()
 +    self._all_nodes = []
 +    self._potential_master_candidates = []
 +    self._master_candidate_uuids = []
 +    self._ssh_port_map = {}
 +
 +    self._ssconf_mock.reset_mock()
 +    self._ssconf_mock.GetNodeList.reset_mock()
 +    self._ssconf_mock.GetMasterNode.reset_mock()
 +    self._ssconf_mock.GetClusterName.reset_mock()
 +    self._ssconf_mock.GetOnlineNodeList.reset_mock()
 +    self._run_cmd_mock.reset_mock()
 +
 +    self._ssh_file_manager.InitAllNodes(15, 10, 5)
 +    self._master_node = self._ssh_file_manager.GetMasterNodeName()
 +    self._ssh_port_map = self._ssh_file_manager.GetSshPortMap(self._SSH_PORT)
 +    self._potential_master_candidates = \
 +        self._ssh_file_manager.GetAllPotentialMasterCandidateNodeNames()
 +    self._master_candidate_uuids = \
 +        self._ssh_file_manager.GetAllMasterCandidateUuids()
 +    self._all_nodes = self._ssh_file_manager.GetAllNodeNames()
 +
 +    self._ssconf_mock.GetNodeList.return_value = self._all_nodes
 +    self._ssconf_mock.GetOnlineNodeList.return_value = self._all_nodes
 +
 +  def _TearDownTestData(self):
 +    os.remove(self._pub_key_file)
 +
 +  def _GetCallsPerNode(self):
 +    calls_per_node = {}
 +    for (pos, keyword) in self._run_cmd_mock.call_args_list:
 +      (cluster_name, node, _, _, data) = pos
 +      if not node in calls_per_node:
 +        calls_per_node[node] = []
 +      calls_per_node[node].append(data)
 +    return calls_per_node
 +
 +  def testGenerateKey(self):
 +    test_node_name = "node_name_7"
 +    test_node_uuid = "node_uuid_7"
 +
 +    self._SetupTestData()
 +    ssh.AddPublicKey(test_node_uuid, "some_old_key",
 +                     key_file=self._pub_key_file)
 +
 +    backend._GenerateNodeSshKey(test_node_uuid, test_node_name,
 +                                self._ssh_port_map,
 +                                pub_key_file=self._pub_key_file,
 +                                ssconf_store=self._ssconf_mock,
 +                                noded_cert_file=self.noded_cert_file,
 +                                run_cmd_fn=self._run_cmd_mock)
 +
 +    calls_per_node = self._GetCallsPerNode()
 +    for node, calls in calls_per_node.items():
 +      self.assertEquals(node, test_node_name)
 +      for call in calls:
 +        self.assertTrue(constants.SSHS_GENERATE in call)
 +
 +  def _AddNewNodeToTestData(self, name, uuid, key, pot_mc, mc, master):
 +    self._ssh_file_manager.SetOrAddNode(name, uuid, key, pot_mc, mc, master)
 +
 +    if pot_mc:
 +      ssh.AddPublicKey(name, key, key_file=self._pub_key_file)
 +      self._potential_master_candidates.append(name)
 +
 +    self._ssh_port_map[name] = self._SSH_PORT
 +
 +  def _GetNewMasterCandidate(self):
 +    """Returns the properties of a new master candidate node."""
 +    return ("new_node_name", "new_node_uuid", "new_node_key", True, True, False)
 +
 +  def testAddMasterCandidate(self):
 +    (new_node_name, new_node_uuid, new_node_key, is_master_candidate,
 +     is_potential_master_candidate, is_master) = self._GetNewMasterCandidate()
 +
 +    self._AddNewNodeToTestData(
 +        new_node_name, new_node_uuid, new_node_key,
 +        is_potential_master_candidate, is_master_candidate,
 +        is_master)
 +
 +    backend.AddNodeSshKey(new_node_uuid, new_node_name,
 +                          self._potential_master_candidates,
 +                          self._ssh_port_map,
 +                          to_authorized_keys=is_master_candidate,
 +                          to_public_keys=is_potential_master_candidate,
 +                          get_public_keys=is_potential_master_candidate,
 +                          pub_key_file=self._pub_key_file,
 +                          ssconf_store=self._ssconf_mock,
 +                          noded_cert_file=self.noded_cert_file,
 +                          run_cmd_fn=self._run_cmd_mock)
 +
 +    self._ssh_file_manager.AssertPotentialMasterCandidatesOnlyHavePublicKey(
 +        new_node_name)
 +    self._ssh_file_manager.AssertAllNodesHaveAuthorizedKey(new_node_key)
 +
 +  def testAddPotentialMasterCandidate(self):
 +    new_node_name = "new_node_name"
 +    new_node_uuid = "new_node_uuid"
 +    new_node_key = "new_node_key"
 +    is_master_candidate = False
 +    is_potential_master_candidate = True
 +    is_master = False
 +
 +    self._AddNewNodeToTestData(
 +        new_node_name, new_node_uuid, new_node_key,
 +        is_potential_master_candidate, is_master_candidate,
 +        is_master)
 +
 +    backend.AddNodeSshKey(new_node_uuid, new_node_name,
 +                          self._potential_master_candidates,
 +                          self._ssh_port_map,
 +                          to_authorized_keys=is_master_candidate,
 +                          to_public_keys=is_potential_master_candidate,
 +                          get_public_keys=is_potential_master_candidate,
 +                          pub_key_file=self._pub_key_file,
 +                          ssconf_store=self._ssconf_mock,
 +                          noded_cert_file=self.noded_cert_file,
 +                          run_cmd_fn=self._run_cmd_mock)
 +
 +    self._ssh_file_manager.AssertPotentialMasterCandidatesOnlyHavePublicKey(
 +        new_node_name)
 +    self._ssh_file_manager.AssertNoNodeHasAuthorizedKey(new_node_key)
 +
 +  def testAddNormalNode(self):
 +    new_node_name = "new_node_name"
 +    new_node_uuid = "new_node_uuid"
 +    new_node_key = "new_node_key"
 +    is_master_candidate = False
 +    is_potential_master_candidate = False
 +    is_master = False
 +
 +    self._AddNewNodeToTestData(
 +        new_node_name, new_node_uuid, new_node_key,
 +        is_potential_master_candidate, is_master_candidate,
 +        is_master)
 +
 +    self.assertRaises(
 +        AssertionError, backend.AddNodeSshKey, new_node_uuid, new_node_name,
 +        self._potential_master_candidates, self._ssh_port_map,
 +        to_authorized_keys=is_master_candidate,
 +        to_public_keys=is_potential_master_candidate,
 +        get_public_keys=is_potential_master_candidate,
 +        pub_key_file=self._pub_key_file,
 +        ssconf_store=self._ssconf_mock,
 +        noded_cert_file=self.noded_cert_file,
 +        run_cmd_fn=self._run_cmd_mock)
 +
 +    self._ssh_file_manager.AssertNoNodeHasPublicKey(new_node_uuid, new_node_key)
 +    self._ssh_file_manager.AssertNoNodeHasAuthorizedKey(new_node_key)
 +
 +  def testPromoteToMasterCandidate(self):
 +    # Get one of the potential master candidates
 +    node_name, node_info = \
 +      self._ssh_file_manager.GetAllPurePotentialMasterCandidates()[0]
 +    # Update it's role to master candidate in the test data
 +    self._ssh_file_manager.SetOrAddNode(
 +        node_name, node_info.uuid, node_info.key,
 +        node_info.is_potential_master_candidate, True, node_info.is_master)
 +
 +    backend.AddNodeSshKey(node_info.uuid, node_name,
 +                          self._potential_master_candidates,
 +                          self._ssh_port_map,
 +                          to_authorized_keys=True,
 +                          to_public_keys=False,
 +                          get_public_keys=False,
 +                          pub_key_file=self._pub_key_file,
 +                          ssconf_store=self._ssconf_mock,
 +                          noded_cert_file=self.noded_cert_file,
 +                          run_cmd_fn=self._run_cmd_mock)
 +
 +    self._ssh_file_manager.AssertPotentialMasterCandidatesOnlyHavePublicKey(
 +        node_name)
 +    self._ssh_file_manager.AssertAllNodesHaveAuthorizedKey(node_info.key)
 +
 +  def testRemoveMasterCandidate(self):
 +    node_name, (node_uuid, node_key, is_potential_master_candidate,
 +     is_master_candidate, is_master) = \
 +        self._ssh_file_manager.GetAllMasterCandidates()[0]
 +
 +    backend.RemoveNodeSshKey(node_uuid, node_name,
 +                             self._master_candidate_uuids,
 +                             self._potential_master_candidates,
 +                             self._ssh_port_map,
 +                             from_authorized_keys=True,
 +                             from_public_keys=True,
 +                             clear_authorized_keys=True,
 +                             clear_public_keys=True,
 +                             pub_key_file=self._pub_key_file,
 +                             ssconf_store=self._ssconf_mock,
 +                             noded_cert_file=self.noded_cert_file,
 +                             run_cmd_fn=self._run_cmd_mock)
 +
 +    self._ssh_file_manager.AssertNoNodeHasPublicKey(node_uuid, node_key)
 +    self._ssh_file_manager.AssertNoNodeHasAuthorizedKey(node_key)
 +    self.assertEqual(0,
 +        len(self._ssh_file_manager.GetPublicKeysOfNode(node_name)))
 +    self.assertEqual(0,
 +        len(self._ssh_file_manager.GetAuthorizedKeysOfNode(node_name)))
 +
 +  def testRemovePotentialMasterCandidate(self):
 +    (node_name, node_info) = \
 +        self._ssh_file_manager.GetAllPurePotentialMasterCandidates()[0]
 +
 +    backend.RemoveNodeSshKey(node_info.uuid, node_name,
 +                             self._master_candidate_uuids,
 +                             self._potential_master_candidates,
 +                             self._ssh_port_map,
 +                             from_authorized_keys=False,
 +                             from_public_keys=True,
 +                             clear_authorized_keys=True,
 +                             clear_public_keys=True,
 +                             pub_key_file=self._pub_key_file,
 +                             ssconf_store=self._ssconf_mock,
 +                             noded_cert_file=self.noded_cert_file,
 +                             run_cmd_fn=self._run_cmd_mock)
 +
 +    self._ssh_file_manager.AssertNoNodeHasPublicKey(
 +        node_info.uuid, node_info.key)
 +    self._ssh_file_manager.AssertNoNodeHasAuthorizedKey(node_info.key)
 +    self.assertEqual(0,
 +        len(self._ssh_file_manager.GetPublicKeysOfNode(node_name)))
 +    self.assertEqual(0,
 +        len(self._ssh_file_manager.GetAuthorizedKeysOfNode(node_name)))
 +
 +  def testRemoveNormalNode(self):
 +    node_name, node_info = self._ssh_file_manager.GetAllNormalNodes()[0]
 +
 +    backend.RemoveNodeSshKey(node_info.uuid, node_name,
 +                             self._master_candidate_uuids,
 +                             self._potential_master_candidates,
 +                             self._ssh_port_map,
 +                             from_authorized_keys=False,
 +                             from_public_keys=False,
 +                             clear_authorized_keys=True,
 +                             clear_public_keys=True,
 +                             pub_key_file=self._pub_key_file,
 +                             ssconf_store=self._ssconf_mock,
 +                             noded_cert_file=self.noded_cert_file,
 +                             run_cmd_fn=self._run_cmd_mock)
 +
 +    self._ssh_file_manager.AssertNoNodeHasPublicKey(
 +        node_info.uuid, node_info.key)
 +    self._ssh_file_manager.AssertNoNodeHasAuthorizedKey(node_info.key)
 +    self.assertEqual(0,
 +        len(self._ssh_file_manager.GetPublicKeysOfNode(node_name)))
 +    self.assertEqual(0,
 +        len(self._ssh_file_manager.GetAuthorizedKeysOfNode(node_name)))
 +
 +  def testDemoteMasterCandidateToPotentialMasterCandidate(self):
 +    node_name, node_info = self._ssh_file_manager.GetAllMasterCandidates()[0]
 +    self._ssh_file_manager.SetOrAddNode(
 +        node_name, node_info.uuid, node_info.key,
 +        node_info.is_potential_master_candidate, False, node_info.is_master)
 +
 +    backend.RemoveNodeSshKey(node_info.uuid, node_name,
 +                             self._master_candidate_uuids,
 +                             self._potential_master_candidates,
 +                             self._ssh_port_map,
 +                             from_authorized_keys=True,
 +                             from_public_keys=False,
 +                             clear_authorized_keys=False,
 +                             clear_public_keys=False,
 +                             pub_key_file=self._pub_key_file,
 +                             ssconf_store=self._ssconf_mock,
 +                             noded_cert_file=self.noded_cert_file,
 +                             run_cmd_fn=self._run_cmd_mock)
 +
 +    self._ssh_file_manager.AssertPotentialMasterCandidatesOnlyHavePublicKey(
 +        node_name)
 +    self._ssh_file_manager.AssertNoNodeHasAuthorizedKey(node_info.key)
 +
 +  def testDemotePotentialMasterCandidateToNormalNode(self):
 +    (node_name, node_info) = \
 +        self._ssh_file_manager.GetAllPurePotentialMasterCandidates()[0]
 +    self._ssh_file_manager.SetOrAddNode(
 +        node_name, node_info.uuid, node_info.key, False,
 +        node_info.is_master_candidate, node_info.is_master)
 +
 +    backend.RemoveNodeSshKey(node_info.uuid, node_name,
 +                             self._master_candidate_uuids,
 +                             self._potential_master_candidates,
 +                             self._ssh_port_map,
 +                             from_authorized_keys=False,
 +                             from_public_keys=True,
 +                             clear_authorized_keys=False,
 +                             clear_public_keys=False,
 +                             pub_key_file=self._pub_key_file,
 +                             ssconf_store=self._ssconf_mock,
 +                             noded_cert_file=self.noded_cert_file,
 +                             run_cmd_fn=self._run_cmd_mock)
 +
 +    self._ssh_file_manager.AssertNoNodeHasPublicKey(
 +        node_info.uuid, node_info.key)
 +    self._ssh_file_manager.AssertNoNodeHasAuthorizedKey(node_info.key)
 +
 +  def _GetReducedOnlineNodeList(self):
 +    """'Randomly' mark some nodes as offline."""
 +    return [name for name in self._all_nodes
 +            if '3' not in name and '5' not in name]
 +
 +  def testAddKeyWithOfflineNodes(self):
 +    (new_node_name, new_node_uuid, new_node_key, is_master_candidate,
 +     is_potential_master_candidate, is_master) = self._GetNewMasterCandidate()
 +
 +    self._AddNewNodeToTestData(
 +        new_node_name, new_node_uuid, new_node_key,
 +        is_potential_master_candidate, is_master_candidate,
 +        is_master)
 +    self._online_nodes = self._GetReducedOnlineNodeList()
 +    self._ssconf_mock.GetOnlineNodeList.return_value = self._online_nodes
 +
 +    backend.AddNodeSshKey(new_node_uuid, new_node_name,
 +                          self._potential_master_candidates,
 +                          self._ssh_port_map,
 +                          to_authorized_keys=is_master_candidate,
 +                          to_public_keys=is_potential_master_candidate,
 +                          get_public_keys=is_potential_master_candidate,
 +                          pub_key_file=self._pub_key_file,
 +                          ssconf_store=self._ssconf_mock,
 +                          noded_cert_file=self.noded_cert_file,
 +                          run_cmd_fn=self._run_cmd_mock)
 +
 +    for node in self._all_nodes:
 +      if node in self._online_nodes:
 +        self.assertTrue(self._ssh_file_manager.NodeHasAuthorizedKey(
 +            node, new_node_key))
 +      else:
 +        self.assertFalse(self._ssh_file_manager.NodeHasAuthorizedKey(
 +            node, new_node_key))
 +
 +  def testRemoveKeyWithOfflineNodes(self):
 +    (node_name, node_info) = \
 +        self._ssh_file_manager.GetAllMasterCandidates()[0]
 +    self._online_nodes = self._GetReducedOnlineNodeList()
 +    self._ssconf_mock.GetOnlineNodeList.return_value = self._online_nodes
 +
 +    backend.RemoveNodeSshKey(node_info.uuid, node_name,
 +                             self._master_candidate_uuids,
 +                             self._potential_master_candidates,
 +                             self._ssh_port_map,
 +                             from_authorized_keys=True,
 +                             from_public_keys=True,
 +                             clear_authorized_keys=True,
 +                             clear_public_keys=True,
 +                             pub_key_file=self._pub_key_file,
 +                             ssconf_store=self._ssconf_mock,
 +                             noded_cert_file=self.noded_cert_file,
 +                             run_cmd_fn=self._run_cmd_mock)
 +
 +    offline_nodes = [node for node in self._all_nodes
 +                     if node not in self._online_nodes]
 +    self._ssh_file_manager.AssertNodeSetOnlyHasAuthorizedKey(
 +        offline_nodes, node_info.key)
 +
 +  def testAddKeySuccessfullyOnNewNodeWithRetries(self):
 +    """Tests adding a new node's key when updating that node takes retries.
 +
 +    This test checks whether adding a new node's key successfully updates
 +    the SSH key files of all nodes, even if updating the new node's key files
 +    itself takes a couple of retries to succeed.
 +
 +    """
 +    (new_node_name, new_node_uuid, new_node_key, is_master_candidate,
 +     is_potential_master_candidate, is_master) = self._GetNewMasterCandidate()
 +
 +    self._AddNewNodeToTestData(
 +        new_node_name, new_node_uuid, new_node_key,
 +        is_potential_master_candidate, is_master_candidate,
 +        is_master)
 +    self._ssh_file_manager.SetMaxRetries(
 +        new_node_name, constants.SSHS_MAX_RETRIES)
 +
 +    backend.AddNodeSshKey(new_node_uuid, new_node_name,
 +                          self._potential_master_candidates,
 +                          self._ssh_port_map,
 +                          to_authorized_keys=is_master_candidate,
 +                          to_public_keys=is_potential_master_candidate,
 +                          get_public_keys=is_potential_master_candidate,
 +                          pub_key_file=self._pub_key_file,
 +                          ssconf_store=self._ssconf_mock,
 +                          noded_cert_file=self.noded_cert_file,
 +                          run_cmd_fn=self._run_cmd_mock)
 +
 +    self._ssh_file_manager.AssertPotentialMasterCandidatesOnlyHavePublicKey(
 +        new_node_name)
 +    self._ssh_file_manager.AssertAllNodesHaveAuthorizedKey(
 +        new_node_key)
 +
 +  def testAddKeyFailedOnNewNodeWithRetries(self):
 +    """Tests clean up if updating a new node's SSH setup fails.
 +
 +    If adding the keys of a new node fails, because updating the SSH key files
 +    of that new node fails, check whether already carried out operations are
 +    successfully rolled back and thus the state of the cluster is cleaned up.
 +
 +    """
 +    (new_node_name, new_node_uuid, new_node_key, is_master_candidate,
 +     is_potential_master_candidate, is_master) = self._GetNewMasterCandidate()
 +
 +    self._AddNewNodeToTestData(
 +        new_node_name, new_node_uuid, new_node_key,
 +        is_potential_master_candidate, is_master_candidate,
 +        is_master)
 +    self._ssh_file_manager.SetMaxRetries(
 +        new_node_name, constants.SSHS_MAX_RETRIES + 1)
 +
 +    self.assertRaises(
 +        errors.SshUpdateError, backend.AddNodeSshKey, new_node_uuid,
 +        new_node_name, self._potential_master_candidates, self._ssh_port_map,
 +        to_authorized_keys=is_master_candidate,
 +        to_public_keys=is_potential_master_candidate,
 +        get_public_keys=is_potential_master_candidate,
 +        pub_key_file=self._pub_key_file,
 +        ssconf_store=self._ssconf_mock,
 +        noded_cert_file=self.noded_cert_file,
 +        run_cmd_fn=self._run_cmd_mock)
 +
 +    for node in self._all_nodes:
 +      if node == new_node_name:
 +        self.assertTrue(self._ssh_file_manager.NodeHasAuthorizedKey(
 +          node, new_node_key))
 +      else:
 +        self.assertFalse(self._ssh_file_manager.NodeHasAuthorizedKey(
 +          node, new_node_key))
 +
 +    self._ssh_file_manager.AssertNoNodeHasPublicKey(new_node_uuid, new_node_key)
 +
 +  def testAddKeySuccessfullyOnOldNodeWithRetries(self):
 +    """Tests adding a new key even if updating nodes takes retries.
 +
 +    This tests whether adding a new node's key successfully finishes,
 +    even if one of the other cluster nodes takes a couple of retries
 +    to succeed.
 +
 +    """
 +    (new_node_name, new_node_uuid, new_node_key, is_master_candidate,
 +     is_potential_master_candidate, is_master) = self._GetNewMasterCandidate()
 +
 +    other_node_name, _ = self._ssh_file_manager.GetAllMasterCandidates()[0]
 +    self._ssh_file_manager.SetMaxRetries(
 +        other_node_name, constants.SSHS_MAX_RETRIES)
 +    assert other_node_name != new_node_name
 +    self._AddNewNodeToTestData(
 +        new_node_name, new_node_uuid, new_node_key,
 +        is_potential_master_candidate, is_master_candidate,
 +        is_master)
 +
 +    backend.AddNodeSshKey(new_node_uuid, new_node_name,
 +                          self._potential_master_candidates,
 +                          self._ssh_port_map,
 +                          to_authorized_keys=is_master_candidate,
 +                          to_public_keys=is_potential_master_candidate,
 +                          get_public_keys=is_potential_master_candidate,
 +                          pub_key_file=self._pub_key_file,
 +                          ssconf_store=self._ssconf_mock,
 +                          noded_cert_file=self.noded_cert_file,
 +                          run_cmd_fn=self._run_cmd_mock)
 +
 +    self._ssh_file_manager.AssertAllNodesHaveAuthorizedKey(new_node_key)
 +
 +  def testAddKeyFailedOnOldNodeWithRetries(self):
 +    """Tests adding keys when updating one node's SSH setup fails.
 +
 +    This tests whether when adding a new node's key and one node is
 +    unreachable (but not marked as offline) the operation still finishes
 +    properly and only that unreachable node's SSH key setup did not get
 +    updated.
 +
 +    """
 +    (new_node_name, new_node_uuid, new_node_key, is_master_candidate,
 +     is_potential_master_candidate, is_master) = self._GetNewMasterCandidate()
 +
 +    other_node_name, _ = self._ssh_file_manager.GetAllMasterCandidates()[0]
 +    self._ssh_file_manager.SetMaxRetries(
 +        other_node_name, constants.SSHS_MAX_RETRIES + 1)
 +    assert other_node_name != new_node_name
 +    self._AddNewNodeToTestData(
 +        new_node_name, new_node_uuid, new_node_key,
 +        is_potential_master_candidate, is_master_candidate,
 +        is_master)
 +
 +    node_errors = backend.AddNodeSshKey(
 +        new_node_uuid, new_node_name, self._potential_master_candidates,
 +        self._ssh_port_map, to_authorized_keys=is_master_candidate,
 +        to_public_keys=is_potential_master_candidate,
 +        get_public_keys=is_potential_master_candidate,
 +        pub_key_file=self._pub_key_file,
 +        ssconf_store=self._ssconf_mock,
 +        noded_cert_file=self.noded_cert_file,
 +        run_cmd_fn=self._run_cmd_mock)
 +
 +    rest_nodes = [node for node in self._all_nodes
 +                  if node != other_node_name]
 +    rest_nodes.append(new_node_name)
 +    self._ssh_file_manager.AssertNodeSetOnlyHasAuthorizedKey(
 +        rest_nodes, new_node_key)
 +    self.assertTrue([error_msg for (node, error_msg) in node_errors
 +                     if node == other_node_name])
 +
 +  def testRemoveKeySuccessfullyWithRetriesOnOtherNode(self):
 +    """Test removing keys even if one of the old nodes needs retries.
 +
 +    This tests checks whether a key can be removed successfully even
 +    when one of the other nodes needs to be contacted with several
 +    retries.
 +
 +    """
 +    all_master_candidates = self._ssh_file_manager.GetAllMasterCandidates()
 +    node_name, node_info = all_master_candidates[0]
 +    other_node_name, _ = all_master_candidates[1]
 +    assert node_name != self._master_node
 +    assert other_node_name != self._master_node
 +    assert node_name != other_node_name
 +    self._ssh_file_manager.SetMaxRetries(
 +        other_node_name, constants.SSHS_MAX_RETRIES)
 +
 +    backend.RemoveNodeSshKey(node_info.uuid, node_name,
 +                             self._master_candidate_uuids,
 +                             self._potential_master_candidates,
 +                             self._ssh_port_map,
 +                             from_authorized_keys=True,
 +                             from_public_keys=True,
 +                             clear_authorized_keys=True,
 +                             clear_public_keys=True,
 +                             pub_key_file=self._pub_key_file,
 +                             ssconf_store=self._ssconf_mock,
 +                             noded_cert_file=self.noded_cert_file,
 +                             run_cmd_fn=self._run_cmd_mock)
 +
 +    self._ssh_file_manager.AssertNoNodeHasPublicKey(
 +        node_info.uuid, node_info.key)
 +    self._ssh_file_manager.AssertNoNodeHasAuthorizedKey(node_info.key)
 +
 +  def testRemoveKeyFailedWithRetriesOnOtherNode(self):
 +    """Test removing keys even if one of the old nodes fails even with retries.
 +
 +    This tests checks whether the removal of a key finishes properly, even if
 +    the update of the key files on one of the other nodes fails despite several
 +    retries.
 +
 +    """
 +    all_master_candidates = self._ssh_file_manager.GetAllMasterCandidates()
 +    node_name, node_info = all_master_candidates[0]
 +    other_node_name, _ = all_master_candidates[1]
 +    assert node_name != self._master_node
 +    assert other_node_name != self._master_node
 +    assert node_name != other_node_name
 +    self._ssh_file_manager.SetMaxRetries(
 +        other_node_name, constants.SSHS_MAX_RETRIES + 1)
 +
 +    error_msgs = backend.RemoveNodeSshKey(
 +        node_info.uuid, node_name, self._master_candidate_uuids,
 +        self._potential_master_candidates, self._ssh_port_map,
 +        from_authorized_keys=True, from_public_keys=True,
 +        clear_authorized_keys=True, clear_public_keys=True,
 +        pub_key_file=self._pub_key_file, ssconf_store=self._ssconf_mock,
 +        noded_cert_file=self.noded_cert_file, run_cmd_fn=self._run_cmd_mock)
 +
 +    self._ssh_file_manager.AssertNodeSetOnlyHasAuthorizedKey(
 +        [other_node_name], node_info.key)
 +    self.assertTrue([error_msg for (node, error_msg) in error_msgs
 +                     if node == other_node_name])
 +
 +  def testRemoveKeySuccessfullyWithRetriesOnTargetNode(self):
 +    """Test removing keys even if the target nodes needs retries.
 +
 +    This tests checks whether a key can be removed successfully even
 +    when removing the key on the node itself needs retries.
 +
 +    """
 +    all_master_candidates = self._ssh_file_manager.GetAllMasterCandidates()
 +    node_name, node_info = all_master_candidates[0]
 +    assert node_name != self._master_node
 +    self._ssh_file_manager.SetMaxRetries(
 +        node_name, constants.SSHS_MAX_RETRIES)
 +
 +    backend.RemoveNodeSshKey(node_info.uuid, node_name,
 +                             self._master_candidate_uuids,
 +                             self._potential_master_candidates,
 +                             self._ssh_port_map,
 +                             from_authorized_keys=True,
 +                             from_public_keys=True,
 +                             clear_authorized_keys=True,
 +                             clear_public_keys=True,
 +                             pub_key_file=self._pub_key_file,
 +                             ssconf_store=self._ssconf_mock,
 +                             noded_cert_file=self.noded_cert_file,
 +                             run_cmd_fn=self._run_cmd_mock)
 +
 +    self._ssh_file_manager.AssertNoNodeHasPublicKey(
 +        node_info.uuid, node_info.key)
 +    self._ssh_file_manager.AssertNoNodeHasAuthorizedKey(node_info.key)
 +
 +  def testRemoveKeyFailedWithRetriesOnTargetNode(self):
 +    """Test removing keys even if contacting the node fails with retries.
 +
 +    This tests checks whether the removal of a key finishes properly, even if
 +    the update of the key files on the node itself fails despite several
 +    retries.
 +
 +    """
 +    all_master_candidates = self._ssh_file_manager.GetAllMasterCandidates()
 +    node_name, node_info = all_master_candidates[0]
 +    assert node_name != self._master_node
 +    self._ssh_file_manager.SetMaxRetries(
 +        node_name, constants.SSHS_MAX_RETRIES + 1)
 +
 +    error_msgs = backend.RemoveNodeSshKey(
 +        node_info.uuid, node_name, self._master_candidate_uuids,
 +        self._potential_master_candidates, self._ssh_port_map,
 +        from_authorized_keys=True, from_public_keys=True,
 +        clear_authorized_keys=True, clear_public_keys=True,
 +        pub_key_file=self._pub_key_file, ssconf_store=self._ssconf_mock,
 +        noded_cert_file=self.noded_cert_file, run_cmd_fn=self._run_cmd_mock)
 +
 +    self._ssh_file_manager.AssertNodeSetOnlyHasAuthorizedKey(
 +        [node_name], node_info.key)
 +    self.assertTrue([error_msg for (node, error_msg) in error_msgs
 +                     if node == node_name])
 +
 +
 +class TestVerifySshSetup(testutils.GanetiTestCase):
 +
 +  _NODE1_UUID = "uuid1"
 +  _NODE2_UUID = "uuid2"
 +  _NODE3_UUID = "uuid3"
 +  _NODE1_NAME = "name1"
 +  _NODE2_NAME = "name2"
 +  _NODE3_NAME = "name3"
 +  _NODE1_KEYS = ["key11"]
 +  _NODE2_KEYS = ["key21"]
 +  _NODE3_KEYS = ["key31"]
 +
 +  _NODE_STATUS_LIST = [
 +    (_NODE1_UUID, _NODE1_NAME, True, True),
 +    (_NODE2_UUID, _NODE2_NAME, False, True),
 +    (_NODE3_UUID, _NODE3_NAME, False, False),
 +    ]
 +
 +  _PUB_KEY_RESULT = {
 +    _NODE1_UUID: _NODE1_KEYS,
 +    _NODE2_UUID: _NODE2_KEYS,
 +    _NODE3_UUID: _NODE3_KEYS,
 +    }
 +
 +  _AUTH_RESULT = {
 +    _NODE1_KEYS[0]: True,
 +    _NODE2_KEYS[0]: False,
 +    _NODE3_KEYS[0]: False,
 +  }
 +
 +  def setUp(self):
 +    testutils.GanetiTestCase.setUp(self)
 +    self._has_authorized_patcher = testutils \
 +      .patch_object(ssh, "HasAuthorizedKey")
 +    self._has_authorized_mock = self._has_authorized_patcher.start()
 +    self._query_patcher = testutils \
 +      .patch_object(ssh, "QueryPubKeyFile")
 +    self._query_mock = self._query_patcher.start()
 +    self._read_file_patcher = testutils \
 +      .patch_object(utils, "ReadFile")
 +    self._read_file_mock = self._read_file_patcher.start()
 +    self._read_file_mock.return_value = self._NODE1_KEYS[0]
 +    self.tmpdir = tempfile.mkdtemp()
 +    self.pub_key_file = os.path.join(self.tmpdir, "pub_key_file")
 +    open(self.pub_key_file, "w").close()
 +
 +  def tearDown(self):
 +    super(testutils.GanetiTestCase, self).tearDown()
 +    self._has_authorized_patcher.stop()
 +    self._query_patcher.stop()
 +    self._read_file_patcher.stop()
 +    shutil.rmtree(self.tmpdir)
 +
 +  def testValidData(self):
 +    self._has_authorized_mock.side_effect = \
 +      lambda _, key : self._AUTH_RESULT[key]
 +    self._query_mock.return_value = self._PUB_KEY_RESULT
 +    result = backend._VerifySshSetup(self._NODE_STATUS_LIST,
 +                                     self._NODE1_NAME,
 +                                     pub_key_file=self.pub_key_file)
 +    self.assertEqual(result, [])
 +
 +  def testMissingKey(self):
 +    self._has_authorized_mock.side_effect = \
 +      lambda _, key : self._AUTH_RESULT[key]
 +    pub_key_missing = copy.deepcopy(self._PUB_KEY_RESULT)
 +    del pub_key_missing[self._NODE2_UUID]
 +    self._query_mock.return_value = pub_key_missing
 +    result = backend._VerifySshSetup(self._NODE_STATUS_LIST,
 +                                     self._NODE1_NAME,
 +                                     pub_key_file=self.pub_key_file)
 +    self.assertTrue(self._NODE2_UUID in result[0])
 +
 +  def testUnknownKey(self):
 +    self._has_authorized_mock.side_effect = \
 +      lambda _, key : self._AUTH_RESULT[key]
 +    pub_key_missing = copy.deepcopy(self._PUB_KEY_RESULT)
 +    pub_key_missing["unkownnodeuuid"] = "pinkbunny"
 +    self._query_mock.return_value = pub_key_missing
 +    result = backend._VerifySshSetup(self._NODE_STATUS_LIST,
 +                                     self._NODE1_NAME,
 +                                     pub_key_file=self.pub_key_file)
 +    self.assertTrue("unkownnodeuuid" in result[0])
 +
 +  def testMissingMasterCandidate(self):
 +    auth_result = copy.deepcopy(self._AUTH_RESULT)
 +    auth_result["key11"] = False
 +    self._has_authorized_mock.side_effect = \
 +      lambda _, key : auth_result[key]
 +    self._query_mock.return_value = self._PUB_KEY_RESULT
 +    result = backend._VerifySshSetup(self._NODE_STATUS_LIST,
 +                                     self._NODE1_NAME,
 +                                     pub_key_file=self.pub_key_file)
 +    self.assertTrue(self._NODE1_UUID in result[0])
 +
 +  def testSuperfluousNormalNode(self):
 +    auth_result = copy.deepcopy(self._AUTH_RESULT)
 +    auth_result["key31"] = True
 +    self._has_authorized_mock.side_effect = \
 +      lambda _, key : auth_result[key]
 +    self._query_mock.return_value = self._PUB_KEY_RESULT
 +    result = backend._VerifySshSetup(self._NODE_STATUS_LIST,
 +                                     self._NODE1_NAME,
 +                                     pub_key_file=self.pub_key_file)
 +    self.assertTrue(self._NODE3_UUID in result[0])
 +
 +
+ class TestOSEnvironment(unittest.TestCase):
+   """Ensure the presence of public and private parameters.
+   They have to be present inside os environment variables.
+   """
+   def _CreateEnv(self):
+     """Create and return an environment."""
+     config_mock = ConfigMock()
+     inst = config_mock.AddNewInstance(
+              osparams={"public_param": "public_info"},
+              osparams_private=serializer.PrivateDict({"private_param":
+                                                      "private_info",
+                                                      "another_private_param":
+                                                      "more_privacy"}),
+              nics = [])
+     inst.disks_info = ""
+     inst.secondary_nodes = []
+     return backend.OSEnvironment(inst, config_mock.CreateOs())
+   def testParamPresence(self):
+     env = self._CreateEnv()
+     env_keys = env.keys()
+     self.assertTrue("OSP_PUBLIC_PARAM" in env)
+     self.assertTrue("OSP_PRIVATE_PARAM" in env)
+     self.assertTrue("OSP_ANOTHER_PRIVATE_PARAM" in env)
+     self.assertEqual("public_info", env["OSP_PUBLIC_PARAM"])
+     self.assertEqual("private_info", env["OSP_PRIVATE_PARAM"])
+     self.assertEqual("more_privacy", env["OSP_ANOTHER_PRIVATE_PARAM"])
  if __name__ == "__main__":
    testutils.GanetiTestProgram()