Write SSH ports to ssconf files
authorHelga Velroyen <helgav@google.com>
Tue, 30 Jun 2015 08:48:11 +0000 (10:48 +0200)
committerHelga Velroyen <helgav@google.com>
Mon, 6 Jul 2015 10:46:55 +0000 (12:46 +0200)
For the downgrading of the SSL setup from 2.12 to 2.11, we
need to be able to SSH into machines while no daemons are
running. Unfortunately currently the only way to obtain
custom-configured SSH ports is by queries. In order to
access this information with daemons being shutdown, this
patch adds the SSH port information to an ssconf file.

This will also be used to simplify some backend calls for
the *SSH* handling in 2.13.

Signed-off-by: Helga Velroyen <helgav@google.com>
Reviewed-by: Petr Pudlak <pudlak@google.com>

lib/config.py
lib/ssconf.py
src/Ganeti/Constants.hs
src/Ganeti/Ssconf.hs
src/Ganeti/WConfd/Ssconf.hs

index 1a79f5e..28ddd80 100644 (file)
@@ -328,6 +328,10 @@ class ConfigWriter(object):
     """
     return os.path.exists(pathutils.CLUSTER_CONF_FILE)
 
+  def _UnlockedGetNdParams(self, node):
+    nodegroup = self._UnlockedGetNodeGroup(node.group)
+    return self._ConfigData().cluster.FillND(node, nodegroup)
+
   @_ConfigSync(shared=1)
   def GetNdParams(self, node):
     """Get the node params populated with cluster defaults.
@@ -337,8 +341,7 @@ class ConfigWriter(object):
     @return: A dict with the filled in node params
 
     """
-    nodegroup = self._UnlockedGetNodeGroup(node.group)
-    return self._ConfigData().cluster.FillND(node, nodegroup)
+    return self._UnlockedGetNdParams(node)
 
   @_ConfigSync(shared=1)
   def GetNdGroupParams(self, nodegroup):
@@ -2972,6 +2975,13 @@ class ConfigWriter(object):
       ssconf_values[ssconf_key] = all_hvparams[hv]
     return ssconf_values
 
+  def _UnlockedGetSshPortMap(self, node_infos):
+    node_ports = dict([(node.name,
+                        self._UnlockedGetNdParams(node).get(
+                            constants.ND_SSH_PORT))
+                       for node in node_infos])
+    return node_ports
+
   def _UnlockedGetSsconfValues(self):
     """Return the values needed by ssconf.
 
@@ -3023,6 +3033,10 @@ class ConfigWriter(object):
                 self._ConfigData().networks.values()]
     networks_data = fn(utils.NiceSort(networks))
 
+    ssh_ports = fn("%s=%s" % (node_name, port)
+                   for node_name, port
+                   in self._UnlockedGetSshPortMap(node_infos).items())
+
     ssconf_values = {
       constants.SS_CLUSTER_NAME: cluster.cluster_name,
       constants.SS_CLUSTER_TAGS: cluster_tags,
@@ -3051,6 +3065,7 @@ class ConfigWriter(object):
       constants.SS_NODEGROUPS: nodegroups_data,
       constants.SS_NETWORKS: networks_data,
       constants.SS_ENABLED_USER_SHUTDOWN: str(cluster.enabled_user_shutdown),
+      constants.SS_SSH_PORTS: ssh_ports,
       }
     ssconf_values = self._ExtendByAllHvparamsStrings(ssconf_values,
                                                      all_hvparams)
index 07ec612..111493e 100644 (file)
@@ -84,6 +84,7 @@ _VALID_KEYS = compat.UniqueFrozenset([
   constants.SS_HVPARAMS_XEN_CHROOT,
   constants.SS_HVPARAMS_XEN_LXC,
   constants.SS_ENABLED_USER_SHUTDOWN,
+  constants.SS_SSH_PORTS,
   ])
 
 #: Maximum size for ssconf files
@@ -257,6 +258,27 @@ class SimpleStore(object):
     nl = data.splitlines(False)
     return nl
 
+  def _GetDictOfSsconfMap(self, ss_file_key):
+    """Reads a file with lines like key=value and returns a dict.
+
+    This utility function reads a file containing ssconf values of
+    the form "key=value", splits the lines at "=" and returns a
+    dictionary mapping the keys to the values.
+
+    @type ss_file_key: string
+    @param ss_file_key: the constant referring to an ssconf file
+    @rtype: dict of string to string
+    @return: a dictionary mapping the keys to the values
+
+    """
+    data = self._ReadFile(ss_file_key)
+    lines = data.splitlines(False)
+    mapping = {}
+    for line in lines:
+      (key, value) = line.split("=")
+      mapping[key] = value
+    return mapping
+
   def GetMasterCandidatesCertMap(self):
     """Returns the map of master candidate UUIDs to ssl cert.
 
@@ -265,13 +287,18 @@ class SimpleStore(object):
       to their SSL certificate digests
 
     """
-    data = self._ReadFile(constants.SS_MASTER_CANDIDATES_CERTS)
-    lines = data.splitlines(False)
-    certs = {}
-    for line in lines:
-      (node_uuid, cert_digest) = line.split("=")
-      certs[node_uuid] = cert_digest
-    return certs
+    return self._GetDictOfSsconfMap(constants.SS_MASTER_CANDIDATES_CERTS)
+
+  def GetSshPortMap(self):
+    """Returns the map of node names to SSH port.
+
+    @rtype: dict of string to string
+    @return: dictionary mapping the node names to their SSH port
+
+    """
+    return dict([(node_name, int(ssh_port)) for
+                  node_name, ssh_port in
+                  self._GetDictOfSsconfMap(constants.SS_SSH_PORTS).items()])
 
   def GetMasterIP(self):
     """Get the IP of the master node for this cluster.
@@ -389,13 +416,7 @@ class SimpleStore(object):
     @returns: dictionary with hypervisor parameters
 
     """
-    data = self._ReadFile(constants.SS_HVPARAMS_PREF + hvname)
-    lines = data.splitlines(False)
-    hvparams = {}
-    for line in lines:
-      (key, value) = line.split("=")
-      hvparams[key] = value
-    return hvparams
+    return self._GetDictOfSsconfMap(constants.SS_HVPARAMS_PREF + hvname)
 
   def GetHvparams(self):
     """Return the hypervisor parameters of all hypervisors.
index 7cdc3ff..557bc1c 100644 (file)
@@ -3765,6 +3765,9 @@ ssFilePerms = 0o444
 ssEnabledUserShutdown :: String
 ssEnabledUserShutdown = "enabled_user_shutdown"
 
+ssSshPorts :: String
+ssSshPorts = "ssh_ports"
+
 -- | Cluster wide default parameters
 defaultEnabledHypervisor :: String
 defaultEnabledHypervisor = htXenPvm
index ae8283d..bfcf3c8 100644 (file)
@@ -113,6 +113,7 @@ $(declareLADT ''String "SSKey" (
   , ("SSNodegroups",            C.ssNodegroups)
   , ("SSNetworks",              C.ssNetworks)
   , ("SSEnabledUserShutdown",   C.ssEnabledUserShutdown)
+  , ("SSSshPorts",              C.ssSshPorts)
   ] ++
   -- Automatically generate SSHvparamsXxx for each hypervisor type:
   map ((("SSHvparams" ++) . show)
index 6aa7765..a831ef4 100644 (file)
@@ -126,6 +126,8 @@ mkSSConf cdata = SSConf . M.fromList $
                    . configNetworks $ cdata)
     , (SSEnabledUserShutdown, return . show . clusterEnabledUserShutdown
                               $ cluster)
+    , (SSSshPorts, mapLines (eqPair . (nodeName
+                                       &&& getSshPort cdata)) nodes)
     ] ++
     map (first hvparamsSSKey) (mkSSConfHvparams cluster)
   where
@@ -139,3 +141,9 @@ mkSSConf cdata = SSConf . M.fromList $
     nodes = niceSortKey nodeName . toList $ configNodes cdata
     (offline, online) = partition nodeOffline nodes
     nodeGroups = niceSortKey groupName . toList $ configNodegroups cdata
+
+    -- This will return the empty string only for the situation where the
+    -- configuration is corrupted and no nodegroup can be found for that node.
+    getSshPort :: ConfigData -> Node -> String
+    getSshPort cfg node = maybe "" (show . ndpSshPort)
+                          $ getNodeNdParams cfg node