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