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, 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 } 59
60 - def __init__(self):
61 """Constructor for ConfdProcessor 62 63 """ 64 self.disabled = True 65 self.hmac_key = utils.ReadFile(constants.CONFD_HMAC_KEY) 66 self.reader = None 67 assert \ 68 not constants.CONFD_REQS.symmetric_difference(self.DISPATCH_TABLE), \ 69 "DISPATCH_TABLE is unaligned with CONFD_REQS"
70
71 - def Enable(self):
72 try: 73 self.reader = ssconf.SimpleConfigReader() 74 self.disabled = False 75 except errors.ConfigurationError: 76 self.disabled = True 77 raise
78
79 - def Disable(self):
80 self.disabled = True 81 self.reader = None
82
83 - def ExecQuery(self, payload_in, ip, port):
84 """Process a single UDP request from a client. 85 86 @type payload_in: string 87 @param payload_in: request raw data 88 @type ip: string 89 @param ip: source ip address 90 @param port: integer 91 @type port: source port 92 93 """ 94 if self.disabled: 95 logging.debug('Confd is disabled. Ignoring query.') 96 return 97 try: 98 request = self.ExtractRequest(payload_in) 99 reply, rsalt = self.ProcessRequest(request) 100 payload_out = self.PackReply(reply, rsalt) 101 return payload_out 102 except errors.ConfdRequestError, err: 103 logging.info('Ignoring broken query from %s:%d: %s', ip, port, err) 104 return None
105
106 - def ExtractRequest(self, payload):
107 """Extracts a ConfdRequest object from a serialized hmac signed string. 108 109 This functions also performs signature/timestamp validation. 110 111 """ 112 current_time = time.time() 113 logging.debug("Extracting request with size: %d", len(payload)) 114 try: 115 (message, salt) = serializer.LoadSigned(payload, self.hmac_key) 116 except errors.SignatureError, err: 117 msg = "invalid signature: %s" % err 118 raise errors.ConfdRequestError(msg) 119 try: 120 message_timestamp = int(salt) 121 except (ValueError, TypeError): 122 msg = "non-integer timestamp: %s" % salt 123 raise errors.ConfdRequestError(msg) 124 125 skew = abs(current_time - message_timestamp) 126 if skew > constants.CONFD_MAX_CLOCK_SKEW: 127 msg = "outside time range (skew: %d)" % skew 128 raise errors.ConfdRequestError(msg) 129 130 try: 131 request = objects.ConfdRequest.FromDict(message) 132 except AttributeError, err: 133 raise errors.ConfdRequestError('%s' % err) 134 135 return request
136
137 - def ProcessRequest(self, request):
138 """Process one ConfdRequest request, and produce an answer 139 140 @type request: L{objects.ConfdRequest} 141 @rtype: (L{objects.ConfdReply}, string) 142 @return: tuple of reply and salt to add to the signature 143 144 """ 145 logging.debug("Processing request: %s", request) 146 if request.protocol != constants.CONFD_PROTOCOL_VERSION: 147 msg = "wrong protocol version %d" % request.protocol 148 raise errors.ConfdRequestError(msg) 149 150 if request.type not in constants.CONFD_REQS: 151 msg = "wrong request type %d" % request.type 152 raise errors.ConfdRequestError(msg) 153 154 rsalt = request.rsalt 155 if not rsalt: 156 msg = "missing requested salt" 157 raise errors.ConfdRequestError(msg) 158 159 query_object = self.DISPATCH_TABLE[request.type](self.reader) 160 status, answer = query_object.Exec(request.query) 161 reply = objects.ConfdReply( 162 protocol=constants.CONFD_PROTOCOL_VERSION, 163 status=status, 164 answer=answer, 165 serial=self.reader.GetConfigSerialNo(), 166 ) 167 168 logging.debug("Sending reply: %s", reply) 169 170 return (reply, rsalt)
171
172 - def PackReply(self, reply, rsalt):
173 """Serialize and sign the given reply, with salt rsalt 174 175 @type reply: L{objects.ConfdReply} 176 @type rsalt: string 177 178 """ 179 return serializer.DumpSigned(reply.ToDict(), self.hmac_key, rsalt)
180