Allow -soundhw option to kvm
[ganeti-local] / lib / netutils.py
1 #
2 #
3
4 # Copyright (C) 2010, 2011, 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 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 os
32 import re
33 import socket
34 import struct
35 import IN
36 import logging
37
38 from ganeti import constants
39 from ganeti import errors
40 from ganeti import utils
41 from ganeti import vcluster
42
43 # Structure definition for getsockopt(SOL_SOCKET, SO_PEERCRED, ...):
44 # struct ucred { pid_t pid; uid_t uid; gid_t gid; };
45 #
46 # The GNU C Library defines gid_t and uid_t to be "unsigned int" and
47 # pid_t to "int".
48 #
49 # IEEE Std 1003.1-2008:
50 # "nlink_t, uid_t, gid_t, and id_t shall be integer types"
51 # "blksize_t, pid_t, and ssize_t shall be signed integer types"
52 _STRUCT_UCRED = "iII"
53 _STRUCT_UCRED_SIZE = struct.calcsize(_STRUCT_UCRED)
54
55 # Regexes used to find IP addresses in the output of ip.
56 _IP_RE_TEXT = r"[.:a-z0-9]+"      # separate for testing purposes
57 _IP_FAMILY_RE = re.compile(r"(?P<family>inet6?)\s+(?P<ip>%s)/" % _IP_RE_TEXT,
58                            re.IGNORECASE)
59
60 # Dict used to convert from a string representing an IP family to an IP
61 # version
62 _NAME_TO_IP_VER = {
63   "inet": constants.IP4_VERSION,
64   "inet6": constants.IP6_VERSION,
65   }
66
67
68 def _GetIpAddressesFromIpOutput(ip_output):
69   """Parses the output of the ip command and retrieves the IP addresses and
70   version.
71
72   @param ip_output: string containing the output of the ip command;
73   @rtype: dict; (int, list)
74   @return: a dict having as keys the IP versions and as values the
75            corresponding list of addresses found in the IP output.
76
77   """
78   addr = dict((i, []) for i in _NAME_TO_IP_VER.values())
79
80   for row in ip_output.splitlines():
81     match = _IP_FAMILY_RE.search(row)
82     if match and IPAddress.IsValid(match.group("ip")):
83       addr[_NAME_TO_IP_VER[match.group("family")]].append(match.group("ip"))
84
85   return addr
86
87
88 def GetSocketCredentials(sock):
89   """Returns the credentials of the foreign process connected to a socket.
90
91   @param sock: Unix socket
92   @rtype: tuple; (number, number, number)
93   @return: The PID, UID and GID of the connected foreign process.
94
95   """
96   peercred = sock.getsockopt(socket.SOL_SOCKET, IN.SO_PEERCRED,
97                              _STRUCT_UCRED_SIZE)
98   return struct.unpack(_STRUCT_UCRED, peercred)
99
100
101 def IsValidInterface(ifname):
102   """Validate an interface name.
103
104   @type ifname: string
105   @param ifname: Name of the network interface
106   @return: boolean indicating whether the interface name is valid or not.
107
108   """
109   return os.path.exists(utils.PathJoin("/sys/class/net", ifname))
110
111
112 def GetInterfaceIpAddresses(ifname):
113   """Returns the IP addresses associated to the interface.
114
115   @type ifname: string
116   @param ifname: Name of the network interface
117   @return: A dict having for keys the IP version (either
118            L{constants.IP4_VERSION} or L{constants.IP6_VERSION}) and for
119            values the lists of IP addresses of the respective version
120            associated to the interface
121
122   """
123   result = utils.RunCmd([constants.IP_COMMAND_PATH, "-o", "addr", "show",
124                          ifname])
125
126   if result.failed:
127     logging.error("Error running the ip command while getting the IP"
128                   " addresses of %s", ifname)
129     return None
130
131   return _GetIpAddressesFromIpOutput(result.output)
132
133
134 def GetHostname(name=None, family=None):
135   """Returns a Hostname object.
136
137   @type name: str
138   @param name: hostname or None
139   @type family: int
140   @param family: AF_INET | AF_INET6 | None
141   @rtype: L{Hostname}
142   @return: Hostname object
143   @raise errors.OpPrereqError: in case of errors in resolving
144
145   """
146   try:
147     return Hostname(name=name, family=family)
148   except errors.ResolverError, err:
149     raise errors.OpPrereqError("The given name (%s) does not resolve: %s" %
150                                (err[0], err[2]), errors.ECODE_RESOLVER)
151
152
153 class Hostname:
154   """Class implementing resolver and hostname functionality.
155
156   """
157   _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$")
158
159   def __init__(self, name=None, family=None):
160     """Initialize the host name object.
161
162     If the name argument is None, it will use this system's name.
163
164     @type family: int
165     @param family: AF_INET | AF_INET6 | None
166     @type name: str
167     @param name: hostname or None
168
169     """
170     self.name = self.GetFqdn(name)
171     self.ip = self.GetIP(self.name, family=family)
172
173   @classmethod
174   def GetSysName(cls):
175     """Legacy method the get the current system's name.
176
177     """
178     return cls.GetFqdn()
179
180   @classmethod
181   def GetFqdn(cls, hostname=None):
182     """Return fqdn.
183
184     If hostname is None the system's fqdn is returned.
185
186     @type hostname: str
187     @param hostname: name to be fqdn'ed
188     @rtype: str
189     @return: fqdn of given name, if it exists, unmodified name otherwise
190
191     """
192     if hostname is None:
193       virtfqdn = vcluster.GetVirtualHostname()
194       if virtfqdn:
195         result = virtfqdn
196       else:
197         result = socket.getfqdn()
198     else:
199       result = socket.getfqdn(hostname)
200
201     return cls.GetNormalizedName(result)
202
203   @staticmethod
204   def GetIP(hostname, family=None):
205     """Return IP address of given hostname.
206
207     Supports both IPv4 and IPv6.
208
209     @type hostname: str
210     @param hostname: hostname to look up
211     @type family: int
212     @param family: AF_INET | AF_INET6 | None
213     @rtype: str
214     @return: IP address
215     @raise errors.ResolverError: in case of errors in resolving
216
217     """
218     try:
219       if family in (socket.AF_INET, socket.AF_INET6):
220         result = socket.getaddrinfo(hostname, None, family)
221       else:
222         result = socket.getaddrinfo(hostname, None)
223     except (socket.gaierror, socket.herror, socket.error), err:
224       # hostname not found in DNS, or other socket exception in the
225       # (code, description format)
226       raise errors.ResolverError(hostname, err.args[0], err.args[1])
227
228     # getaddrinfo() returns a list of 5-tupes (family, socktype, proto,
229     # canonname, sockaddr). We return the first tuple's first address in
230     # sockaddr
231     try:
232       return result[0][4][0]
233     except IndexError, err:
234       # we don't have here an actual error code, it's just that the
235       # data type returned by getaddrinfo is not what we expected;
236       # let's keep the same format in the exception arguments with a
237       # dummy error code
238       raise errors.ResolverError(hostname, 0,
239                                  "Unknown error in getaddrinfo(): %s" % err)
240
241   @classmethod
242   def GetNormalizedName(cls, hostname):
243     """Validate and normalize the given hostname.
244
245     @attention: the validation is a bit more relaxed than the standards
246         require; most importantly, we allow underscores in names
247     @raise errors.OpPrereqError: when the name is not valid
248
249     """
250     hostname = hostname.lower()
251     if (not cls._VALID_NAME_RE.match(hostname) or
252         # double-dots, meaning empty label
253         ".." in hostname or
254         # empty initial label
255         hostname.startswith(".")):
256       raise errors.OpPrereqError("Invalid hostname '%s'" % hostname,
257                                  errors.ECODE_INVAL)
258     if hostname.endswith("."):
259       hostname = hostname.rstrip(".")
260     return hostname
261
262
263 def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
264   """Simple ping implementation using TCP connect(2).
265
266   Check if the given IP is reachable by doing attempting a TCP connect
267   to it.
268
269   @type target: str
270   @param target: the IP to ping
271   @type port: int
272   @param port: the port to connect to
273   @type timeout: int
274   @param timeout: the timeout on the connection attempt
275   @type live_port_needed: boolean
276   @param live_port_needed: whether a closed port will cause the
277       function to return failure, as if there was a timeout
278   @type source: str or None
279   @param source: if specified, will cause the connect to be made
280       from this specific source address; failures to bind other
281       than C{EADDRNOTAVAIL} will be ignored
282
283   """
284   try:
285     family = IPAddress.GetAddressFamily(target)
286   except errors.GenericError:
287     return False
288
289   sock = socket.socket(family, socket.SOCK_STREAM)
290   success = False
291
292   if source is not None:
293     try:
294       sock.bind((source, 0))
295     except socket.error, err:
296       if err[0] == errno.EADDRNOTAVAIL:
297         success = False
298
299   sock.settimeout(timeout)
300
301   try:
302     sock.connect((target, port))
303     sock.close()
304     success = True
305   except socket.timeout:
306     success = False
307   except socket.error, err:
308     success = (not live_port_needed) and (err[0] == errno.ECONNREFUSED)
309
310   return success
311
312
313 def GetDaemonPort(daemon_name):
314   """Get the daemon port for this cluster.
315
316   Note that this routine does not read a ganeti-specific file, but
317   instead uses C{socket.getservbyname} to allow pre-customization of
318   this parameter outside of Ganeti.
319
320   @type daemon_name: string
321   @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
322   @rtype: int
323
324   """
325   if daemon_name not in constants.DAEMONS_PORTS:
326     raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
327
328   (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
329   try:
330     port = socket.getservbyname(daemon_name, proto)
331   except socket.error:
332     port = default_port
333
334   return port
335
336
337 class IPAddress(object):
338   """Class that represents an IP address.
339
340   """
341   iplen = 0
342   family = None
343   loopback_cidr = None
344
345   @staticmethod
346   def _GetIPIntFromString(address):
347     """Abstract method to please pylint.
348
349     """
350     raise NotImplementedError
351
352   @classmethod
353   def IsValid(cls, address):
354     """Validate a IP address.
355
356     @type address: str
357     @param address: IP address to be checked
358     @rtype: bool
359     @return: True if valid, False otherwise
360
361     """
362     if cls.family is None:
363       try:
364         family = cls.GetAddressFamily(address)
365       except errors.IPAddressError:
366         return False
367     else:
368       family = cls.family
369
370     try:
371       socket.inet_pton(family, address)
372       return True
373     except socket.error:
374       return False
375
376   @classmethod
377   def ValidateNetmask(cls, netmask):
378     """Validate a netmask suffix in CIDR notation.
379
380     @type netmask: int
381     @param netmask: netmask suffix to validate
382     @rtype: bool
383     @return: True if valid, False otherwise
384
385     """
386     assert (isinstance(netmask, (int, long)))
387
388     return 0 < netmask <= cls.iplen
389
390   @classmethod
391   def Own(cls, address):
392     """Check if the current host has the the given IP address.
393
394     This is done by trying to bind the given address. We return True if we
395     succeed or false if a socket.error is raised.
396
397     @type address: str
398     @param address: IP address to be checked
399     @rtype: bool
400     @return: True if we own the address, False otherwise
401
402     """
403     if cls.family is None:
404       try:
405         family = cls.GetAddressFamily(address)
406       except errors.IPAddressError:
407         return False
408     else:
409       family = cls.family
410
411     s = socket.socket(family, socket.SOCK_DGRAM)
412     success = False
413     try:
414       try:
415         s.bind((address, 0))
416         success = True
417       except socket.error:
418         success = False
419     finally:
420       s.close()
421     return success
422
423   @classmethod
424   def InNetwork(cls, cidr, address):
425     """Determine whether an address is within a network.
426
427     @type cidr: string
428     @param cidr: Network in CIDR notation, e.g. '192.0.2.0/24', '2001:db8::/64'
429     @type address: str
430     @param address: IP address
431     @rtype: bool
432     @return: True if address is in cidr, False otherwise
433
434     """
435     address_int = cls._GetIPIntFromString(address)
436     subnet = cidr.split("/")
437     assert len(subnet) == 2
438     try:
439       prefix = int(subnet[1])
440     except ValueError:
441       return False
442
443     assert 0 <= prefix <= cls.iplen
444     target_int = cls._GetIPIntFromString(subnet[0])
445     # Convert prefix netmask to integer value of netmask
446     netmask_int = (2 ** cls.iplen) - 1 ^ ((2 ** cls.iplen) - 1 >> prefix)
447     # Calculate hostmask
448     hostmask_int = netmask_int ^ (2 ** cls.iplen) - 1
449     # Calculate network address by and'ing netmask
450     network_int = target_int & netmask_int
451     # Calculate broadcast address by or'ing hostmask
452     broadcast_int = target_int | hostmask_int
453
454     return network_int <= address_int <= broadcast_int
455
456   @staticmethod
457   def GetAddressFamily(address):
458     """Get the address family of the given address.
459
460     @type address: str
461     @param address: ip address whose family will be returned
462     @rtype: int
463     @return: C{socket.AF_INET} or C{socket.AF_INET6}
464     @raise errors.GenericError: for invalid addresses
465
466     """
467     try:
468       return IP4Address(address).family
469     except errors.IPAddressError:
470       pass
471
472     try:
473       return IP6Address(address).family
474     except errors.IPAddressError:
475       pass
476
477     raise errors.IPAddressError("Invalid address '%s'" % address)
478
479   @staticmethod
480   def GetVersionFromAddressFamily(family):
481     """Convert an IP address family to the corresponding IP version.
482
483     @type family: int
484     @param family: IP address family, one of socket.AF_INET or socket.AF_INET6
485     @return: an int containing the IP version, one of L{constants.IP4_VERSION}
486              or L{constants.IP6_VERSION}
487     @raise errors.ProgrammerError: for unknown families
488
489     """
490     if family == socket.AF_INET:
491       return constants.IP4_VERSION
492     elif family == socket.AF_INET6:
493       return constants.IP6_VERSION
494
495     raise errors.ProgrammerError("%s is not a valid IP address family" % family)
496
497   @staticmethod
498   def GetAddressFamilyFromVersion(version):
499     """Convert an IP version to the corresponding IP address family.
500
501     @type version: int
502     @param version: IP version, one of L{constants.IP4_VERSION} or
503                     L{constants.IP6_VERSION}
504     @return: an int containing the IP address family, one of C{socket.AF_INET}
505              or C{socket.AF_INET6}
506     @raise errors.ProgrammerError: for unknown IP versions
507
508     """
509     if version == constants.IP4_VERSION:
510       return socket.AF_INET
511     elif version == constants.IP6_VERSION:
512       return socket.AF_INET6
513
514     raise errors.ProgrammerError("%s is not a valid IP version" % version)
515
516   @staticmethod
517   def GetClassFromIpVersion(version):
518     """Return the IPAddress subclass for the given IP version.
519
520     @type version: int
521     @param version: IP version, one of L{constants.IP4_VERSION} or
522                     L{constants.IP6_VERSION}
523     @return: a subclass of L{netutils.IPAddress}
524     @raise errors.ProgrammerError: for unknowo IP versions
525
526     """
527     if version == constants.IP4_VERSION:
528       return IP4Address
529     elif version == constants.IP6_VERSION:
530       return IP6Address
531
532     raise errors.ProgrammerError("%s is not a valid IP version" % version)
533
534   @staticmethod
535   def GetClassFromIpFamily(family):
536     """Return the IPAddress subclass for the given IP family.
537
538     @param family: IP family (one of C{socket.AF_INET} or C{socket.AF_INET6}
539     @return: a subclass of L{netutils.IPAddress}
540     @raise errors.ProgrammerError: for unknowo IP versions
541
542     """
543     return IPAddress.GetClassFromIpVersion(
544               IPAddress.GetVersionFromAddressFamily(family))
545
546   @classmethod
547   def IsLoopback(cls, address):
548     """Determine whether it is a loopback address.
549
550     @type address: str
551     @param address: IP address to be checked
552     @rtype: bool
553     @return: True if loopback, False otherwise
554
555     """
556     try:
557       return cls.InNetwork(cls.loopback_cidr, address)
558     except errors.IPAddressError:
559       return False
560
561
562 class IP4Address(IPAddress):
563   """IPv4 address class.
564
565   """
566   iplen = 32
567   family = socket.AF_INET
568   loopback_cidr = "127.0.0.0/8"
569
570   def __init__(self, address):
571     """Constructor for IPv4 address.
572
573     @type address: str
574     @param address: IP address
575     @raises errors.IPAddressError: if address invalid
576
577     """
578     IPAddress.__init__(self)
579     if not self.IsValid(address):
580       raise errors.IPAddressError("IPv4 Address %s invalid" % address)
581
582     self.address = address
583
584   @staticmethod
585   def _GetIPIntFromString(address):
586     """Get integer value of IPv4 address.
587
588     @type address: str
589     @param address: IPv6 address
590     @rtype: int
591     @return: integer value of given IP address
592
593     """
594     address_int = 0
595     parts = address.split(".")
596     assert len(parts) == 4
597     for part in parts:
598       address_int = (address_int << 8) | int(part)
599
600     return address_int
601
602
603 class IP6Address(IPAddress):
604   """IPv6 address class.
605
606   """
607   iplen = 128
608   family = socket.AF_INET6
609   loopback_cidr = "::1/128"
610
611   def __init__(self, address):
612     """Constructor for IPv6 address.
613
614     @type address: str
615     @param address: IP address
616     @raises errors.IPAddressError: if address invalid
617
618     """
619     IPAddress.__init__(self)
620     if not self.IsValid(address):
621       raise errors.IPAddressError("IPv6 Address [%s] invalid" % address)
622     self.address = address
623
624   @staticmethod
625   def _GetIPIntFromString(address):
626     """Get integer value of IPv6 address.
627
628     @type address: str
629     @param address: IPv6 address
630     @rtype: int
631     @return: integer value of given IP address
632
633     """
634     doublecolons = address.count("::")
635     assert not doublecolons > 1
636     if doublecolons == 1:
637       # We have a shorthand address, expand it
638       parts = []
639       twoparts = address.split("::")
640       sep = len(twoparts[0].split(":")) + len(twoparts[1].split(":"))
641       parts = twoparts[0].split(":")
642       parts.extend(["0"] * (8 - sep))
643       parts += twoparts[1].split(":")
644     else:
645       parts = address.split(":")
646
647     address_int = 0
648     for part in parts:
649       address_int = (address_int << 16) + int(part or "0", 16)
650
651     return address_int
652
653
654 def FormatAddress(address, family=None):
655   """Format a socket address
656
657   @type address: family specific (usually tuple)
658   @param address: address, as reported by this class
659   @type family: integer
660   @param family: socket family (one of socket.AF_*) or None
661
662   """
663   if family is None:
664     try:
665       family = IPAddress.GetAddressFamily(address[0])
666     except errors.IPAddressError:
667       raise errors.ParameterError(address)
668
669   if family == socket.AF_UNIX and len(address) == 3:
670     return "pid=%s, uid=%s, gid=%s" % address
671
672   if family in (socket.AF_INET, socket.AF_INET6) and len(address) == 2:
673     host, port = address
674     if family == socket.AF_INET6:
675       res = "[%s]" % host
676     else:
677       res = host
678
679     if port is not None:
680       res += ":%s" % port
681
682     return res
683
684   raise errors.ParameterError(family, address)