Statistics
| Branch: | Tag: | Revision:

root / lib / utils / text.py @ adec726e

History | View | Annotate | Download (17.4 kB)

1 7fcffe27 Michael Hanselmann
#
2 7fcffe27 Michael Hanselmann
#
3 7fcffe27 Michael Hanselmann
4 7fcffe27 Michael Hanselmann
# Copyright (C) 2006, 2007, 2010, 2011 Google Inc.
5 7fcffe27 Michael Hanselmann
#
6 7fcffe27 Michael Hanselmann
# This program is free software; you can redistribute it and/or modify
7 7fcffe27 Michael Hanselmann
# it under the terms of the GNU General Public License as published by
8 7fcffe27 Michael Hanselmann
# the Free Software Foundation; either version 2 of the License, or
9 7fcffe27 Michael Hanselmann
# (at your option) any later version.
10 7fcffe27 Michael Hanselmann
#
11 7fcffe27 Michael Hanselmann
# This program is distributed in the hope that it will be useful, but
12 7fcffe27 Michael Hanselmann
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 7fcffe27 Michael Hanselmann
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 7fcffe27 Michael Hanselmann
# General Public License for more details.
15 7fcffe27 Michael Hanselmann
#
16 7fcffe27 Michael Hanselmann
# You should have received a copy of the GNU General Public License
17 7fcffe27 Michael Hanselmann
# along with this program; if not, write to the Free Software
18 7fcffe27 Michael Hanselmann
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 7fcffe27 Michael Hanselmann
# 02110-1301, USA.
20 7fcffe27 Michael Hanselmann
21 7fcffe27 Michael Hanselmann
"""Utility functions for manipulating or working with text.
22 7fcffe27 Michael Hanselmann

23 7fcffe27 Michael Hanselmann
"""
24 7fcffe27 Michael Hanselmann
25 7fcffe27 Michael Hanselmann
26 7fcffe27 Michael Hanselmann
import re
27 7fcffe27 Michael Hanselmann
import os
28 7fcffe27 Michael Hanselmann
import time
29 7fcffe27 Michael Hanselmann
import collections
30 7fcffe27 Michael Hanselmann
31 7fcffe27 Michael Hanselmann
from ganeti import errors
32 7fcffe27 Michael Hanselmann
33 7fcffe27 Michael Hanselmann
34 7fcffe27 Michael Hanselmann
#: Unit checker regexp
35 7fcffe27 Michael Hanselmann
_PARSEUNIT_REGEX = re.compile(r"^([.\d]+)\s*([a-zA-Z]+)?$")
36 7fcffe27 Michael Hanselmann
37 7fcffe27 Michael Hanselmann
#: Characters which don't need to be quoted for shell commands
38 d0c8c01d Iustin Pop
_SHELL_UNQUOTED_RE = re.compile("^[-.,=:/_+@A-Za-z0-9]+$")
39 7fcffe27 Michael Hanselmann
40 7ebd876f Michael Hanselmann
#: Shell param checker regexp
41 7ebd876f Michael Hanselmann
_SHELLPARAM_REGEX = re.compile(r"^[-a-zA-Z0-9._+/:%@]+$")
42 7ebd876f Michael Hanselmann
43 5401c39d Michael Hanselmann
#: ASCII equivalent of unicode character 'HORIZONTAL ELLIPSIS' (U+2026)
44 5401c39d Michael Hanselmann
_ASCII_ELLIPSIS = "..."
45 5401c39d Michael Hanselmann
46 d242923c Michael Hanselmann
#: MAC address octet
47 d242923c Michael Hanselmann
_MAC_ADDR_OCTET_RE = r"[0-9a-f]{2}"
48 d242923c Michael Hanselmann
49 7fcffe27 Michael Hanselmann
50 7fcffe27 Michael Hanselmann
def MatchNameComponent(key, name_list, case_sensitive=True):
51 7fcffe27 Michael Hanselmann
  """Try to match a name against a list.
52 7fcffe27 Michael Hanselmann

53 7fcffe27 Michael Hanselmann
  This function will try to match a name like test1 against a list
54 7fcffe27 Michael Hanselmann
  like C{['test1.example.com', 'test2.example.com', ...]}. Against
55 7fcffe27 Michael Hanselmann
  this list, I{'test1'} as well as I{'test1.example'} will match, but
56 7fcffe27 Michael Hanselmann
  not I{'test1.ex'}. A multiple match will be considered as no match
57 7fcffe27 Michael Hanselmann
  at all (e.g. I{'test1'} against C{['test1.example.com',
58 7fcffe27 Michael Hanselmann
  'test1.example.org']}), except when the key fully matches an entry
59 7fcffe27 Michael Hanselmann
  (e.g. I{'test1'} against C{['test1', 'test1.example.com']}).
60 7fcffe27 Michael Hanselmann

61 7fcffe27 Michael Hanselmann
  @type key: str
62 7fcffe27 Michael Hanselmann
  @param key: the name to be searched
63 7fcffe27 Michael Hanselmann
  @type name_list: list
64 7fcffe27 Michael Hanselmann
  @param name_list: the list of strings against which to search the key
65 7fcffe27 Michael Hanselmann
  @type case_sensitive: boolean
66 7fcffe27 Michael Hanselmann
  @param case_sensitive: whether to provide a case-sensitive match
67 7fcffe27 Michael Hanselmann

68 7fcffe27 Michael Hanselmann
  @rtype: None or str
69 7fcffe27 Michael Hanselmann
  @return: None if there is no match I{or} if there are multiple matches,
70 7fcffe27 Michael Hanselmann
      otherwise the element from the list which matches
71 7fcffe27 Michael Hanselmann

72 7fcffe27 Michael Hanselmann
  """
73 7fcffe27 Michael Hanselmann
  if key in name_list:
74 7fcffe27 Michael Hanselmann
    return key
75 7fcffe27 Michael Hanselmann
76 7fcffe27 Michael Hanselmann
  re_flags = 0
77 7fcffe27 Michael Hanselmann
  if not case_sensitive:
78 7fcffe27 Michael Hanselmann
    re_flags |= re.IGNORECASE
79 7fcffe27 Michael Hanselmann
    key = key.upper()
80 bbfed756 Michael Hanselmann
81 bbfed756 Michael Hanselmann
  name_re = re.compile(r"^%s(\..*)?$" % re.escape(key), re_flags)
82 bbfed756 Michael Hanselmann
83 7fcffe27 Michael Hanselmann
  names_filtered = []
84 7fcffe27 Michael Hanselmann
  string_matches = []
85 7fcffe27 Michael Hanselmann
  for name in name_list:
86 bbfed756 Michael Hanselmann
    if name_re.match(name) is not None:
87 7fcffe27 Michael Hanselmann
      names_filtered.append(name)
88 7fcffe27 Michael Hanselmann
      if not case_sensitive and key == name.upper():
89 7fcffe27 Michael Hanselmann
        string_matches.append(name)
90 7fcffe27 Michael Hanselmann
91 7fcffe27 Michael Hanselmann
  if len(string_matches) == 1:
92 7fcffe27 Michael Hanselmann
    return string_matches[0]
93 7fcffe27 Michael Hanselmann
  if len(names_filtered) == 1:
94 7fcffe27 Michael Hanselmann
    return names_filtered[0]
95 bbfed756 Michael Hanselmann
96 7fcffe27 Michael Hanselmann
  return None
97 7fcffe27 Michael Hanselmann
98 7fcffe27 Michael Hanselmann
99 bbfed756 Michael Hanselmann
def _DnsNameGlobHelper(match):
100 bbfed756 Michael Hanselmann
  """Helper function for L{DnsNameGlobPattern}.
101 bbfed756 Michael Hanselmann

102 bbfed756 Michael Hanselmann
  Returns regular expression pattern for parts of the pattern.
103 bbfed756 Michael Hanselmann

104 bbfed756 Michael Hanselmann
  """
105 bbfed756 Michael Hanselmann
  text = match.group(0)
106 bbfed756 Michael Hanselmann
107 bbfed756 Michael Hanselmann
  if text == "*":
108 bbfed756 Michael Hanselmann
    return "[^.]*"
109 bbfed756 Michael Hanselmann
  elif text == "?":
110 bbfed756 Michael Hanselmann
    return "[^.]"
111 bbfed756 Michael Hanselmann
  else:
112 bbfed756 Michael Hanselmann
    return re.escape(text)
113 bbfed756 Michael Hanselmann
114 bbfed756 Michael Hanselmann
115 bbfed756 Michael Hanselmann
def DnsNameGlobPattern(pattern):
116 bbfed756 Michael Hanselmann
  """Generates regular expression from DNS name globbing pattern.
117 bbfed756 Michael Hanselmann

118 bbfed756 Michael Hanselmann
  A DNS name globbing pattern (e.g. C{*.site}) is converted to a regular
119 bbfed756 Michael Hanselmann
  expression. Escape sequences or ranges (e.g. [a-z]) are not supported.
120 bbfed756 Michael Hanselmann

121 bbfed756 Michael Hanselmann
  Matching always starts at the leftmost part. An asterisk (*) matches all
122 bbfed756 Michael Hanselmann
  characters except the dot (.) separating DNS name parts. A question mark (?)
123 bbfed756 Michael Hanselmann
  matches a single character except the dot (.).
124 bbfed756 Michael Hanselmann

125 bbfed756 Michael Hanselmann
  @type pattern: string
126 bbfed756 Michael Hanselmann
  @param pattern: DNS name globbing pattern
127 bbfed756 Michael Hanselmann
  @rtype: string
128 bbfed756 Michael Hanselmann
  @return: Regular expression
129 bbfed756 Michael Hanselmann

130 bbfed756 Michael Hanselmann
  """
131 bbfed756 Michael Hanselmann
  return r"^%s(\..*)?$" % re.sub(r"\*|\?|[^*?]*", _DnsNameGlobHelper, pattern)
132 bbfed756 Michael Hanselmann
133 bbfed756 Michael Hanselmann
134 7fcffe27 Michael Hanselmann
def FormatUnit(value, units):
135 7fcffe27 Michael Hanselmann
  """Formats an incoming number of MiB with the appropriate unit.
136 7fcffe27 Michael Hanselmann

137 7fcffe27 Michael Hanselmann
  @type value: int
138 7fcffe27 Michael Hanselmann
  @param value: integer representing the value in MiB (1048576)
139 7fcffe27 Michael Hanselmann
  @type units: char
140 7fcffe27 Michael Hanselmann
  @param units: the type of formatting we should do:
141 7fcffe27 Michael Hanselmann
      - 'h' for automatic scaling
142 7fcffe27 Michael Hanselmann
      - 'm' for MiBs
143 7fcffe27 Michael Hanselmann
      - 'g' for GiBs
144 7fcffe27 Michael Hanselmann
      - 't' for TiBs
145 7fcffe27 Michael Hanselmann
  @rtype: str
146 7fcffe27 Michael Hanselmann
  @return: the formatted value (with suffix)
147 7fcffe27 Michael Hanselmann

148 7fcffe27 Michael Hanselmann
  """
149 d0c8c01d Iustin Pop
  if units not in ("m", "g", "t", "h"):
150 7fcffe27 Michael Hanselmann
    raise errors.ProgrammerError("Invalid unit specified '%s'" % str(units))
151 7fcffe27 Michael Hanselmann
152 d0c8c01d Iustin Pop
  suffix = ""
153 7fcffe27 Michael Hanselmann
154 d0c8c01d Iustin Pop
  if units == "m" or (units == "h" and value < 1024):
155 d0c8c01d Iustin Pop
    if units == "h":
156 d0c8c01d Iustin Pop
      suffix = "M"
157 7fcffe27 Michael Hanselmann
    return "%d%s" % (round(value, 0), suffix)
158 7fcffe27 Michael Hanselmann
159 d0c8c01d Iustin Pop
  elif units == "g" or (units == "h" and value < (1024 * 1024)):
160 d0c8c01d Iustin Pop
    if units == "h":
161 d0c8c01d Iustin Pop
      suffix = "G"
162 7fcffe27 Michael Hanselmann
    return "%0.1f%s" % (round(float(value) / 1024, 1), suffix)
163 7fcffe27 Michael Hanselmann
164 7fcffe27 Michael Hanselmann
  else:
165 d0c8c01d Iustin Pop
    if units == "h":
166 d0c8c01d Iustin Pop
      suffix = "T"
167 7fcffe27 Michael Hanselmann
    return "%0.1f%s" % (round(float(value) / 1024 / 1024, 1), suffix)
168 7fcffe27 Michael Hanselmann
169 7fcffe27 Michael Hanselmann
170 7fcffe27 Michael Hanselmann
def ParseUnit(input_string):
171 7fcffe27 Michael Hanselmann
  """Tries to extract number and scale from the given string.
172 7fcffe27 Michael Hanselmann

173 7fcffe27 Michael Hanselmann
  Input must be in the format C{NUMBER+ [DOT NUMBER+] SPACE*
174 7fcffe27 Michael Hanselmann
  [UNIT]}. If no unit is specified, it defaults to MiB. Return value
175 7fcffe27 Michael Hanselmann
  is always an int in MiB.
176 7fcffe27 Michael Hanselmann

177 7fcffe27 Michael Hanselmann
  """
178 7fcffe27 Michael Hanselmann
  m = _PARSEUNIT_REGEX.match(str(input_string))
179 7fcffe27 Michael Hanselmann
  if not m:
180 7fcffe27 Michael Hanselmann
    raise errors.UnitParseError("Invalid format")
181 7fcffe27 Michael Hanselmann
182 7fcffe27 Michael Hanselmann
  value = float(m.groups()[0])
183 7fcffe27 Michael Hanselmann
184 7fcffe27 Michael Hanselmann
  unit = m.groups()[1]
185 7fcffe27 Michael Hanselmann
  if unit:
186 7fcffe27 Michael Hanselmann
    lcunit = unit.lower()
187 7fcffe27 Michael Hanselmann
  else:
188 d0c8c01d Iustin Pop
    lcunit = "m"
189 7fcffe27 Michael Hanselmann
190 d0c8c01d Iustin Pop
  if lcunit in ("m", "mb", "mib"):
191 7fcffe27 Michael Hanselmann
    # Value already in MiB
192 7fcffe27 Michael Hanselmann
    pass
193 7fcffe27 Michael Hanselmann
194 d0c8c01d Iustin Pop
  elif lcunit in ("g", "gb", "gib"):
195 7fcffe27 Michael Hanselmann
    value *= 1024
196 7fcffe27 Michael Hanselmann
197 d0c8c01d Iustin Pop
  elif lcunit in ("t", "tb", "tib"):
198 7fcffe27 Michael Hanselmann
    value *= 1024 * 1024
199 7fcffe27 Michael Hanselmann
200 7fcffe27 Michael Hanselmann
  else:
201 7fcffe27 Michael Hanselmann
    raise errors.UnitParseError("Unknown unit: %s" % unit)
202 7fcffe27 Michael Hanselmann
203 7fcffe27 Michael Hanselmann
  # Make sure we round up
204 7fcffe27 Michael Hanselmann
  if int(value) < value:
205 7fcffe27 Michael Hanselmann
    value += 1
206 7fcffe27 Michael Hanselmann
207 7fcffe27 Michael Hanselmann
  # Round up to the next multiple of 4
208 7fcffe27 Michael Hanselmann
  value = int(value)
209 7fcffe27 Michael Hanselmann
  if value % 4:
210 7fcffe27 Michael Hanselmann
    value += 4 - value % 4
211 7fcffe27 Michael Hanselmann
212 7fcffe27 Michael Hanselmann
  return value
213 7fcffe27 Michael Hanselmann
214 7fcffe27 Michael Hanselmann
215 7fcffe27 Michael Hanselmann
def ShellQuote(value):
216 7fcffe27 Michael Hanselmann
  """Quotes shell argument according to POSIX.
217 7fcffe27 Michael Hanselmann

218 7fcffe27 Michael Hanselmann
  @type value: str
219 7fcffe27 Michael Hanselmann
  @param value: the argument to be quoted
220 7fcffe27 Michael Hanselmann
  @rtype: str
221 7fcffe27 Michael Hanselmann
  @return: the quoted value
222 7fcffe27 Michael Hanselmann

223 7fcffe27 Michael Hanselmann
  """
224 7fcffe27 Michael Hanselmann
  if _SHELL_UNQUOTED_RE.match(value):
225 7fcffe27 Michael Hanselmann
    return value
226 7fcffe27 Michael Hanselmann
  else:
227 7fcffe27 Michael Hanselmann
    return "'%s'" % value.replace("'", "'\\''")
228 7fcffe27 Michael Hanselmann
229 7fcffe27 Michael Hanselmann
230 7fcffe27 Michael Hanselmann
def ShellQuoteArgs(args):
231 7fcffe27 Michael Hanselmann
  """Quotes a list of shell arguments.
232 7fcffe27 Michael Hanselmann

233 7fcffe27 Michael Hanselmann
  @type args: list
234 7fcffe27 Michael Hanselmann
  @param args: list of arguments to be quoted
235 7fcffe27 Michael Hanselmann
  @rtype: str
236 7fcffe27 Michael Hanselmann
  @return: the quoted arguments concatenated with spaces
237 7fcffe27 Michael Hanselmann

238 7fcffe27 Michael Hanselmann
  """
239 7fcffe27 Michael Hanselmann
  return " ".join([ShellQuote(i) for i in args])
240 7fcffe27 Michael Hanselmann
241 7fcffe27 Michael Hanselmann
242 7fcffe27 Michael Hanselmann
class ShellWriter:
243 7fcffe27 Michael Hanselmann
  """Helper class to write scripts with indentation.
244 7fcffe27 Michael Hanselmann

245 7fcffe27 Michael Hanselmann
  """
246 7fcffe27 Michael Hanselmann
  INDENT_STR = "  "
247 7fcffe27 Michael Hanselmann
248 4af458e3 Michael Hanselmann
  def __init__(self, fh, indent=True):
249 7fcffe27 Michael Hanselmann
    """Initializes this class.
250 7fcffe27 Michael Hanselmann

251 7fcffe27 Michael Hanselmann
    """
252 7fcffe27 Michael Hanselmann
    self._fh = fh
253 4af458e3 Michael Hanselmann
    self._indent_enabled = indent
254 7fcffe27 Michael Hanselmann
    self._indent = 0
255 7fcffe27 Michael Hanselmann
256 7fcffe27 Michael Hanselmann
  def IncIndent(self):
257 7fcffe27 Michael Hanselmann
    """Increase indentation level by 1.
258 7fcffe27 Michael Hanselmann

259 7fcffe27 Michael Hanselmann
    """
260 7fcffe27 Michael Hanselmann
    self._indent += 1
261 7fcffe27 Michael Hanselmann
262 7fcffe27 Michael Hanselmann
  def DecIndent(self):
263 7fcffe27 Michael Hanselmann
    """Decrease indentation level by 1.
264 7fcffe27 Michael Hanselmann

265 7fcffe27 Michael Hanselmann
    """
266 7fcffe27 Michael Hanselmann
    assert self._indent > 0
267 7fcffe27 Michael Hanselmann
    self._indent -= 1
268 7fcffe27 Michael Hanselmann
269 7fcffe27 Michael Hanselmann
  def Write(self, txt, *args):
270 7fcffe27 Michael Hanselmann
    """Write line to output file.
271 7fcffe27 Michael Hanselmann

272 7fcffe27 Michael Hanselmann
    """
273 7fcffe27 Michael Hanselmann
    assert self._indent >= 0
274 7fcffe27 Michael Hanselmann
275 7fcffe27 Michael Hanselmann
    if args:
276 dce20078 Michael Hanselmann
      line = txt % args
277 7fcffe27 Michael Hanselmann
    else:
278 dce20078 Michael Hanselmann
      line = txt
279 dce20078 Michael Hanselmann
280 4af458e3 Michael Hanselmann
    if line and self._indent_enabled:
281 dce20078 Michael Hanselmann
      # Indent only if there's something on the line
282 dce20078 Michael Hanselmann
      self._fh.write(self._indent * self.INDENT_STR)
283 dce20078 Michael Hanselmann
284 dce20078 Michael Hanselmann
    self._fh.write(line)
285 7fcffe27 Michael Hanselmann
286 7fcffe27 Michael Hanselmann
    self._fh.write("\n")
287 7fcffe27 Michael Hanselmann
288 7fcffe27 Michael Hanselmann
289 7fcffe27 Michael Hanselmann
def GenerateSecret(numbytes=20):
290 7fcffe27 Michael Hanselmann
  """Generates a random secret.
291 7fcffe27 Michael Hanselmann

292 7fcffe27 Michael Hanselmann
  This will generate a pseudo-random secret returning an hex string
293 7fcffe27 Michael Hanselmann
  (so that it can be used where an ASCII string is needed).
294 7fcffe27 Michael Hanselmann

295 7fcffe27 Michael Hanselmann
  @param numbytes: the number of bytes which will be represented by the returned
296 7fcffe27 Michael Hanselmann
      string (defaulting to 20, the length of a SHA1 hash)
297 7fcffe27 Michael Hanselmann
  @rtype: str
298 7fcffe27 Michael Hanselmann
  @return: an hex representation of the pseudo-random sequence
299 7fcffe27 Michael Hanselmann

300 7fcffe27 Michael Hanselmann
  """
301 7fcffe27 Michael Hanselmann
  return os.urandom(numbytes).encode("hex")
302 7fcffe27 Michael Hanselmann
303 7fcffe27 Michael Hanselmann
304 d242923c Michael Hanselmann
def _MakeMacAddrRegexp(octets):
305 d242923c Michael Hanselmann
  """Builds a regular expression for verifying MAC addresses.
306 7fcffe27 Michael Hanselmann

307 d242923c Michael Hanselmann
  @type octets: integer
308 d242923c Michael Hanselmann
  @param octets: How many octets to expect (1-6)
309 d242923c Michael Hanselmann
  @return: Compiled regular expression
310 7fcffe27 Michael Hanselmann

311 d242923c Michael Hanselmann
  """
312 d242923c Michael Hanselmann
  assert octets > 0
313 d242923c Michael Hanselmann
  assert octets <= 6
314 d242923c Michael Hanselmann
315 d242923c Michael Hanselmann
  return re.compile("^%s$" % ":".join([_MAC_ADDR_OCTET_RE] * octets),
316 d242923c Michael Hanselmann
                    re.I)
317 d242923c Michael Hanselmann
318 d242923c Michael Hanselmann
319 d242923c Michael Hanselmann
#: Regular expression for full MAC address
320 d242923c Michael Hanselmann
_MAC_CHECK_RE = _MakeMacAddrRegexp(6)
321 d242923c Michael Hanselmann
322 d242923c Michael Hanselmann
#: Regular expression for half a MAC address
323 d242923c Michael Hanselmann
_MAC_PREFIX_CHECK_RE = _MakeMacAddrRegexp(3)
324 d242923c Michael Hanselmann
325 d242923c Michael Hanselmann
326 d242923c Michael Hanselmann
def _MacAddressCheck(check_re, mac, msg):
327 d242923c Michael Hanselmann
  """Checks a MAC address using a regular expression.
328 d242923c Michael Hanselmann

329 d242923c Michael Hanselmann
  @param check_re: Compiled regular expression as returned by C{re.compile}
330 d242923c Michael Hanselmann
  @type mac: string
331 d242923c Michael Hanselmann
  @param mac: MAC address to be validated
332 d242923c Michael Hanselmann
  @type msg: string
333 d242923c Michael Hanselmann
  @param msg: Error message (%s will be replaced with MAC address)
334 d242923c Michael Hanselmann

335 d242923c Michael Hanselmann
  """
336 d242923c Michael Hanselmann
  if check_re.match(mac):
337 d242923c Michael Hanselmann
    return mac.lower()
338 d242923c Michael Hanselmann
339 d242923c Michael Hanselmann
  raise errors.OpPrereqError(msg % mac, errors.ECODE_INVAL)
340 7fcffe27 Michael Hanselmann
341 d242923c Michael Hanselmann
342 d242923c Michael Hanselmann
def NormalizeAndValidateMac(mac):
343 d242923c Michael Hanselmann
  """Normalizes and check if a MAC address is valid and contains six octets.
344 d242923c Michael Hanselmann

345 d242923c Michael Hanselmann
  Checks whether the supplied MAC address is formally correct. Accepts
346 d242923c Michael Hanselmann
  colon-separated format only. Normalize it to all lower case.
347 d242923c Michael Hanselmann

348 d242923c Michael Hanselmann
  @type mac: string
349 d242923c Michael Hanselmann
  @param mac: MAC address to be validated
350 d242923c Michael Hanselmann
  @rtype: string
351 d242923c Michael Hanselmann
  @return: Normalized and validated MAC address
352 d242923c Michael Hanselmann
  @raise errors.OpPrereqError: If the MAC address isn't valid
353 7fcffe27 Michael Hanselmann

354 7fcffe27 Michael Hanselmann
  """
355 d242923c Michael Hanselmann
  return _MacAddressCheck(_MAC_CHECK_RE, mac, "Invalid MAC address '%s'")
356 7fcffe27 Michael Hanselmann
357 d242923c Michael Hanselmann
358 d242923c Michael Hanselmann
def NormalizeAndValidateThreeOctetMacPrefix(mac):
359 d242923c Michael Hanselmann
  """Normalizes a potential MAC address prefix (three octets).
360 d242923c Michael Hanselmann

361 d242923c Michael Hanselmann
  Checks whether the supplied string is a valid MAC address prefix consisting
362 d242923c Michael Hanselmann
  of three colon-separated octets. The result is normalized to all lower case.
363 d242923c Michael Hanselmann

364 d242923c Michael Hanselmann
  @type mac: string
365 d242923c Michael Hanselmann
  @param mac: Prefix to be validated
366 d242923c Michael Hanselmann
  @rtype: string
367 d242923c Michael Hanselmann
  @return: Normalized and validated prefix
368 d242923c Michael Hanselmann
  @raise errors.OpPrereqError: If the MAC address prefix isn't valid
369 d242923c Michael Hanselmann

370 d242923c Michael Hanselmann
  """
371 d242923c Michael Hanselmann
  return _MacAddressCheck(_MAC_PREFIX_CHECK_RE, mac,
372 d242923c Michael Hanselmann
                          "Invalid MAC address prefix '%s'")
373 7fcffe27 Michael Hanselmann
374 7fcffe27 Michael Hanselmann
375 7fcffe27 Michael Hanselmann
def SafeEncode(text):
376 7fcffe27 Michael Hanselmann
  """Return a 'safe' version of a source string.
377 7fcffe27 Michael Hanselmann

378 7fcffe27 Michael Hanselmann
  This function mangles the input string and returns a version that
379 7fcffe27 Michael Hanselmann
  should be safe to display/encode as ASCII. To this end, we first
380 7fcffe27 Michael Hanselmann
  convert it to ASCII using the 'backslashreplace' encoding which
381 7fcffe27 Michael Hanselmann
  should get rid of any non-ASCII chars, and then we process it
382 7fcffe27 Michael Hanselmann
  through a loop copied from the string repr sources in the python; we
383 7fcffe27 Michael Hanselmann
  don't use string_escape anymore since that escape single quotes and
384 7fcffe27 Michael Hanselmann
  backslashes too, and that is too much; and that escaping is not
385 7fcffe27 Michael Hanselmann
  stable, i.e. string_escape(string_escape(x)) != string_escape(x).
386 7fcffe27 Michael Hanselmann

387 7fcffe27 Michael Hanselmann
  @type text: str or unicode
388 7fcffe27 Michael Hanselmann
  @param text: input data
389 7fcffe27 Michael Hanselmann
  @rtype: str
390 7fcffe27 Michael Hanselmann
  @return: a safe version of text
391 7fcffe27 Michael Hanselmann

392 7fcffe27 Michael Hanselmann
  """
393 7fcffe27 Michael Hanselmann
  if isinstance(text, unicode):
394 7fcffe27 Michael Hanselmann
    # only if unicode; if str already, we handle it below
395 d0c8c01d Iustin Pop
    text = text.encode("ascii", "backslashreplace")
396 7fcffe27 Michael Hanselmann
  resu = ""
397 7fcffe27 Michael Hanselmann
  for char in text:
398 7fcffe27 Michael Hanselmann
    c = ord(char)
399 e687ec01 Michael Hanselmann
    if char == "\t":
400 d0c8c01d Iustin Pop
      resu += r"\t"
401 d0c8c01d Iustin Pop
    elif char == "\n":
402 d0c8c01d Iustin Pop
      resu += r"\n"
403 d0c8c01d Iustin Pop
    elif char == "\r":
404 7fcffe27 Michael Hanselmann
      resu += r'\'r'
405 7fcffe27 Michael Hanselmann
    elif c < 32 or c >= 127: # non-printable
406 7fcffe27 Michael Hanselmann
      resu += "\\x%02x" % (c & 0xff)
407 7fcffe27 Michael Hanselmann
    else:
408 7fcffe27 Michael Hanselmann
      resu += char
409 7fcffe27 Michael Hanselmann
  return resu
410 7fcffe27 Michael Hanselmann
411 7fcffe27 Michael Hanselmann
412 7fcffe27 Michael Hanselmann
def UnescapeAndSplit(text, sep=","):
413 78f99abb Michele Tartara
  r"""Split and unescape a string based on a given separator.
414 7fcffe27 Michael Hanselmann

415 7fcffe27 Michael Hanselmann
  This function splits a string based on a separator where the
416 7fcffe27 Michael Hanselmann
  separator itself can be escape in order to be an element of the
417 7fcffe27 Michael Hanselmann
  elements. The escaping rules are (assuming coma being the
418 7fcffe27 Michael Hanselmann
  separator):
419 7fcffe27 Michael Hanselmann
    - a plain , separates the elements
420 7fcffe27 Michael Hanselmann
    - a sequence \\\\, (double backslash plus comma) is handled as a
421 7fcffe27 Michael Hanselmann
      backslash plus a separator comma
422 7fcffe27 Michael Hanselmann
    - a sequence \, (backslash plus comma) is handled as a
423 7fcffe27 Michael Hanselmann
      non-separator comma
424 7fcffe27 Michael Hanselmann

425 7fcffe27 Michael Hanselmann
  @type text: string
426 7fcffe27 Michael Hanselmann
  @param text: the string to split
427 7fcffe27 Michael Hanselmann
  @type sep: string
428 7fcffe27 Michael Hanselmann
  @param text: the separator
429 7fcffe27 Michael Hanselmann
  @rtype: string
430 7fcffe27 Michael Hanselmann
  @return: a list of strings
431 7fcffe27 Michael Hanselmann

432 7fcffe27 Michael Hanselmann
  """
433 7fcffe27 Michael Hanselmann
  # we split the list by sep (with no escaping at this stage)
434 7fcffe27 Michael Hanselmann
  slist = text.split(sep)
435 7fcffe27 Michael Hanselmann
  # next, we revisit the elements and if any of them ended with an odd
436 7fcffe27 Michael Hanselmann
  # number of backslashes, then we join it with the next
437 7fcffe27 Michael Hanselmann
  rlist = []
438 7fcffe27 Michael Hanselmann
  while slist:
439 7fcffe27 Michael Hanselmann
    e1 = slist.pop(0)
440 7fcffe27 Michael Hanselmann
    if e1.endswith("\\"):
441 7fcffe27 Michael Hanselmann
      num_b = len(e1) - len(e1.rstrip("\\"))
442 e4a48c7b Michael Hanselmann
      if num_b % 2 == 1 and slist:
443 7fcffe27 Michael Hanselmann
        e2 = slist.pop(0)
444 ecabe27e Nikos Skalkotos
        # Merge the two elements and push the result back to the source list for
445 ecabe27e Nikos Skalkotos
        # revisiting. If e2 ended with backslashes, further merging may need to
446 ecabe27e Nikos Skalkotos
        # be done.
447 ecabe27e Nikos Skalkotos
        slist.insert(0, e1 + sep + e2)
448 7fcffe27 Michael Hanselmann
        continue
449 ecabe27e Nikos Skalkotos
    # here the backslashes remain (all), and will be reduced in the next step
450 7fcffe27 Michael Hanselmann
    rlist.append(e1)
451 7fcffe27 Michael Hanselmann
  # finally, replace backslash-something with something
452 7fcffe27 Michael Hanselmann
  rlist = [re.sub(r"\\(.)", r"\1", v) for v in rlist]
453 7fcffe27 Michael Hanselmann
  return rlist
454 7fcffe27 Michael Hanselmann
455 7fcffe27 Michael Hanselmann
456 f2b91949 Klaus Aehlig
def EscapeAndJoin(slist, sep=","):
457 f2b91949 Klaus Aehlig
  """Encode a list in a way parsable by UnescapeAndSplit.
458 f2b91949 Klaus Aehlig

459 f2b91949 Klaus Aehlig
  @type slist: list of strings
460 f2b91949 Klaus Aehlig
  @param slist: the strings to be encoded
461 f2b91949 Klaus Aehlig
  @rtype: string
462 f2b91949 Klaus Aehlig
  @return: the encoding of the list oas a string
463 f2b91949 Klaus Aehlig

464 f2b91949 Klaus Aehlig
  """
465 f2b91949 Klaus Aehlig
  return sep.join([re.sub("\\" + sep, "\\\\" + sep,
466 f2b91949 Klaus Aehlig
                          re.sub(r"\\", r"\\\\", v)) for v in slist])
467 f2b91949 Klaus Aehlig
468 f2b91949 Klaus Aehlig
469 7fcffe27 Michael Hanselmann
def CommaJoin(names):
470 7fcffe27 Michael Hanselmann
  """Nicely join a set of identifiers.
471 7fcffe27 Michael Hanselmann

472 7fcffe27 Michael Hanselmann
  @param names: set, list or tuple
473 7fcffe27 Michael Hanselmann
  @return: a string with the formatted results
474 7fcffe27 Michael Hanselmann

475 7fcffe27 Michael Hanselmann
  """
476 7fcffe27 Michael Hanselmann
  return ", ".join([str(val) for val in names])
477 7fcffe27 Michael Hanselmann
478 7fcffe27 Michael Hanselmann
479 26a72a48 Michael Hanselmann
def FormatTime(val, usecs=None):
480 7fcffe27 Michael Hanselmann
  """Formats a time value.
481 7fcffe27 Michael Hanselmann

482 7fcffe27 Michael Hanselmann
  @type val: float or None
483 7fcffe27 Michael Hanselmann
  @param val: Timestamp as returned by time.time() (seconds since Epoch,
484 7fcffe27 Michael Hanselmann
    1970-01-01 00:00:00 UTC)
485 7fcffe27 Michael Hanselmann
  @return: a string value or N/A if we don't have a valid timestamp
486 7fcffe27 Michael Hanselmann

487 7fcffe27 Michael Hanselmann
  """
488 7fcffe27 Michael Hanselmann
  if val is None or not isinstance(val, (int, float)):
489 7fcffe27 Michael Hanselmann
    return "N/A"
490 26a72a48 Michael Hanselmann
491 7fcffe27 Michael Hanselmann
  # these two codes works on Linux, but they are not guaranteed on all
492 7fcffe27 Michael Hanselmann
  # platforms
493 26a72a48 Michael Hanselmann
  result = time.strftime("%F %T", time.localtime(val))
494 26a72a48 Michael Hanselmann
495 26a72a48 Michael Hanselmann
  if usecs is not None:
496 26a72a48 Michael Hanselmann
    result += ".%06d" % usecs
497 26a72a48 Michael Hanselmann
498 26a72a48 Michael Hanselmann
  return result
499 7fcffe27 Michael Hanselmann
500 7fcffe27 Michael Hanselmann
501 7fcffe27 Michael Hanselmann
def FormatSeconds(secs):
502 7fcffe27 Michael Hanselmann
  """Formats seconds for easier reading.
503 7fcffe27 Michael Hanselmann

504 7fcffe27 Michael Hanselmann
  @type secs: number
505 7fcffe27 Michael Hanselmann
  @param secs: Number of seconds
506 7fcffe27 Michael Hanselmann
  @rtype: string
507 7fcffe27 Michael Hanselmann
  @return: Formatted seconds (e.g. "2d 9h 19m 49s")
508 7fcffe27 Michael Hanselmann

509 7fcffe27 Michael Hanselmann
  """
510 7fcffe27 Michael Hanselmann
  parts = []
511 7fcffe27 Michael Hanselmann
512 7fcffe27 Michael Hanselmann
  secs = round(secs, 0)
513 7fcffe27 Michael Hanselmann
514 7fcffe27 Michael Hanselmann
  if secs > 0:
515 7fcffe27 Michael Hanselmann
    # Negative values would be a bit tricky
516 7fcffe27 Michael Hanselmann
    for unit, one in [("d", 24 * 60 * 60), ("h", 60 * 60), ("m", 60)]:
517 7fcffe27 Michael Hanselmann
      (complete, secs) = divmod(secs, one)
518 7fcffe27 Michael Hanselmann
      if complete or parts:
519 7fcffe27 Michael Hanselmann
        parts.append("%d%s" % (complete, unit))
520 7fcffe27 Michael Hanselmann
521 7fcffe27 Michael Hanselmann
  parts.append("%ds" % secs)
522 7fcffe27 Michael Hanselmann
523 7fcffe27 Michael Hanselmann
  return " ".join(parts)
524 7fcffe27 Michael Hanselmann
525 7fcffe27 Michael Hanselmann
526 7fcffe27 Michael Hanselmann
class LineSplitter:
527 7fcffe27 Michael Hanselmann
  """Splits data chunks into lines separated by newline.
528 7fcffe27 Michael Hanselmann

529 7fcffe27 Michael Hanselmann
  Instances provide a file-like interface.
530 7fcffe27 Michael Hanselmann

531 7fcffe27 Michael Hanselmann
  """
532 7fcffe27 Michael Hanselmann
  def __init__(self, line_fn, *args):
533 7fcffe27 Michael Hanselmann
    """Initializes this class.
534 7fcffe27 Michael Hanselmann

535 7fcffe27 Michael Hanselmann
    @type line_fn: callable
536 7fcffe27 Michael Hanselmann
    @param line_fn: Function called for each line, first parameter is line
537 7fcffe27 Michael Hanselmann
    @param args: Extra arguments for L{line_fn}
538 7fcffe27 Michael Hanselmann

539 7fcffe27 Michael Hanselmann
    """
540 7fcffe27 Michael Hanselmann
    assert callable(line_fn)
541 7fcffe27 Michael Hanselmann
542 7fcffe27 Michael Hanselmann
    if args:
543 7fcffe27 Michael Hanselmann
      # Python 2.4 doesn't have functools.partial yet
544 7fcffe27 Michael Hanselmann
      self._line_fn = \
545 b459a848 Andrea Spadaccini
        lambda line: line_fn(line, *args) # pylint: disable=W0142
546 7fcffe27 Michael Hanselmann
    else:
547 7fcffe27 Michael Hanselmann
      self._line_fn = line_fn
548 7fcffe27 Michael Hanselmann
549 7fcffe27 Michael Hanselmann
    self._lines = collections.deque()
550 7fcffe27 Michael Hanselmann
    self._buffer = ""
551 7fcffe27 Michael Hanselmann
552 7fcffe27 Michael Hanselmann
  def write(self, data):
553 7fcffe27 Michael Hanselmann
    parts = (self._buffer + data).split("\n")
554 7fcffe27 Michael Hanselmann
    self._buffer = parts.pop()
555 7fcffe27 Michael Hanselmann
    self._lines.extend(parts)
556 7fcffe27 Michael Hanselmann
557 7fcffe27 Michael Hanselmann
  def flush(self):
558 7fcffe27 Michael Hanselmann
    while self._lines:
559 7fcffe27 Michael Hanselmann
      self._line_fn(self._lines.popleft().rstrip("\r\n"))
560 7fcffe27 Michael Hanselmann
561 7fcffe27 Michael Hanselmann
  def close(self):
562 7fcffe27 Michael Hanselmann
    self.flush()
563 7fcffe27 Michael Hanselmann
    if self._buffer:
564 7fcffe27 Michael Hanselmann
      self._line_fn(self._buffer)
565 7ebd876f Michael Hanselmann
566 7ebd876f Michael Hanselmann
567 7ebd876f Michael Hanselmann
def IsValidShellParam(word):
568 7ebd876f Michael Hanselmann
  """Verifies is the given word is safe from the shell's p.o.v.
569 7ebd876f Michael Hanselmann

570 7ebd876f Michael Hanselmann
  This means that we can pass this to a command via the shell and be
571 7ebd876f Michael Hanselmann
  sure that it doesn't alter the command line and is passed as such to
572 7ebd876f Michael Hanselmann
  the actual command.
573 7ebd876f Michael Hanselmann

574 7ebd876f Michael Hanselmann
  Note that we are overly restrictive here, in order to be on the safe
575 7ebd876f Michael Hanselmann
  side.
576 7ebd876f Michael Hanselmann

577 7ebd876f Michael Hanselmann
  @type word: str
578 7ebd876f Michael Hanselmann
  @param word: the word to check
579 7ebd876f Michael Hanselmann
  @rtype: boolean
580 7ebd876f Michael Hanselmann
  @return: True if the word is 'safe'
581 7ebd876f Michael Hanselmann

582 7ebd876f Michael Hanselmann
  """
583 7ebd876f Michael Hanselmann
  return bool(_SHELLPARAM_REGEX.match(word))
584 7ebd876f Michael Hanselmann
585 7ebd876f Michael Hanselmann
586 7ebd876f Michael Hanselmann
def BuildShellCmd(template, *args):
587 7ebd876f Michael Hanselmann
  """Build a safe shell command line from the given arguments.
588 7ebd876f Michael Hanselmann

589 7ebd876f Michael Hanselmann
  This function will check all arguments in the args list so that they
590 7ebd876f Michael Hanselmann
  are valid shell parameters (i.e. they don't contain shell
591 7ebd876f Michael Hanselmann
  metacharacters). If everything is ok, it will return the result of
592 7ebd876f Michael Hanselmann
  template % args.
593 7ebd876f Michael Hanselmann

594 7ebd876f Michael Hanselmann
  @type template: str
595 7ebd876f Michael Hanselmann
  @param template: the string holding the template for the
596 7ebd876f Michael Hanselmann
      string formatting
597 7ebd876f Michael Hanselmann
  @rtype: str
598 7ebd876f Michael Hanselmann
  @return: the expanded command line
599 7ebd876f Michael Hanselmann

600 7ebd876f Michael Hanselmann
  """
601 7ebd876f Michael Hanselmann
  for word in args:
602 7ebd876f Michael Hanselmann
    if not IsValidShellParam(word):
603 7ebd876f Michael Hanselmann
      raise errors.ProgrammerError("Shell argument '%s' contains"
604 7ebd876f Michael Hanselmann
                                   " invalid characters" % word)
605 7ebd876f Michael Hanselmann
  return template % args
606 92ea69bf Michael Hanselmann
607 92ea69bf Michael Hanselmann
608 92ea69bf Michael Hanselmann
def FormatOrdinal(value):
609 92ea69bf Michael Hanselmann
  """Formats a number as an ordinal in the English language.
610 92ea69bf Michael Hanselmann

611 92ea69bf Michael Hanselmann
  E.g. the number 1 becomes "1st", 22 becomes "22nd".
612 92ea69bf Michael Hanselmann

613 92ea69bf Michael Hanselmann
  @type value: integer
614 92ea69bf Michael Hanselmann
  @param value: Number
615 92ea69bf Michael Hanselmann
  @rtype: string
616 92ea69bf Michael Hanselmann

617 92ea69bf Michael Hanselmann
  """
618 92ea69bf Michael Hanselmann
  tens = value % 10
619 92ea69bf Michael Hanselmann
620 92ea69bf Michael Hanselmann
  if value > 10 and value < 20:
621 92ea69bf Michael Hanselmann
    suffix = "th"
622 92ea69bf Michael Hanselmann
  elif tens == 1:
623 92ea69bf Michael Hanselmann
    suffix = "st"
624 92ea69bf Michael Hanselmann
  elif tens == 2:
625 92ea69bf Michael Hanselmann
    suffix = "nd"
626 92ea69bf Michael Hanselmann
  elif tens == 3:
627 92ea69bf Michael Hanselmann
    suffix = "rd"
628 92ea69bf Michael Hanselmann
  else:
629 92ea69bf Michael Hanselmann
    suffix = "th"
630 92ea69bf Michael Hanselmann
631 92ea69bf Michael Hanselmann
  return "%s%s" % (value, suffix)
632 5401c39d Michael Hanselmann
633 5401c39d Michael Hanselmann
634 5401c39d Michael Hanselmann
def Truncate(text, length):
635 5401c39d Michael Hanselmann
  """Truncate string and add ellipsis if needed.
636 5401c39d Michael Hanselmann

637 5401c39d Michael Hanselmann
  @type text: string
638 5401c39d Michael Hanselmann
  @param text: Text
639 5401c39d Michael Hanselmann
  @type length: integer
640 5401c39d Michael Hanselmann
  @param length: Desired length
641 5401c39d Michael Hanselmann
  @rtype: string
642 5401c39d Michael Hanselmann
  @return: Truncated text
643 5401c39d Michael Hanselmann

644 5401c39d Michael Hanselmann
  """
645 5401c39d Michael Hanselmann
  assert length > len(_ASCII_ELLIPSIS)
646 5401c39d Michael Hanselmann
647 5401c39d Michael Hanselmann
  # Serialize if necessary
648 5401c39d Michael Hanselmann
  if not isinstance(text, basestring):
649 5401c39d Michael Hanselmann
    text = str(text)
650 5401c39d Michael Hanselmann
651 5401c39d Michael Hanselmann
  if len(text) <= length:
652 5401c39d Michael Hanselmann
    return text
653 5401c39d Michael Hanselmann
  else:
654 5401c39d Michael Hanselmann
    return text[:length - len(_ASCII_ELLIPSIS)] + _ASCII_ELLIPSIS
655 2cbe9af3 Michael Hanselmann
656 2cbe9af3 Michael Hanselmann
657 2cbe9af3 Michael Hanselmann
def FilterEmptyLinesAndComments(text):
658 2cbe9af3 Michael Hanselmann
  """Filters empty lines and comments from a line-based string.
659 2cbe9af3 Michael Hanselmann

660 2cbe9af3 Michael Hanselmann
  Whitespace is also removed from the beginning and end of all lines.
661 2cbe9af3 Michael Hanselmann

662 2cbe9af3 Michael Hanselmann
  @type text: string
663 2cbe9af3 Michael Hanselmann
  @param text: Input string
664 6a9b9778 Michael Hanselmann
  @rtype: list
665 2cbe9af3 Michael Hanselmann

666 2cbe9af3 Michael Hanselmann
  """
667 6a9b9778 Michael Hanselmann
  return [line for line in map(lambda s: s.strip(), text.splitlines())
668 6a9b9778 Michael Hanselmann
          # Ignore empty lines and comments
669 6a9b9778 Michael Hanselmann
          if line and not line.startswith("#")]
670 eac9b7b8 Michael Hanselmann
671 eac9b7b8 Michael Hanselmann
672 eac9b7b8 Michael Hanselmann
def FormatKeyValue(data):
673 eac9b7b8 Michael Hanselmann
  """Formats a dictionary as "key=value" parameters.
674 eac9b7b8 Michael Hanselmann

675 eac9b7b8 Michael Hanselmann
  The keys are sorted to have a stable order.
676 eac9b7b8 Michael Hanselmann

677 eac9b7b8 Michael Hanselmann
  @type data: dict
678 eac9b7b8 Michael Hanselmann
  @rtype: list of string
679 eac9b7b8 Michael Hanselmann

680 eac9b7b8 Michael Hanselmann
  """
681 eac9b7b8 Michael Hanselmann
  return ["%s=%s" % (key, value) for (key, value) in sorted(data.items())]