Don't pass sstore to LUs anymore
[ganeti-local] / lib / config.py
index 8635a8b..57c56d8 100644 (file)
@@ -43,19 +43,16 @@ from ganeti import constants
 from ganeti import rpc
 from ganeti import objects
 from ganeti import serializer
 from ganeti import rpc
 from ganeti import objects
 from ganeti import serializer
-from ganeti import ssconf
 
 
 _config_lock = locking.SharedLock()
 
 
 
 
 _config_lock = locking.SharedLock()
 
 
-def ValidateConfig():
-  sstore = ssconf.SimpleStore()
-
-  if sstore.GetConfigVersion() != constants.CONFIG_VERSION:
+def _ValidateConfig(data):
+  if data.version != constants.CONFIG_VERSION:
     raise errors.ConfigurationError("Cluster configuration version"
                                     " mismatch, got %s instead of %s" %
     raise errors.ConfigurationError("Cluster configuration version"
                                     " mismatch, got %s instead of %s" %
-                                    (sstore.GetConfigVersion(),
+                                    (data.version,
                                      constants.CONFIG_VERSION))
 
 
                                      constants.CONFIG_VERSION))
 
 
@@ -126,6 +123,25 @@ class ConfigWriter:
     all_macs = self._AllMACs()
     return mac in all_macs
 
     all_macs = self._AllMACs()
     return mac in all_macs
 
+  @locking.ssynchronized(_config_lock, shared=1)
+  def GenerateDRBDSecret(self):
+    """Generate a DRBD secret.
+
+    This checks the current disks for duplicates.
+
+    """
+    self._OpenConfig()
+    all_secrets = self._AllDRBDSecrets()
+    retries = 64
+    while retries > 0:
+      secret = utils.GenerateSecret()
+      if secret not in all_secrets:
+        break
+      retries -= 1
+    else:
+      raise errors.ConfigurationError("Can't generate unique DRBD secret")
+    return secret
+
   def _ComputeAllLVs(self):
     """Compute the list of all LVs.
 
   def _ComputeAllLVs(self):
     """Compute the list of all LVs.
 
@@ -185,6 +201,25 @@ class ConfigWriter:
 
     return result
 
 
     return result
 
+  def _AllDRBDSecrets(self):
+    """Return all DRBD secrets present in the config.
+
+    """
+    def helper(disk, result):
+      """Recursively gather secrets from this disk."""
+      if disk.dev_type == constants.DT_DRBD8:
+        result.append(disk.logical_id[5])
+      if disk.children:
+        for child in disk.children:
+          helper(child, result)
+
+    result = []
+    for instance in self._config_data.instances.values():
+      for disk in instance.disks:
+        helper(disk, result)
+
+    return result
+
   @locking.ssynchronized(_config_lock, shared=1)
   def VerifyConfig(self):
     """Stub verify function.
   @locking.ssynchronized(_config_lock, shared=1)
   def VerifyConfig(self):
     """Stub verify function.
@@ -193,6 +228,7 @@ class ConfigWriter:
 
     result = []
     seen_macs = []
 
     result = []
     seen_macs = []
+    ports = {}
     data = self._config_data
     for instance_name in data.instances:
       instance = data.instances[instance_name]
     data = self._config_data
     for instance_name in data.instances:
       instance = data.instances[instance_name]
@@ -209,6 +245,43 @@ class ConfigWriter:
                         (instance_name, idx, nic.mac))
         else:
           seen_macs.append(nic.mac)
                         (instance_name, idx, nic.mac))
         else:
           seen_macs.append(nic.mac)
+
+      # gather the drbd ports for duplicate checks
+      for dsk in instance.disks:
+        if dsk.dev_type in constants.LDS_DRBD:
+          tcp_port = dsk.logical_id[2]
+          if tcp_port not in ports:
+            ports[tcp_port] = []
+          ports[tcp_port].append((instance.name, "drbd disk %s" % dsk.iv_name))
+      # gather network port reservation
+      net_port = getattr(instance, "network_port", None)
+      if net_port is not None:
+        if net_port not in ports:
+          ports[net_port] = []
+        ports[net_port].append((instance.name, "network port"))
+
+    # cluster-wide pool of free ports
+    for free_port in self._config_data.cluster.tcpudp_port_pool:
+      if free_port not in ports:
+        ports[free_port] = []
+      ports[free_port].append(("cluster", "port marked as free"))
+
+    # compute tcp/udp duplicate ports
+    keys = ports.keys()
+    keys.sort()
+    for pnum in keys:
+      pdata = ports[pnum]
+      if len(pdata) > 1:
+        txt = ", ".join(["%s/%s" % val for val in pdata])
+        result.append("tcp/udp port %s has duplicates: %s" % (pnum, txt))
+
+    # highest used tcp port check
+    if keys:
+      if keys[-1] > self._config_data.cluster.highest_used_port:
+        result.append("Highest used port mismatch, saved %s, computed %s" %
+                      (self._config_data.cluster.highest_used_port,
+                       keys[-1]))
+
     return result
 
   def _UnlockedSetDiskID(self, disk, node_name):
     return result
 
   def _UnlockedSetDiskID(self, disk, node_name):
@@ -230,7 +303,7 @@ class ConfigWriter:
     if disk.logical_id is None and disk.physical_id is not None:
       return
     if disk.dev_type == constants.LD_DRBD8:
     if disk.logical_id is None and disk.physical_id is not None:
       return
     if disk.dev_type == constants.LD_DRBD8:
-      pnode, snode, port, pminor, sminor = disk.logical_id
+      pnode, snode, port, pminor, sminor, secret = disk.logical_id
       if node_name not in (pnode, snode):
         raise errors.ConfigurationError("DRBD device not knowing node %s" %
                                         node_name)
       if node_name not in (pnode, snode):
         raise errors.ConfigurationError("DRBD device not knowing node %s" %
                                         node_name)
@@ -242,9 +315,9 @@ class ConfigWriter:
       p_data = (pnode_info.secondary_ip, port)
       s_data = (snode_info.secondary_ip, port)
       if pnode == node_name:
       p_data = (pnode_info.secondary_ip, port)
       s_data = (snode_info.secondary_ip, port)
       if pnode == node_name:
-        disk.physical_id = p_data + s_data + (pminor,)
+        disk.physical_id = p_data + s_data + (pminor, secret)
       else: # it must be secondary, we tested above
       else: # it must be secondary, we tested above
-        disk.physical_id = s_data + p_data + (sminor,)
+        disk.physical_id = s_data + p_data + (sminor, secret)
     else:
       disk.physical_id = disk.logical_id
     return
     else:
       disk.physical_id = disk.logical_id
     return
@@ -316,8 +389,8 @@ class ConfigWriter:
 
     """
     def _AppendUsedPorts(instance_name, disk, used):
 
     """
     def _AppendUsedPorts(instance_name, disk, used):
-      if disk.dev_type == constants.LD_DRBD8 and len(disk.logical_id) == 5:
-        nodeA, nodeB, dummy, minorA, minorB = disk.logical_id
+      if disk.dev_type == constants.LD_DRBD8 and len(disk.logical_id) >= 5:
+        nodeA, nodeB, dummy, minorA, minorB = disk.logical_id[:5]
         for node, port in ((nodeA, minorA), (nodeB, minorB)):
           assert node in used, "Instance node not found in node list"
           if port in used[node]:
         for node, port in ((nodeA, minorA), (nodeB, minorB)):
           assert node in used, "Instance node not found in node list"
           if port in used[node]:
@@ -359,6 +432,7 @@ class ConfigWriter:
         # no minors used, we can start at 0
         result.append(0)
         ndata[0] = instance
         # no minors used, we can start at 0
         result.append(0)
         ndata[0] = instance
+        self._temporary_drbds[(nname, 0)] = instance
         continue
       keys = ndata.keys()
       keys.sort()
         continue
       keys = ndata.keys()
       keys.sort()
@@ -395,6 +469,69 @@ class ConfigWriter:
         del self._temporary_drbds[key]
 
   @locking.ssynchronized(_config_lock, shared=1)
         del self._temporary_drbds[key]
 
   @locking.ssynchronized(_config_lock, shared=1)
+  def GetConfigVersion(self):
+    """Get the configuration version.
+
+    @return: Config version
+
+    """
+    return self._config_data.version
+
+  @locking.ssynchronized(_config_lock, shared=1)
+  def GetClusterName(self):
+    """Get cluster name.
+
+    @return: Cluster name
+
+    """
+    self._OpenConfig()
+    return self._config_data.cluster.cluster_name
+
+  @locking.ssynchronized(_config_lock, shared=1)
+  def GetMasterNode(self):
+    """Get the hostname of the master node for this cluster.
+
+    @return: Master hostname
+
+    """
+    self._OpenConfig()
+    return self._config_data.cluster.master_node
+
+  @locking.ssynchronized(_config_lock, shared=1)
+  def GetMasterIP(self):
+    """Get the IP of the master node for this cluster.
+
+    @return: Master IP
+
+    """
+    self._OpenConfig()
+    return self._config_data.cluster.master_ip
+
+  @locking.ssynchronized(_config_lock, shared=1)
+  def GetMasterNetdev(self):
+    """Get the master network device for this cluster.
+
+    """
+    self._OpenConfig()
+    return self._config_data.cluster.master_netdev
+
+  @locking.ssynchronized(_config_lock, shared=1)
+  def GetFileStorageDir(self):
+    """Get the file storage dir for this cluster.
+
+    """
+    self._OpenConfig()
+    return self._config_data.cluster.file_storage_dir
+
+  @locking.ssynchronized(_config_lock, shared=1)
+  def GetHypervisorType(self):
+    """Get the hypervisor type for this cluster.
+
+    """
+    self._OpenConfig()
+    return self._config_data.cluster.hypervisor
+
+  @locking.ssynchronized(_config_lock, shared=1)
   def GetHostKey(self):
     """Return the rsa hostkey from the config.
 
   def GetHostKey(self):
     """Return the rsa hostkey from the config.
 
@@ -424,6 +561,7 @@ class ConfigWriter:
     self._OpenConfig()
     instance.serial_no = 1
     self._config_data.instances[instance.name] = instance
     self._OpenConfig()
     instance.serial_no = 1
     self._config_data.instances[instance.name] = instance
+    self._config_data.cluster.serial_no += 1
     self._WriteConfig()
 
   def _SetInstanceStatus(self, instance_name, status):
     self._WriteConfig()
 
   def _SetInstanceStatus(self, instance_name, status):
@@ -462,6 +600,7 @@ class ConfigWriter:
     if instance_name not in self._config_data.instances:
       raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
     del self._config_data.instances[instance_name]
     if instance_name not in self._config_data.instances:
       raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
     del self._config_data.instances[instance_name]
+    self._config_data.cluster.serial_no += 1
     self._WriteConfig()
 
   @locking.ssynchronized(_config_lock)
     self._WriteConfig()
 
   @locking.ssynchronized(_config_lock)
@@ -490,6 +629,7 @@ class ConfigWriter:
                                                            disk.iv_name))
 
     self._config_data.instances[inst.name] = inst
                                                            disk.iv_name))
 
     self._config_data.instances[inst.name] = inst
+    self._config_data.cluster.serial_no += 1
     self._WriteConfig()
 
   @locking.ssynchronized(_config_lock)
     self._WriteConfig()
 
   @locking.ssynchronized(_config_lock)
@@ -584,6 +724,7 @@ class ConfigWriter:
     self._OpenConfig()
     node.serial_no = 1
     self._config_data.nodes[node.name] = node
     self._OpenConfig()
     node.serial_no = 1
     self._config_data.nodes[node.name] = node
+    self._config_data.cluster.serial_no += 1
     self._WriteConfig()
 
   @locking.ssynchronized(_config_lock)
     self._WriteConfig()
 
   @locking.ssynchronized(_config_lock)
@@ -598,6 +739,7 @@ class ConfigWriter:
       raise errors.ConfigurationError("Unknown node '%s'" % node_name)
 
     del self._config_data.nodes[node_name]
       raise errors.ConfigurationError("Unknown node '%s'" % node_name)
 
     del self._config_data.nodes[node_name]
+    self._config_data.cluster.serial_no += 1
     self._WriteConfig()
 
   @locking.ssynchronized(_config_lock, shared=1)
     self._WriteConfig()
 
   @locking.ssynchronized(_config_lock, shared=1)
@@ -669,13 +811,6 @@ class ConfigWriter:
                     for node in self._UnlockedGetNodeList()])
     return my_dict
 
                     for node in self._UnlockedGetNodeList()])
     return my_dict
 
-  @locking.ssynchronized(_config_lock, shared=1)
-  def DumpConfig(self):
-    """Return the entire configuration of the cluster.
-    """
-    self._OpenConfig()
-    return self._config_data
-
   def _BumpSerialNo(self):
     """Bump up the serial number of the config.
 
   def _BumpSerialNo(self):
     """Bump up the serial number of the config.
 
@@ -702,9 +837,6 @@ class ConfigWriter:
       # data is current, so skip loading of config file
       return
 
       # data is current, so skip loading of config file
       return
 
-    # Make sure the configuration has the right version
-    ValidateConfig()
-
     f = open(self._cfg_file, 'r')
     try:
       try:
     f = open(self._cfg_file, 'r')
     try:
       try:
@@ -713,6 +845,10 @@ class ConfigWriter:
         raise errors.ConfigurationError(err)
     finally:
       f.close()
         raise errors.ConfigurationError(err)
     finally:
       f.close()
+
+    # Make sure the configuration has the right version
+    _ValidateConfig(data)
+
     if (not hasattr(data, 'cluster') or
         not hasattr(data.cluster, 'rsahostkeypub')):
       raise errors.ConfigurationError("Incomplete configuration"
     if (not hasattr(data, 'cluster') or
         not hasattr(data.cluster, 'rsahostkeypub')):
       raise errors.ConfigurationError("Incomplete configuration"
@@ -779,36 +915,28 @@ class ConfigWriter:
     self._DistributeConfig()
 
   @locking.ssynchronized(_config_lock)
     self._DistributeConfig()
 
   @locking.ssynchronized(_config_lock)
-  def InitConfig(self, node, primary_ip, secondary_ip,
-                 hostkeypub, mac_prefix, vg_name, def_bridge):
+  def InitConfig(self, version, cluster_config, master_node_config):
     """Create the initial cluster configuration.
 
     It will contain the current node, which will also be the master
     """Create the initial cluster configuration.
 
     It will contain the current node, which will also be the master
-    node, and no instances or operating systmes.
+    node, and no instances.
 
 
-    Args:
-      node: the nodename of the initial node
-      primary_ip: the IP address of the current host
-      secondary_ip: the secondary IP of the current host or None
-      hostkeypub: the public hostkey of this host
-
-    """
-    hu_port = constants.FIRST_DRBD_PORT - 1
-    globalconfig = objects.Cluster(serial_no=1,
-                                   rsahostkeypub=hostkeypub,
-                                   highest_used_port=hu_port,
-                                   mac_prefix=mac_prefix,
-                                   volume_group_name=vg_name,
-                                   default_bridge=def_bridge,
-                                   tcpudp_port_pool=set())
-    if secondary_ip is None:
-      secondary_ip = primary_ip
-    nodeconfig = objects.Node(name=node, primary_ip=primary_ip,
-                              secondary_ip=secondary_ip, serial_no=1)
-
-    self._config_data = objects.ConfigData(nodes={node: nodeconfig},
+    @type version: int
+    @param version: Configuration version
+    @type cluster_config: objects.Cluster
+    @param cluster_config: Cluster configuration
+    @type master_node_config: objects.Node
+    @param master_node_config: Master node configuration
+
+    """
+    nodes = {
+      master_node_config.name: master_node_config,
+      }
+
+    self._config_data = objects.ConfigData(version=version,
+                                           cluster=cluster_config,
+                                           nodes=nodes,
                                            instances={},
                                            instances={},
-                                           cluster=globalconfig,
                                            serial_no=1)
     self._WriteConfig()
 
                                            serial_no=1)
     self._WriteConfig()
 
@@ -827,6 +955,7 @@ class ConfigWriter:
     """
     self._OpenConfig()
     self._config_data.cluster.volume_group_name = vg_name
     """
     self._OpenConfig()
     self._config_data.cluster.volume_group_name = vg_name
+    self._config_data.cluster.serial_no += 1
     self._WriteConfig()
 
   @locking.ssynchronized(_config_lock, shared=1)
     self._WriteConfig()
 
   @locking.ssynchronized(_config_lock, shared=1)