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 re
32 import socket
33 import struct
34 import IN
35
36 from ganeti import constants
37 from ganeti import errors
38
39
40
41
42
43
44
45
46
47
48 _STRUCT_UCRED = "iII"
49 _STRUCT_UCRED_SIZE = struct.calcsize(_STRUCT_UCRED)
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
73
76 """Class implementing resolver and hostname functionality
77
78 """
79 _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$")
80
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
96 """Returns the hostname without domain.
97
98 """
99 return self.name.split('.')[0]
100
101 @staticmethod
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
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
127
128 raise errors.ResolverError(hostname, err.args[0], err.args[1])
129
130 return result
131
132 @classmethod
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
144 ".." in hostname or
145
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
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
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
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
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
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
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
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