Package ganeti :: Module netutils
[hide private]
[frames] | no frames]

Source Code for Module ganeti.netutils

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2010 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 network utility module. 
 23   
 24  This module holds functions that can be used in both daemons (all) and 
 25  the command line scripts. 
 26   
 27  """ 
 28   
 29   
 30  import errno 
 31  import re 
 32  import socket 
 33  import struct 
 34  import IN 
 35   
 36  from ganeti import constants 
 37  from ganeti import errors 
 38   
 39  # Structure definition for getsockopt(SOL_SOCKET, SO_PEERCRED, ...): 
 40  # struct ucred { pid_t pid; uid_t uid; gid_t gid; }; 
 41  # 
 42  # The GNU C Library defines gid_t and uid_t to be "unsigned int" and 
 43  # pid_t to "int". 
 44  # 
 45  # IEEE Std 1003.1-2008: 
 46  # "nlink_t, uid_t, gid_t, and id_t shall be integer types" 
 47  # "blksize_t, pid_t, and ssize_t shall be signed integer types" 
 48  _STRUCT_UCRED = "iII" 
 49  _STRUCT_UCRED_SIZE = struct.calcsize(_STRUCT_UCRED) 
50 51 52 -def GetSocketCredentials(sock):
53 """Returns the credentials of the foreign process connected to a socket. 54 55 @param sock: Unix socket 56 @rtype: tuple; (number, number, number) 57 @return: The PID, UID and GID of the connected foreign process. 58 59 """ 60 peercred = sock.getsockopt(socket.SOL_SOCKET, IN.SO_PEERCRED, 61 _STRUCT_UCRED_SIZE) 62 return struct.unpack(_STRUCT_UCRED, peercred)
63
64 65 -def GetHostInfo(name=None):
66 """Lookup host name and raise an OpPrereqError for failures""" 67 68 try: 69 return HostInfo(name) 70 except errors.ResolverError, err: 71 raise errors.OpPrereqError("The given name (%s) does not resolve: %s" % 72 (err[0], err[2]), errors.ECODE_RESOLVER)
73
74 75 -class HostInfo:
76 """Class implementing resolver and hostname functionality 77 78 """ 79 _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$") 80
81 - def __init__(self, name=None):
82 """Initialize the host name object. 83 84 If the name argument is not passed, it will use this system's 85 name. 86 87 """ 88 if name is None: 89 name = self.SysName() 90 91 self.query = name 92 self.name, self.aliases, self.ipaddrs = self.LookupHostname(name) 93 self.ip = self.ipaddrs[0]
94
95 - def ShortName(self):
96 """Returns the hostname without domain. 97 98 """ 99 return self.name.split('.')[0]
100 101 @staticmethod
102 - def SysName():
103 """Return the current system's name. 104 105 This is simply a wrapper over C{socket.gethostname()}. 106 107 """ 108 return socket.gethostname()
109 110 @staticmethod
111 - def LookupHostname(hostname):
112 """Look up hostname 113 114 @type hostname: str 115 @param hostname: hostname to look up 116 117 @rtype: tuple 118 @return: a tuple (name, aliases, ipaddrs) as returned by 119 C{socket.gethostbyname_ex} 120 @raise errors.ResolverError: in case of errors in resolving 121 122 """ 123 try: 124 result = socket.gethostbyname_ex(hostname) 125 except (socket.gaierror, socket.herror, socket.error), err: 126 # hostname not found in DNS, or other socket exception in the 127 # (code, description format) 128 raise errors.ResolverError(hostname, err.args[0], err.args[1]) 129 130 return result
131 132 @classmethod
133 - def NormalizeName(cls, hostname):
134 """Validate and normalize the given hostname. 135 136 @attention: the validation is a bit more relaxed than the standards 137 require; most importantly, we allow underscores in names 138 @raise errors.OpPrereqError: when the name is not valid 139 140 """ 141 hostname = hostname.lower() 142 if (not cls._VALID_NAME_RE.match(hostname) or 143 # double-dots, meaning empty label 144 ".." in hostname or 145 # empty initial label 146 hostname.startswith(".")): 147 raise errors.OpPrereqError("Invalid hostname '%s'" % hostname, 148 errors.ECODE_INVAL) 149 if hostname.endswith("."): 150 hostname = hostname.rstrip(".") 151 return hostname
152
153 154 -def _GenericIsValidIP(family, ip):
155 """Generic internal version of ip validation. 156 157 @type family: int 158 @param family: socket.AF_INET | socket.AF_INET6 159 @type ip: str 160 @param ip: the address to be checked 161 @rtype: boolean 162 @return: True if ip is valid, False otherwise 163 164 """ 165 try: 166 socket.inet_pton(family, ip) 167 return True 168 except socket.error: 169 return False
170
171 172 -def IsValidIP4(ip):
173 """Verifies an IPv4 address. 174 175 This function checks if the given address is a valid IPv4 address. 176 177 @type ip: str 178 @param ip: the address to be checked 179 @rtype: boolean 180 @return: True if ip is valid, False otherwise 181 182 """ 183 return _GenericIsValidIP(socket.AF_INET, ip)
184
185 186 -def IsValidIP6(ip):
187 """Verifies an IPv6 address. 188 189 This function checks if the given address is a valid IPv6 address. 190 191 @type ip: str 192 @param ip: the address to be checked 193 @rtype: boolean 194 @return: True if ip is valid, False otherwise 195 196 """ 197 return _GenericIsValidIP(socket.AF_INET6, ip)
198
199 200 -def IsValidIP(ip):
201 """Verifies an IP address. 202 203 This function checks if the given IP address (both IPv4 and IPv6) is valid. 204 205 @type ip: str 206 @param ip: the address to be checked 207 @rtype: boolean 208 @return: True if ip is valid, False otherwise 209 210 """ 211 return IsValidIP4(ip) or IsValidIP6(ip)
212
213 214 -def GetAddressFamily(ip):
215 """Get the address family of the given address. 216 217 @type ip: str 218 @param ip: ip address whose family will be returned 219 @rtype: int 220 @return: socket.AF_INET or socket.AF_INET6 221 @raise errors.GenericError: for invalid addresses 222 223 """ 224 if IsValidIP6(ip): 225 return socket.AF_INET6 226 elif IsValidIP4(ip): 227 return socket.AF_INET 228 else: 229 raise errors.GenericError("Address %s not valid" % ip)
230
231 232 -def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
233 """Simple ping implementation using TCP connect(2). 234 235 Check if the given IP is reachable by doing attempting a TCP connect 236 to it. 237 238 @type target: str 239 @param target: the IP or hostname to ping 240 @type port: int 241 @param port: the port to connect to 242 @type timeout: int 243 @param timeout: the timeout on the connection attempt 244 @type live_port_needed: boolean 245 @param live_port_needed: whether a closed port will cause the 246 function to return failure, as if there was a timeout 247 @type source: str or None 248 @param source: if specified, will cause the connect to be made 249 from this specific source address; failures to bind other 250 than C{EADDRNOTAVAIL} will be ignored 251 252 """ 253 try: 254 family = GetAddressFamily(target) 255 except errors.GenericError: 256 return False 257 258 sock = socket.socket(family, socket.SOCK_STREAM) 259 success = False 260 261 if source is not None: 262 try: 263 sock.bind((source, 0)) 264 except socket.error, (errcode, _): 265 if errcode == errno.EADDRNOTAVAIL: 266 success = False 267 268 sock.settimeout(timeout) 269 270 try: 271 sock.connect((target, port)) 272 sock.close() 273 success = True 274 except socket.timeout: 275 success = False 276 except socket.error, (errcode, _): 277 success = (not live_port_needed) and (errcode == errno.ECONNREFUSED) 278 279 return success
280
281 282 -def OwnIpAddress(address):
283 """Check if the current host has the the given IP address. 284 285 This is done by trying to bind the given address. We return True if we 286 succeed or false if a socket.error is raised. 287 288 @type address: string 289 @param address: the address to check 290 @rtype: bool 291 @return: True if we own the address 292 293 """ 294 family = GetAddressFamily(address) 295 s = socket.socket(family, socket.SOCK_DGRAM) 296 success = False 297 try: 298 try: 299 s.bind((address, 0)) 300 success = True 301 except socket.error: 302 success = False 303 finally: 304 s.close() 305 return success
306
307 308 -def GetDaemonPort(daemon_name):
309 """Get the daemon port for this cluster. 310 311 Note that this routine does not read a ganeti-specific file, but 312 instead uses C{socket.getservbyname} to allow pre-customization of 313 this parameter outside of Ganeti. 314 315 @type daemon_name: string 316 @param daemon_name: daemon name (in constants.DAEMONS_PORTS) 317 @rtype: int 318 319 """ 320 if daemon_name not in constants.DAEMONS_PORTS: 321 raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name) 322 323 (proto, default_port) = constants.DAEMONS_PORTS[daemon_name] 324 try: 325 port = socket.getservbyname(daemon_name, proto) 326 except socket.error: 327 port = default_port 328 329 return port
330