LUInstanceCreate: Acquire node allocation lock
[ganeti-local] / lib / query.py
index d041ad5..7efdaa4 100644 (file)
@@ -71,6 +71,10 @@ from ganeti.constants import (QFT_UNKNOWN, QFT_TEXT, QFT_BOOL, QFT_NUMBER,
                               RS_NORMAL, RS_UNKNOWN, RS_NODATA,
                               RS_UNAVAIL, RS_OFFLINE)
 
+(NETQ_CONFIG,
+ NETQ_GROUP,
+ NETQ_STATS,
+ NETQ_INST) = range(300, 304)
 
 # Constants for requesting data from the caller/data provider. Each property
 # collected/computed separately by the data provider should have its own to
@@ -1339,15 +1343,13 @@ def _BuildNodeFields():
   # Add simple fields
   fields.extend([
     (_MakeField(name, title, kind, doc), NQ_CONFIG, flags, _GetItemAttr(name))
-    for (name, (title, kind, flags, doc)) in _NODE_SIMPLE_FIELDS.items()
-    ])
+    for (name, (title, kind, flags, doc)) in _NODE_SIMPLE_FIELDS.items()])
 
   # Add fields requiring live data
   fields.extend([
     (_MakeField(name, title, kind, doc), NQ_LIVE, 0,
      compat.partial(_GetLiveNodeField, nfield, kind))
-    for (name, (title, kind, nfield, doc)) in _NODE_LIVE_FIELDS.items()
-    ])
+    for (name, (title, kind, nfield, doc)) in _NODE_LIVE_FIELDS.items()])
 
   # Add timestamps
   fields.extend(_GetItemTimestampFields(NQ_CONFIG))
@@ -1546,6 +1548,20 @@ def _GetInstNic(index, cb):
   return fn
 
 
+def _GetInstNicNetwork(ctx, _, nic): # pylint: disable=W0613
+  """Get a NIC's Network.
+
+  @type ctx: L{InstanceQueryData}
+  @type nic: L{objects.NIC}
+  @param nic: NIC object
+
+  """
+  if nic.network is None:
+    return _FS_UNAVAIL
+  else:
+    return nic.network
+
+
 def _GetInstNicIp(ctx, _, nic): # pylint: disable=W0613
   """Get a NIC's IP address.
 
@@ -1657,6 +1673,9 @@ def _GetInstanceNetworkFields():
     (_MakeField("nic.bridges", "NIC_bridges", QFT_OTHER,
                 "List containing each network interface's bridge"),
      IQ_CONFIG, 0, _GetInstAllNicBridges),
+    (_MakeField("nic.networks", "NIC_networks", QFT_OTHER,
+                "List containing each interface's network"), IQ_CONFIG, 0,
+     lambda ctx, inst: [nic.network for nic in inst.nics]),
     ]
 
   # NICs by number
@@ -1678,6 +1697,9 @@ def _GetInstanceNetworkFields():
       (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, QFT_TEXT,
                   "Bridge of %s network interface" % numtext),
        IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicBridge)),
+      (_MakeField("nic.network/%s" % i, "NicNetwork/%s" % i, QFT_TEXT,
+                  "Network of %s network interface" % numtext),
+       IQ_CONFIG, 0, _GetInstNic(i, _GetInstNicNetwork)),
       ])
 
   aliases = [
@@ -1687,6 +1709,7 @@ def _GetInstanceNetworkFields():
     ("bridge", "nic.bridge/0"),
     ("nic_mode", "nic.mode/0"),
     ("nic_link", "nic.link/0"),
+    ("nic_network", "nic.network/0"),
     ]
 
   return (fields, aliases)
@@ -1747,8 +1770,7 @@ def _GetInstanceDiskFields():
     (_MakeField("disk.size/%s" % i, "Disk/%s" % i, QFT_UNIT,
                 "Disk size of %s disk" % utils.FormatOrdinal(i + 1)),
      IQ_CONFIG, 0, _GetInstDiskSize(i))
-    for i in range(constants.MAX_DISKS)
-    ])
+    for i in range(constants.MAX_DISKS)])
 
   return fields
 
@@ -1796,8 +1818,7 @@ def _GetInstanceParameterFields():
                 _VTToQFT[kind], "The \"%s\" hypervisor parameter" % name),
      IQ_CONFIG, 0, _GetInstHvParam(name))
     for name, kind in constants.HVS_PARAMETER_TYPES.items()
-    if name not in constants.HVC_GLOBALS
-    ])
+    if name not in constants.HVC_GLOBALS])
 
   # BE params
   def _GetInstBeParam(name):
@@ -1808,8 +1829,7 @@ def _GetInstanceParameterFields():
                 constants.BES_PARAMETER_TITLES.get(name, "be/%s" % name),
                 _VTToQFT[kind], "The \"%s\" backend parameter" % name),
      IQ_CONFIG, 0, _GetInstBeParam(name))
-    for name, kind in constants.BES_PARAMETER_TYPES.items()
-    ])
+    for name, kind in constants.BES_PARAMETER_TYPES.items()])
 
   return fields
 
@@ -1912,8 +1932,7 @@ def _BuildInstanceFields():
   # Add simple fields
   fields.extend([
     (_MakeField(name, title, kind, doc), IQ_CONFIG, flags, _GetItemAttr(name))
-    for (name, (title, kind, flags, doc)) in _INST_SIMPLE_FIELDS.items()
-    ])
+    for (name, (title, kind, flags, doc)) in _INST_SIMPLE_FIELDS.items()])
 
   # Fields requiring talking to the node
   fields.extend([
@@ -2406,21 +2425,152 @@ def _BuildClusterFields():
   fields.extend([
     (_MakeField(name, title, kind, doc), CQ_CONFIG, flags, _GetItemAttr(name))
     for (name, (title, kind, flags, doc)) in _CLUSTER_SIMPLE_FIELDS.items()
-    ])
+    ],)
 
   # Version fields
   fields.extend([
     (_MakeField(name, title, kind, doc), None, 0, _StaticValue(value))
-    for (name, (title, kind, value, doc)) in _CLUSTER_VERSION_FIELDS.items()
-    ])
+    for (name, (title, kind, value, doc)) in _CLUSTER_VERSION_FIELDS.items()])
 
   # Add timestamps
   fields.extend(_GetItemTimestampFields(CQ_CONFIG))
 
   return _PrepareFieldList(fields, [
-    ("name", "cluster_name"),
+    ("name", "cluster_name")])
+
+
+class NetworkQueryData:
+  """Data container for network data queries.
+
+  """
+  def __init__(self, networks, network_to_groups,
+               network_to_instances, stats):
+    """Initializes this class.
+
+    @param networks: List of network objects
+    @type network_to_groups: dict; network UUID as key
+    @param network_to_groups: Per-network list of groups
+    @type network_to_instances: dict; network UUID as key
+    @param network_to_instances: Per-network list of instances
+    @type stats: dict; network UUID as key
+    @param stats: Per-network usage statistics
+
+    """
+    self.networks = networks
+    self.network_to_groups = network_to_groups
+    self.network_to_instances = network_to_instances
+    self.stats = stats
+
+  def __iter__(self):
+    """Iterate over all networks.
+
+    """
+    for net in self.networks:
+      if self.stats:
+        self.curstats = self.stats.get(net.uuid, None)
+      else:
+        self.curstats = None
+      yield net
+
+
+_NETWORK_SIMPLE_FIELDS = {
+  "name": ("Network", QFT_TEXT, 0, "The network"),
+  "network": ("Subnet", QFT_TEXT, 0, "The subnet"),
+  "gateway": ("Gateway", QFT_OTHER, 0, "The gateway"),
+  "network6": ("IPv6Subnet", QFT_OTHER, 0, "The ipv6 subnet"),
+  "gateway6": ("IPv6Gateway", QFT_OTHER, 0, "The ipv6 gateway"),
+  "mac_prefix": ("MacPrefix", QFT_OTHER, 0, "The mac prefix"),
+  "network_type": ("NetworkType", QFT_OTHER, 0, "The network type"),
+  "serial_no": ("SerialNo", QFT_NUMBER, 0, _SERIAL_NO_DOC % "Network"),
+  "uuid": ("UUID", QFT_TEXT, 0, "Network UUID"),
+  }
+
+
+_NETWORK_STATS_FIELDS = {
+  "free_count": ("FreeCount", QFT_NUMBER, 0, "How many addresses are free"),
+  "reserved_count": ("ReservedCount", QFT_NUMBER, 0,
+                     "How many addresses are reserved"),
+  "map": ("Map", QFT_TEXT, 0, "The actual mapping"),
+  "external_reservations": ("ExternalReservations", QFT_TEXT, 0,
+                            "The external reservations"),
+  }
+
+
+def _GetNetworkStatsField(field, kind, ctx, _):
+  """Gets the value of a "stats" field from L{NetworkQueryData}.
+
+  @param field: Field name
+  @param kind: Data kind, one of L{constants.QFT_ALL}
+  @type ctx: L{NetworkQueryData}
+
+  """
+
+  try:
+    value = ctx.curstats[field]
+  except KeyError:
+    return _FS_UNAVAIL
+
+  if kind == QFT_TEXT:
+    return value
+
+  assert kind in (QFT_NUMBER, QFT_UNIT)
+
+  # Try to convert into number
+  try:
+    return int(value)
+  except (ValueError, TypeError):
+    logging.exception("Failed to convert network field '%s' (value %r) to int",
+                      field, value)
+    return _FS_UNAVAIL
+
+
+def _BuildNetworkFields():
+  """Builds list of fields for network queries.
+
+  """
+  fields = [
+    (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), IQ_CONFIG, 0,
+     lambda ctx, inst: list(inst.GetTags())),
+    ]
+
+  # Add simple fields
+  fields.extend([
+    (_MakeField(name, title, kind, doc),
+     NETQ_CONFIG, 0, _GetItemAttr(name))
+     for (name, (title, kind, _, doc)) in _NETWORK_SIMPLE_FIELDS.items()])
+
+  def _GetLength(getter):
+    return lambda ctx, network: len(getter(ctx)[network.uuid])
+
+  def _GetSortedList(getter):
+    return lambda ctx, network: utils.NiceSort(getter(ctx)[network.uuid])
+
+  network_to_groups = operator.attrgetter("network_to_groups")
+  network_to_instances = operator.attrgetter("network_to_instances")
+
+  # Add fields for node groups
+  fields.extend([
+    (_MakeField("group_cnt", "NodeGroups", QFT_NUMBER, "Number of nodegroups"),
+     NETQ_GROUP, 0, _GetLength(network_to_groups)),
+    (_MakeField("group_list", "GroupList", QFT_OTHER, "List of nodegroups"),
+     NETQ_GROUP, 0, _GetSortedList(network_to_groups)),
+    ])
+
+  # Add fields for instances
+  fields.extend([
+    (_MakeField("inst_cnt", "Instances", QFT_NUMBER, "Number of instances"),
+     NETQ_INST, 0, _GetLength(network_to_instances)),
+    (_MakeField("inst_list", "InstanceList", QFT_OTHER, "List of instances"),
+     NETQ_INST, 0, _GetSortedList(network_to_instances)),
     ])
 
+  # Add fields for usage statistics
+  fields.extend([
+    (_MakeField(name, title, kind, doc), NETQ_STATS, 0,
+    compat.partial(_GetNetworkStatsField, name, kind))
+    for (name, (title, kind, _, doc)) in _NETWORK_STATS_FIELDS.items()])
+
+  return _PrepareFieldList(fields, [])
 
 #: Fields for cluster information
 CLUSTER_FIELDS = _BuildClusterFields()
@@ -2446,6 +2596,9 @@ JOB_FIELDS = _BuildJobFields()
 #: Fields available for exports
 EXPORT_FIELDS = _BuildExportFields()
 
+#: Fields available for network queries
+NETWORK_FIELDS = _BuildNetworkFields()
+
 #: All available resources
 ALL_FIELDS = {
   constants.QR_CLUSTER: CLUSTER_FIELDS,
@@ -2456,6 +2609,7 @@ ALL_FIELDS = {
   constants.QR_OS: OS_FIELDS,
   constants.QR_JOB: JOB_FIELDS,
   constants.QR_EXPORT: EXPORT_FIELDS,
+  constants.QR_NETWORK: NETWORK_FIELDS,
   }
 
 #: All available field lists