cmdlib: Drop use of “len(…) != 0”
[ganeti-local] / lib / confd / querylib.py
index 11dad04..e231a14 100644 (file)
@@ -1,7 +1,7 @@
 #
 #
 
-# Copyright (C) 2009, Google Inc.
+# Copyright (C) 2009 Google Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -33,6 +33,8 @@ QUERY_UNKNOWN_ENTRY_ERROR = (constants.CONFD_REPL_STATUS_ERROR,
                              constants.CONFD_ERROR_UNKNOWN_ENTRY)
 QUERY_INTERNAL_ERROR = (constants.CONFD_REPL_STATUS_ERROR,
                         constants.CONFD_ERROR_INTERNAL)
+QUERY_ARGUMENT_ERROR = (constants.CONFD_REPL_STATUS_ERROR,
+                        constants.CONFD_ERROR_ARGUMENT)
 
 
 class ConfdQuery(object):
@@ -48,7 +50,7 @@ class ConfdQuery(object):
     """
     self.reader = reader
 
-  def Exec(self, query):
+  def Exec(self, query): # pylint: disable=R0201,W0613
     """Process a single UDP request from a client.
 
     Different queries should override this function, which by defaults returns
@@ -92,16 +94,38 @@ class ClusterMasterQuery(ConfdQuery):
   It accepts no arguments, and returns the current cluster master.
 
   """
+  def _GetMasterNode(self):
+    return self.reader.GetMasterNode()
+
   def Exec(self, query):
     """ClusterMasterQuery main execution
 
     """
-    if query is None:
+    if isinstance(query, dict):
+      if constants.CONFD_REQQ_FIELDS in query:
+        status = constants.CONFD_REPL_STATUS_OK
+        req_fields = query[constants.CONFD_REQQ_FIELDS]
+        if not isinstance(req_fields, (list, tuple)):
+          logging.debug("FIELDS request should be a list")
+          return QUERY_ARGUMENT_ERROR
+
+        answer = []
+        for field in req_fields:
+          if field == constants.CONFD_REQFIELD_NAME:
+            answer.append(self._GetMasterNode())
+          elif field == constants.CONFD_REQFIELD_IP:
+            answer.append(self.reader.GetMasterIP())
+          elif field == constants.CONFD_REQFIELD_MNODE_PIP:
+            answer.append(self.reader.GetNodePrimaryIp(self._GetMasterNode()))
+      else:
+        logging.debug("missing FIELDS in query dict")
+        return QUERY_ARGUMENT_ERROR
+    elif not query:
       status = constants.CONFD_REPL_STATUS_OK
       answer = self.reader.GetMasterNode()
     else:
-      status = constants.CONFD_REPL_STATUS_ERROR
-      answer = 'master query accepts no query argument'
+      logging.debug("Invalid master query argument: not dict or empty")
+      return QUERY_ARGUMENT_ERROR
 
     return status, answer
 
@@ -139,34 +163,77 @@ class NodeRoleQuery(ConfdQuery):
 
 
 class InstanceIpToNodePrimaryIpQuery(ConfdQuery):
-  """A query for the location of an instance's ip.
-
-  It returns the primary ip of the node hosting the instance having the
-  requested ip address, or an error if no such address is known.
+  """A query for the location of one or more instance's ips.
 
   """
   def Exec(self, query):
     """InstanceIpToNodePrimaryIpQuery main execution.
 
+    @type query: string or dict
+    @param query: instance ip or dict containing:
+                  constants.CONFD_REQQ_LINK: nic link (optional)
+                  constants.CONFD_REQQ_IPLIST: list of ips
+                  constants.CONFD_REQQ_IP: single ip
+                  (one IP type request is mandatory)
+    @rtype: (integer, ...)
+    @return: ((status, answer) or (success, [(status, answer)...])
+
     """
-    instance_ip = query
-    instance = self.reader.GetInstanceByIp(instance_ip)
-    if instance is None:
-      return QUERY_UNKNOWN_ENTRY_ERROR
+    if isinstance(query, dict):
+      if constants.CONFD_REQQ_IP in query:
+        instances_list = [query[constants.CONFD_REQQ_IP]]
+        mode = constants.CONFD_REQQ_IP
+      elif constants.CONFD_REQQ_IPLIST in query:
+        instances_list = query[constants.CONFD_REQQ_IPLIST]
+        mode = constants.CONFD_REQQ_IPLIST
+      else:
+        logging.debug("missing IP or IPLIST in query dict")
+        return QUERY_ARGUMENT_ERROR
+
+      if constants.CONFD_REQQ_LINK in query:
+        network_link = query[constants.CONFD_REQQ_LINK]
+      else:
+        network_link = None # default will be used
+    else:
+      logging.debug("Invalid query argument type for: %s", query)
+      return QUERY_ARGUMENT_ERROR
+
+    pnodes_list = []
+
+    for instance_ip in instances_list:
+      if not isinstance(instance_ip, basestring):
+        logging.debug("Invalid IP type for: %s", instance_ip)
+        return QUERY_ARGUMENT_ERROR
+
+      instance = self.reader.GetInstanceByLinkIp(instance_ip, network_link)
+      if not instance:
+        logging.debug("Unknown instance IP: %s", instance_ip)
+        pnodes_list.append(QUERY_UNKNOWN_ENTRY_ERROR)
+        continue
 
-    pnode = self.reader.GetInstancePrimaryNode(instance)
-    if pnode is None:
-      # this shouldn't happen
-      logging.error("Internal configuration inconsistent (instance-to-pnode)")
-      return QUERY_INTERNAL_ERROR
+      pnode = self.reader.GetInstancePrimaryNode(instance)
+      if not pnode:
+        logging.error("Instance '%s' doesn't have an associated primary"
+                      " node", instance)
+        pnodes_list.append(QUERY_INTERNAL_ERROR)
+        continue
 
-    pnode_primary_ip = self.reader.GetNodePrimaryIp(pnode)
-    if pnode_primary_ip is None:
-      # this shouldn't happen
-      logging.error("Internal configuration inconsistent (node-to-primary-ip)")
-      return QUERY_INTERNAL_ERROR
+      pnode_primary_ip = self.reader.GetNodePrimaryIp(pnode)
+      if not pnode_primary_ip:
+        logging.error("Primary node '%s' doesn't have an associated"
+                      " primary IP", pnode)
+        pnodes_list.append(QUERY_INTERNAL_ERROR)
+        continue
 
-    return constants.CONFD_REPL_STATUS_OK, pnode_primary_ip
+      pnodes_list.append((constants.CONFD_REPL_STATUS_OK, pnode_primary_ip))
+
+    # If a single ip was requested, return a single answer, otherwise
+    # the whole list, with a success status (since each entry has its
+    # own success/failure)
+    if mode == constants.CONFD_REQQ_IP:
+      return pnodes_list[0]
+
+    return constants.CONFD_REPL_STATUS_OK, pnodes_list
 
 
 class NodesPipsQuery(ConfdQuery):
@@ -208,3 +275,20 @@ class MasterCandidatesPipsQuery(ConfdQuery):
 
     return status, answer
 
+
+class InstancesIpsQuery(ConfdQuery):
+  """A query for instances IPs.
+
+  It returns the list of IPs of NICs connected to the requested link or all the
+  instances IPs if no link is submitted.
+
+  """
+  def Exec(self, query):
+    """InstancesIpsQuery main execution.
+
+    """
+    link = query
+    status = constants.CONFD_REPL_STATUS_OK
+    answer = self.reader.GetInstancesIps(link)
+
+    return status, answer