X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/0bc8432bfe956bb8fa4e624a6850e3d6fa5077a3..2492231f7381ce33f2164322f799c84b2d7cceef:/lib/confd/querylib.py diff --git a/lib/confd/querylib.py b/lib/confd/querylib.py index af0c32d..e1e323f 100644 --- a/lib/confd/querylib.py +++ b/lib/confd/querylib.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +# # # Copyright (C) 2009, Google Inc. @@ -27,11 +27,15 @@ import logging from ganeti import constants + # constants for some common errors to return from a query 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): """Confd Query base class. @@ -46,7 +50,7 @@ class ConfdQuery(object): """ self.reader = reader - def Exec(self, query): + def Exec(self, query): # pylint: disable-msg=R0201,W0613 """Process a single UDP request from a client. Different queries should override this function, which by defaults returns @@ -66,7 +70,8 @@ class ConfdQuery(object): class PingQuery(ConfdQuery): """An empty confd query. - It will return success on an empty argument, and an error on any other argument. + It will return success on an empty argument, and an error on any other + argument. """ def Exec(self, query): @@ -83,6 +88,48 @@ class PingQuery(ConfdQuery): return status, answer +class ClusterMasterQuery(ConfdQuery): + """Cluster master query. + + 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 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: + logging.debug("Invalid master query argument: not dict or empty") + return QUERY_ARGUMENT_ERROR + + return status, answer + + class NodeRoleQuery(ConfdQuery): """A query for the role of a node. @@ -116,32 +163,137 @@ 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 + elif isinstance(query, basestring): + # 2.1 beta1 and beta2 mode, to be deprecated for 2.2 + instances_list = [query] + network_link = None + mode = constants.CONFD_REQQ_IP + 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): + """A query for nodes primary IPs. + + It returns the list of nodes primary IPs. + + """ + def Exec(self, query): + """NodesPipsQuery main execution. + """ + if query is None: + status = constants.CONFD_REPL_STATUS_OK + answer = self.reader.GetNodesPrimaryIps() + else: + status = constants.CONFD_REPL_STATUS_ERROR + answer = "non-empty node primary IPs query" + + return status, answer + + +class MasterCandidatesPipsQuery(ConfdQuery): + """A query for master candidates primary IPs. + + It returns the list of master candidates primary IPs. + + """ + def Exec(self, query): + """MasterCandidatesPipsQuery main execution. + + """ + if query is None: + status = constants.CONFD_REPL_STATUS_OK + answer = self.reader.GetMasterCandidatesPrimaryIps() + else: + status = constants.CONFD_REPL_STATUS_ERROR + answer = "non-empty master candidates primary IPs query" + + 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