Package ganeti :: Package confd :: Module server
[hide private]
[frames] | no frames]

Source Code for Module ganeti.confd.server

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2009, 2012 Google Inc. 
  5  # 
  6  # This program is free software; you can redistribute it and/or modify 
  7  # it under the terms of the GNU General Public License as published by 
  8  # the Free Software Foundation; either version 2 of the License, or 
  9  # (at your option) any later version. 
 10  # 
 11  # This program is distributed in the hope that it will be useful, but 
 12  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 14  # General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU General Public License 
 17  # along with this program; if not, write to the Free Software 
 18  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
 19  # 02110-1301, USA. 
 20   
 21   
 22  """Ganeti configuration daemon server library. 
 23   
 24  Ganeti-confd is a daemon to query master candidates for configuration values. 
 25  It uses UDP+HMAC for authentication with a global cluster key. 
 26   
 27  """ 
 28   
 29  import logging 
 30  import time 
 31   
 32  from ganeti import constants 
 33  from ganeti import objects 
 34  from ganeti import errors 
 35  from ganeti import utils 
 36  from ganeti import serializer 
 37  from ganeti import ssconf 
 38   
 39  from ganeti.confd import querylib 
 40   
 41   
42 -class ConfdProcessor(object):
43 """A processor for confd requests. 44 45 @ivar reader: confd SimpleConfigReader 46 @ivar disabled: whether confd serving is disabled 47 48 """ 49 DISPATCH_TABLE = { 50 constants.CONFD_REQ_PING: querylib.PingQuery, 51 constants.CONFD_REQ_NODE_ROLE_BYNAME: querylib.NodeRoleQuery, 52 constants.CONFD_REQ_NODE_PIP_BY_INSTANCE_IP: 53 querylib.InstanceIpToNodePrimaryIpQuery, 54 constants.CONFD_REQ_CLUSTER_MASTER: querylib.ClusterMasterQuery, 55 constants.CONFD_REQ_NODE_PIP_LIST: querylib.NodesPipsQuery, 56 constants.CONFD_REQ_MC_PIP_LIST: querylib.MasterCandidatesPipsQuery, 57 constants.CONFD_REQ_INSTANCES_IPS_LIST: querylib.InstancesIpsQuery, 58 constants.CONFD_REQ_NODE_DRBD: querylib.NodeDrbdQuery, 59 } 60
61 - def __init__(self):
62 """Constructor for ConfdProcessor 63 64 """ 65 self.disabled = True 66 self.hmac_key = utils.ReadFile(constants.CONFD_HMAC_KEY) 67 self.reader = None 68 assert \ 69 not constants.CONFD_REQS.symmetric_difference(self.DISPATCH_TABLE), \ 70 "DISPATCH_TABLE is unaligned with CONFD_REQS"
71
72 - def Enable(self):
73 try: 74 self.reader = ssconf.SimpleConfigReader() 75 self.disabled = False 76 except errors.ConfigurationError: 77 self.disabled = True 78 raise
79
80 - def Disable(self):
81 self.disabled = True 82 self.reader = None
83
84 - def ExecQuery(self, payload_in, ip, port):
85 """Process a single UDP request from a client. 86 87 @type payload_in: string 88 @param payload_in: request raw data 89 @type ip: string 90 @param ip: source ip address 91 @param port: integer 92 @type port: source port 93 94 """ 95 if self.disabled: 96 logging.debug("Confd is disabled. Ignoring query.") 97 return 98 try: 99 request = self.ExtractRequest(payload_in) 100 reply, rsalt = self.ProcessRequest(request) 101 payload_out = self.PackReply(reply, rsalt) 102 return payload_out 103 except errors.ConfdRequestError, err: 104 logging.info("Ignoring broken query from %s:%d: %s", ip, port, err) 105 return None
106
107 - def ExtractRequest(self, payload):
108 """Extracts a ConfdRequest object from a serialized hmac signed string. 109 110 This functions also performs signature/timestamp validation. 111 112 """ 113 current_time = time.time() 114 logging.debug("Extracting request with size: %d", len(payload)) 115 try: 116 (message, salt) = serializer.LoadSigned(payload, self.hmac_key) 117 except errors.SignatureError, err: 118 msg = "invalid signature: %s" % err 119 raise errors.ConfdRequestError(msg) 120 try: 121 message_timestamp = int(salt) 122 except (ValueError, TypeError): 123 msg = "non-integer timestamp: %s" % salt 124 raise errors.ConfdRequestError(msg) 125 126 skew = abs(current_time - message_timestamp) 127 if skew > constants.CONFD_MAX_CLOCK_SKEW: 128 msg = "outside time range (skew: %d)" % skew 129 raise errors.ConfdRequestError(msg) 130 131 try: 132 request = objects.ConfdRequest.FromDict(message) 133 except AttributeError, err: 134 raise errors.ConfdRequestError(str(err)) 135 136 return request
137
138 - def ProcessRequest(self, request):
139 """Process one ConfdRequest request, and produce an answer 140 141 @type request: L{objects.ConfdRequest} 142 @rtype: (L{objects.ConfdReply}, string) 143 @return: tuple of reply and salt to add to the signature 144 145 """ 146 logging.debug("Processing request: %s", request) 147 if request.protocol != constants.CONFD_PROTOCOL_VERSION: 148 msg = "wrong protocol version %d" % request.protocol 149 raise errors.ConfdRequestError(msg) 150 151 if request.type not in constants.CONFD_REQS: 152 msg = "wrong request type %d" % request.type 153 raise errors.ConfdRequestError(msg) 154 155 rsalt = request.rsalt 156 if not rsalt: 157 msg = "missing requested salt" 158 raise errors.ConfdRequestError(msg) 159 160 query_object = self.DISPATCH_TABLE[request.type](self.reader) 161 status, answer = query_object.Exec(request.query) 162 reply = objects.ConfdReply( 163 protocol=constants.CONFD_PROTOCOL_VERSION, 164 status=status, 165 answer=answer, 166 serial=self.reader.GetConfigSerialNo(), 167 ) 168 169 logging.debug("Sending reply: %s", reply) 170 171 return (reply, rsalt)
172
173 - def PackReply(self, reply, rsalt):
174 """Serialize and sign the given reply, with salt rsalt 175 176 @type reply: L{objects.ConfdReply} 177 @type rsalt: string 178 179 """ 180 return serializer.DumpSigned(reply.ToDict(), self.hmac_key, rsalt)
181