Statistics
| Branch: | Tag: | Revision:

root / lib / utils / text.py @ adc523ab

History | View | Annotate | Download (12.7 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 7fcffe27 Michael Hanselmann
_SHELL_UNQUOTED_RE = re.compile('^[-.,=:/_+@A-Za-z0-9]+$')
39 7fcffe27 Michael Hanselmann
40 7fcffe27 Michael Hanselmann
#: MAC checker regexp
41 7fcffe27 Michael Hanselmann
_MAC_CHECK_RE = re.compile("^([0-9a-f]{2}:){5}[0-9a-f]{2}$", re.I)
42 7fcffe27 Michael Hanselmann
43 7ebd876f Michael Hanselmann
#: Shell param checker regexp
44 7ebd876f Michael Hanselmann
_SHELLPARAM_REGEX = re.compile(r"^[-a-zA-Z0-9._+/:%@]+$")
45 7ebd876f Michael Hanselmann
46 7fcffe27 Michael Hanselmann
47 7fcffe27 Michael Hanselmann
def MatchNameComponent(key, name_list, case_sensitive=True):
48 7fcffe27 Michael Hanselmann
  """Try to match a name against a list.
49 7fcffe27 Michael Hanselmann

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

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

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

69 7fcffe27 Michael Hanselmann
  """
70 7fcffe27 Michael Hanselmann
  if key in name_list:
71 7fcffe27 Michael Hanselmann
    return key
72 7fcffe27 Michael Hanselmann
73 7fcffe27 Michael Hanselmann
  re_flags = 0
74 7fcffe27 Michael Hanselmann
  if not case_sensitive:
75 7fcffe27 Michael Hanselmann
    re_flags |= re.IGNORECASE
76 7fcffe27 Michael Hanselmann
    key = key.upper()
77 7fcffe27 Michael Hanselmann
  mo = re.compile("^%s(\..*)?$" % re.escape(key), re_flags)
78 7fcffe27 Michael Hanselmann
  names_filtered = []
79 7fcffe27 Michael Hanselmann
  string_matches = []
80 7fcffe27 Michael Hanselmann
  for name in name_list:
81 7fcffe27 Michael Hanselmann
    if mo.match(name) is not None:
82 7fcffe27 Michael Hanselmann
      names_filtered.append(name)
83 7fcffe27 Michael Hanselmann
      if not case_sensitive and key == name.upper():
84 7fcffe27 Michael Hanselmann
        string_matches.append(name)
85 7fcffe27 Michael Hanselmann
86 7fcffe27 Michael Hanselmann
  if len(string_matches) == 1:
87 7fcffe27 Michael Hanselmann
    return string_matches[0]
88 7fcffe27 Michael Hanselmann
  if len(names_filtered) == 1:
89 7fcffe27 Michael Hanselmann
    return names_filtered[0]
90 7fcffe27 Michael Hanselmann
  return None
91 7fcffe27 Michael Hanselmann
92 7fcffe27 Michael Hanselmann
93 7fcffe27 Michael Hanselmann
def FormatUnit(value, units):
94 7fcffe27 Michael Hanselmann
  """Formats an incoming number of MiB with the appropriate unit.
95 7fcffe27 Michael Hanselmann

96 7fcffe27 Michael Hanselmann
  @type value: int
97 7fcffe27 Michael Hanselmann
  @param value: integer representing the value in MiB (1048576)
98 7fcffe27 Michael Hanselmann
  @type units: char
99 7fcffe27 Michael Hanselmann
  @param units: the type of formatting we should do:
100 7fcffe27 Michael Hanselmann
      - 'h' for automatic scaling
101 7fcffe27 Michael Hanselmann
      - 'm' for MiBs
102 7fcffe27 Michael Hanselmann
      - 'g' for GiBs
103 7fcffe27 Michael Hanselmann
      - 't' for TiBs
104 7fcffe27 Michael Hanselmann
  @rtype: str
105 7fcffe27 Michael Hanselmann
  @return: the formatted value (with suffix)
106 7fcffe27 Michael Hanselmann

107 7fcffe27 Michael Hanselmann
  """
108 7fcffe27 Michael Hanselmann
  if units not in ('m', 'g', 't', 'h'):
109 7fcffe27 Michael Hanselmann
    raise errors.ProgrammerError("Invalid unit specified '%s'" % str(units))
110 7fcffe27 Michael Hanselmann
111 7fcffe27 Michael Hanselmann
  suffix = ''
112 7fcffe27 Michael Hanselmann
113 7fcffe27 Michael Hanselmann
  if units == 'm' or (units == 'h' and value < 1024):
114 7fcffe27 Michael Hanselmann
    if units == 'h':
115 7fcffe27 Michael Hanselmann
      suffix = 'M'
116 7fcffe27 Michael Hanselmann
    return "%d%s" % (round(value, 0), suffix)
117 7fcffe27 Michael Hanselmann
118 7fcffe27 Michael Hanselmann
  elif units == 'g' or (units == 'h' and value < (1024 * 1024)):
119 7fcffe27 Michael Hanselmann
    if units == 'h':
120 7fcffe27 Michael Hanselmann
      suffix = 'G'
121 7fcffe27 Michael Hanselmann
    return "%0.1f%s" % (round(float(value) / 1024, 1), suffix)
122 7fcffe27 Michael Hanselmann
123 7fcffe27 Michael Hanselmann
  else:
124 7fcffe27 Michael Hanselmann
    if units == 'h':
125 7fcffe27 Michael Hanselmann
      suffix = 'T'
126 7fcffe27 Michael Hanselmann
    return "%0.1f%s" % (round(float(value) / 1024 / 1024, 1), suffix)
127 7fcffe27 Michael Hanselmann
128 7fcffe27 Michael Hanselmann
129 7fcffe27 Michael Hanselmann
def ParseUnit(input_string):
130 7fcffe27 Michael Hanselmann
  """Tries to extract number and scale from the given string.
131 7fcffe27 Michael Hanselmann

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

136 7fcffe27 Michael Hanselmann
  """
137 7fcffe27 Michael Hanselmann
  m = _PARSEUNIT_REGEX.match(str(input_string))
138 7fcffe27 Michael Hanselmann
  if not m:
139 7fcffe27 Michael Hanselmann
    raise errors.UnitParseError("Invalid format")
140 7fcffe27 Michael Hanselmann
141 7fcffe27 Michael Hanselmann
  value = float(m.groups()[0])
142 7fcffe27 Michael Hanselmann
143 7fcffe27 Michael Hanselmann
  unit = m.groups()[1]
144 7fcffe27 Michael Hanselmann
  if unit:
145 7fcffe27 Michael Hanselmann
    lcunit = unit.lower()
146 7fcffe27 Michael Hanselmann
  else:
147 7fcffe27 Michael Hanselmann
    lcunit = 'm'
148 7fcffe27 Michael Hanselmann
149 7fcffe27 Michael Hanselmann
  if lcunit in ('m', 'mb', 'mib'):
150 7fcffe27 Michael Hanselmann
    # Value already in MiB
151 7fcffe27 Michael Hanselmann
    pass
152 7fcffe27 Michael Hanselmann
153 7fcffe27 Michael Hanselmann
  elif lcunit in ('g', 'gb', 'gib'):
154 7fcffe27 Michael Hanselmann
    value *= 1024
155 7fcffe27 Michael Hanselmann
156 7fcffe27 Michael Hanselmann
  elif lcunit in ('t', 'tb', 'tib'):
157 7fcffe27 Michael Hanselmann
    value *= 1024 * 1024
158 7fcffe27 Michael Hanselmann
159 7fcffe27 Michael Hanselmann
  else:
160 7fcffe27 Michael Hanselmann
    raise errors.UnitParseError("Unknown unit: %s" % unit)
161 7fcffe27 Michael Hanselmann
162 7fcffe27 Michael Hanselmann
  # Make sure we round up
163 7fcffe27 Michael Hanselmann
  if int(value) < value:
164 7fcffe27 Michael Hanselmann
    value += 1
165 7fcffe27 Michael Hanselmann
166 7fcffe27 Michael Hanselmann
  # Round up to the next multiple of 4
167 7fcffe27 Michael Hanselmann
  value = int(value)
168 7fcffe27 Michael Hanselmann
  if value % 4:
169 7fcffe27 Michael Hanselmann
    value += 4 - value % 4
170 7fcffe27 Michael Hanselmann
171 7fcffe27 Michael Hanselmann
  return value
172 7fcffe27 Michael Hanselmann
173 7fcffe27 Michael Hanselmann
174 7fcffe27 Michael Hanselmann
def ShellQuote(value):
175 7fcffe27 Michael Hanselmann
  """Quotes shell argument according to POSIX.
176 7fcffe27 Michael Hanselmann

177 7fcffe27 Michael Hanselmann
  @type value: str
178 7fcffe27 Michael Hanselmann
  @param value: the argument to be quoted
179 7fcffe27 Michael Hanselmann
  @rtype: str
180 7fcffe27 Michael Hanselmann
  @return: the quoted value
181 7fcffe27 Michael Hanselmann

182 7fcffe27 Michael Hanselmann
  """
183 7fcffe27 Michael Hanselmann
  if _SHELL_UNQUOTED_RE.match(value):
184 7fcffe27 Michael Hanselmann
    return value
185 7fcffe27 Michael Hanselmann
  else:
186 7fcffe27 Michael Hanselmann
    return "'%s'" % value.replace("'", "'\\''")
187 7fcffe27 Michael Hanselmann
188 7fcffe27 Michael Hanselmann
189 7fcffe27 Michael Hanselmann
def ShellQuoteArgs(args):
190 7fcffe27 Michael Hanselmann
  """Quotes a list of shell arguments.
191 7fcffe27 Michael Hanselmann

192 7fcffe27 Michael Hanselmann
  @type args: list
193 7fcffe27 Michael Hanselmann
  @param args: list of arguments to be quoted
194 7fcffe27 Michael Hanselmann
  @rtype: str
195 7fcffe27 Michael Hanselmann
  @return: the quoted arguments concatenated with spaces
196 7fcffe27 Michael Hanselmann

197 7fcffe27 Michael Hanselmann
  """
198 7fcffe27 Michael Hanselmann
  return " ".join([ShellQuote(i) for i in args])
199 7fcffe27 Michael Hanselmann
200 7fcffe27 Michael Hanselmann
201 7fcffe27 Michael Hanselmann
class ShellWriter:
202 7fcffe27 Michael Hanselmann
  """Helper class to write scripts with indentation.
203 7fcffe27 Michael Hanselmann

204 7fcffe27 Michael Hanselmann
  """
205 7fcffe27 Michael Hanselmann
  INDENT_STR = "  "
206 7fcffe27 Michael Hanselmann
207 7fcffe27 Michael Hanselmann
  def __init__(self, fh):
208 7fcffe27 Michael Hanselmann
    """Initializes this class.
209 7fcffe27 Michael Hanselmann

210 7fcffe27 Michael Hanselmann
    """
211 7fcffe27 Michael Hanselmann
    self._fh = fh
212 7fcffe27 Michael Hanselmann
    self._indent = 0
213 7fcffe27 Michael Hanselmann
214 7fcffe27 Michael Hanselmann
  def IncIndent(self):
215 7fcffe27 Michael Hanselmann
    """Increase indentation level by 1.
216 7fcffe27 Michael Hanselmann

217 7fcffe27 Michael Hanselmann
    """
218 7fcffe27 Michael Hanselmann
    self._indent += 1
219 7fcffe27 Michael Hanselmann
220 7fcffe27 Michael Hanselmann
  def DecIndent(self):
221 7fcffe27 Michael Hanselmann
    """Decrease indentation level by 1.
222 7fcffe27 Michael Hanselmann

223 7fcffe27 Michael Hanselmann
    """
224 7fcffe27 Michael Hanselmann
    assert self._indent > 0
225 7fcffe27 Michael Hanselmann
    self._indent -= 1
226 7fcffe27 Michael Hanselmann
227 7fcffe27 Michael Hanselmann
  def Write(self, txt, *args):
228 7fcffe27 Michael Hanselmann
    """Write line to output file.
229 7fcffe27 Michael Hanselmann

230 7fcffe27 Michael Hanselmann
    """
231 7fcffe27 Michael Hanselmann
    assert self._indent >= 0
232 7fcffe27 Michael Hanselmann
233 7fcffe27 Michael Hanselmann
    self._fh.write(self._indent * self.INDENT_STR)
234 7fcffe27 Michael Hanselmann
235 7fcffe27 Michael Hanselmann
    if args:
236 7fcffe27 Michael Hanselmann
      self._fh.write(txt % args)
237 7fcffe27 Michael Hanselmann
    else:
238 7fcffe27 Michael Hanselmann
      self._fh.write(txt)
239 7fcffe27 Michael Hanselmann
240 7fcffe27 Michael Hanselmann
    self._fh.write("\n")
241 7fcffe27 Michael Hanselmann
242 7fcffe27 Michael Hanselmann
243 7fcffe27 Michael Hanselmann
def GenerateSecret(numbytes=20):
244 7fcffe27 Michael Hanselmann
  """Generates a random secret.
245 7fcffe27 Michael Hanselmann

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

249 7fcffe27 Michael Hanselmann
  @param numbytes: the number of bytes which will be represented by the returned
250 7fcffe27 Michael Hanselmann
      string (defaulting to 20, the length of a SHA1 hash)
251 7fcffe27 Michael Hanselmann
  @rtype: str
252 7fcffe27 Michael Hanselmann
  @return: an hex representation of the pseudo-random sequence
253 7fcffe27 Michael Hanselmann

254 7fcffe27 Michael Hanselmann
  """
255 7fcffe27 Michael Hanselmann
  return os.urandom(numbytes).encode("hex")
256 7fcffe27 Michael Hanselmann
257 7fcffe27 Michael Hanselmann
258 7fcffe27 Michael Hanselmann
def NormalizeAndValidateMac(mac):
259 7fcffe27 Michael Hanselmann
  """Normalizes and check if a MAC address is valid.
260 7fcffe27 Michael Hanselmann

261 7fcffe27 Michael Hanselmann
  Checks whether the supplied MAC address is formally correct, only
262 7fcffe27 Michael Hanselmann
  accepts colon separated format. Normalize it to all lower.
263 7fcffe27 Michael Hanselmann

264 7fcffe27 Michael Hanselmann
  @type mac: str
265 7fcffe27 Michael Hanselmann
  @param mac: the MAC to be validated
266 7fcffe27 Michael Hanselmann
  @rtype: str
267 7fcffe27 Michael Hanselmann
  @return: returns the normalized and validated MAC.
268 7fcffe27 Michael Hanselmann

269 7fcffe27 Michael Hanselmann
  @raise errors.OpPrereqError: If the MAC isn't valid
270 7fcffe27 Michael Hanselmann

271 7fcffe27 Michael Hanselmann
  """
272 7fcffe27 Michael Hanselmann
  if not _MAC_CHECK_RE.match(mac):
273 7fcffe27 Michael Hanselmann
    raise errors.OpPrereqError("Invalid MAC address '%s'" % mac,
274 7fcffe27 Michael Hanselmann
                               errors.ECODE_INVAL)
275 7fcffe27 Michael Hanselmann
276 7fcffe27 Michael Hanselmann
  return mac.lower()
277 7fcffe27 Michael Hanselmann
278 7fcffe27 Michael Hanselmann
279 7fcffe27 Michael Hanselmann
def SafeEncode(text):
280 7fcffe27 Michael Hanselmann
  """Return a 'safe' version of a source string.
281 7fcffe27 Michael Hanselmann

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

291 7fcffe27 Michael Hanselmann
  @type text: str or unicode
292 7fcffe27 Michael Hanselmann
  @param text: input data
293 7fcffe27 Michael Hanselmann
  @rtype: str
294 7fcffe27 Michael Hanselmann
  @return: a safe version of text
295 7fcffe27 Michael Hanselmann

296 7fcffe27 Michael Hanselmann
  """
297 7fcffe27 Michael Hanselmann
  if isinstance(text, unicode):
298 7fcffe27 Michael Hanselmann
    # only if unicode; if str already, we handle it below
299 7fcffe27 Michael Hanselmann
    text = text.encode('ascii', 'backslashreplace')
300 7fcffe27 Michael Hanselmann
  resu = ""
301 7fcffe27 Michael Hanselmann
  for char in text:
302 7fcffe27 Michael Hanselmann
    c = ord(char)
303 7fcffe27 Michael Hanselmann
    if char  == '\t':
304 7fcffe27 Michael Hanselmann
      resu += r'\t'
305 7fcffe27 Michael Hanselmann
    elif char == '\n':
306 7fcffe27 Michael Hanselmann
      resu += r'\n'
307 7fcffe27 Michael Hanselmann
    elif char == '\r':
308 7fcffe27 Michael Hanselmann
      resu += r'\'r'
309 7fcffe27 Michael Hanselmann
    elif c < 32 or c >= 127: # non-printable
310 7fcffe27 Michael Hanselmann
      resu += "\\x%02x" % (c & 0xff)
311 7fcffe27 Michael Hanselmann
    else:
312 7fcffe27 Michael Hanselmann
      resu += char
313 7fcffe27 Michael Hanselmann
  return resu
314 7fcffe27 Michael Hanselmann
315 7fcffe27 Michael Hanselmann
316 7fcffe27 Michael Hanselmann
def UnescapeAndSplit(text, sep=","):
317 7fcffe27 Michael Hanselmann
  """Split and unescape a string based on a given separator.
318 7fcffe27 Michael Hanselmann

319 7fcffe27 Michael Hanselmann
  This function splits a string based on a separator where the
320 7fcffe27 Michael Hanselmann
  separator itself can be escape in order to be an element of the
321 7fcffe27 Michael Hanselmann
  elements. The escaping rules are (assuming coma being the
322 7fcffe27 Michael Hanselmann
  separator):
323 7fcffe27 Michael Hanselmann
    - a plain , separates the elements
324 7fcffe27 Michael Hanselmann
    - a sequence \\\\, (double backslash plus comma) is handled as a
325 7fcffe27 Michael Hanselmann
      backslash plus a separator comma
326 7fcffe27 Michael Hanselmann
    - a sequence \, (backslash plus comma) is handled as a
327 7fcffe27 Michael Hanselmann
      non-separator comma
328 7fcffe27 Michael Hanselmann

329 7fcffe27 Michael Hanselmann
  @type text: string
330 7fcffe27 Michael Hanselmann
  @param text: the string to split
331 7fcffe27 Michael Hanselmann
  @type sep: string
332 7fcffe27 Michael Hanselmann
  @param text: the separator
333 7fcffe27 Michael Hanselmann
  @rtype: string
334 7fcffe27 Michael Hanselmann
  @return: a list of strings
335 7fcffe27 Michael Hanselmann

336 7fcffe27 Michael Hanselmann
  """
337 7fcffe27 Michael Hanselmann
  # we split the list by sep (with no escaping at this stage)
338 7fcffe27 Michael Hanselmann
  slist = text.split(sep)
339 7fcffe27 Michael Hanselmann
  # next, we revisit the elements and if any of them ended with an odd
340 7fcffe27 Michael Hanselmann
  # number of backslashes, then we join it with the next
341 7fcffe27 Michael Hanselmann
  rlist = []
342 7fcffe27 Michael Hanselmann
  while slist:
343 7fcffe27 Michael Hanselmann
    e1 = slist.pop(0)
344 7fcffe27 Michael Hanselmann
    if e1.endswith("\\"):
345 7fcffe27 Michael Hanselmann
      num_b = len(e1) - len(e1.rstrip("\\"))
346 7fcffe27 Michael Hanselmann
      if num_b % 2 == 1:
347 7fcffe27 Michael Hanselmann
        e2 = slist.pop(0)
348 7fcffe27 Michael Hanselmann
        # here the backslashes remain (all), and will be reduced in
349 7fcffe27 Michael Hanselmann
        # the next step
350 7fcffe27 Michael Hanselmann
        rlist.append(e1 + sep + e2)
351 7fcffe27 Michael Hanselmann
        continue
352 7fcffe27 Michael Hanselmann
    rlist.append(e1)
353 7fcffe27 Michael Hanselmann
  # finally, replace backslash-something with something
354 7fcffe27 Michael Hanselmann
  rlist = [re.sub(r"\\(.)", r"\1", v) for v in rlist]
355 7fcffe27 Michael Hanselmann
  return rlist
356 7fcffe27 Michael Hanselmann
357 7fcffe27 Michael Hanselmann
358 7fcffe27 Michael Hanselmann
def CommaJoin(names):
359 7fcffe27 Michael Hanselmann
  """Nicely join a set of identifiers.
360 7fcffe27 Michael Hanselmann

361 7fcffe27 Michael Hanselmann
  @param names: set, list or tuple
362 7fcffe27 Michael Hanselmann
  @return: a string with the formatted results
363 7fcffe27 Michael Hanselmann

364 7fcffe27 Michael Hanselmann
  """
365 7fcffe27 Michael Hanselmann
  return ", ".join([str(val) for val in names])
366 7fcffe27 Michael Hanselmann
367 7fcffe27 Michael Hanselmann
368 7fcffe27 Michael Hanselmann
def FormatTime(val):
369 7fcffe27 Michael Hanselmann
  """Formats a time value.
370 7fcffe27 Michael Hanselmann

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

376 7fcffe27 Michael Hanselmann
  """
377 7fcffe27 Michael Hanselmann
  if val is None or not isinstance(val, (int, float)):
378 7fcffe27 Michael Hanselmann
    return "N/A"
379 7fcffe27 Michael Hanselmann
  # these two codes works on Linux, but they are not guaranteed on all
380 7fcffe27 Michael Hanselmann
  # platforms
381 7fcffe27 Michael Hanselmann
  return time.strftime("%F %T", time.localtime(val))
382 7fcffe27 Michael Hanselmann
383 7fcffe27 Michael Hanselmann
384 7fcffe27 Michael Hanselmann
def FormatSeconds(secs):
385 7fcffe27 Michael Hanselmann
  """Formats seconds for easier reading.
386 7fcffe27 Michael Hanselmann

387 7fcffe27 Michael Hanselmann
  @type secs: number
388 7fcffe27 Michael Hanselmann
  @param secs: Number of seconds
389 7fcffe27 Michael Hanselmann
  @rtype: string
390 7fcffe27 Michael Hanselmann
  @return: Formatted seconds (e.g. "2d 9h 19m 49s")
391 7fcffe27 Michael Hanselmann

392 7fcffe27 Michael Hanselmann
  """
393 7fcffe27 Michael Hanselmann
  parts = []
394 7fcffe27 Michael Hanselmann
395 7fcffe27 Michael Hanselmann
  secs = round(secs, 0)
396 7fcffe27 Michael Hanselmann
397 7fcffe27 Michael Hanselmann
  if secs > 0:
398 7fcffe27 Michael Hanselmann
    # Negative values would be a bit tricky
399 7fcffe27 Michael Hanselmann
    for unit, one in [("d", 24 * 60 * 60), ("h", 60 * 60), ("m", 60)]:
400 7fcffe27 Michael Hanselmann
      (complete, secs) = divmod(secs, one)
401 7fcffe27 Michael Hanselmann
      if complete or parts:
402 7fcffe27 Michael Hanselmann
        parts.append("%d%s" % (complete, unit))
403 7fcffe27 Michael Hanselmann
404 7fcffe27 Michael Hanselmann
  parts.append("%ds" % secs)
405 7fcffe27 Michael Hanselmann
406 7fcffe27 Michael Hanselmann
  return " ".join(parts)
407 7fcffe27 Michael Hanselmann
408 7fcffe27 Michael Hanselmann
409 7fcffe27 Michael Hanselmann
class LineSplitter:
410 7fcffe27 Michael Hanselmann
  """Splits data chunks into lines separated by newline.
411 7fcffe27 Michael Hanselmann

412 7fcffe27 Michael Hanselmann
  Instances provide a file-like interface.
413 7fcffe27 Michael Hanselmann

414 7fcffe27 Michael Hanselmann
  """
415 7fcffe27 Michael Hanselmann
  def __init__(self, line_fn, *args):
416 7fcffe27 Michael Hanselmann
    """Initializes this class.
417 7fcffe27 Michael Hanselmann

418 7fcffe27 Michael Hanselmann
    @type line_fn: callable
419 7fcffe27 Michael Hanselmann
    @param line_fn: Function called for each line, first parameter is line
420 7fcffe27 Michael Hanselmann
    @param args: Extra arguments for L{line_fn}
421 7fcffe27 Michael Hanselmann

422 7fcffe27 Michael Hanselmann
    """
423 7fcffe27 Michael Hanselmann
    assert callable(line_fn)
424 7fcffe27 Michael Hanselmann
425 7fcffe27 Michael Hanselmann
    if args:
426 7fcffe27 Michael Hanselmann
      # Python 2.4 doesn't have functools.partial yet
427 7fcffe27 Michael Hanselmann
      self._line_fn = \
428 7fcffe27 Michael Hanselmann
        lambda line: line_fn(line, *args) # pylint: disable-msg=W0142
429 7fcffe27 Michael Hanselmann
    else:
430 7fcffe27 Michael Hanselmann
      self._line_fn = line_fn
431 7fcffe27 Michael Hanselmann
432 7fcffe27 Michael Hanselmann
    self._lines = collections.deque()
433 7fcffe27 Michael Hanselmann
    self._buffer = ""
434 7fcffe27 Michael Hanselmann
435 7fcffe27 Michael Hanselmann
  def write(self, data):
436 7fcffe27 Michael Hanselmann
    parts = (self._buffer + data).split("\n")
437 7fcffe27 Michael Hanselmann
    self._buffer = parts.pop()
438 7fcffe27 Michael Hanselmann
    self._lines.extend(parts)
439 7fcffe27 Michael Hanselmann
440 7fcffe27 Michael Hanselmann
  def flush(self):
441 7fcffe27 Michael Hanselmann
    while self._lines:
442 7fcffe27 Michael Hanselmann
      self._line_fn(self._lines.popleft().rstrip("\r\n"))
443 7fcffe27 Michael Hanselmann
444 7fcffe27 Michael Hanselmann
  def close(self):
445 7fcffe27 Michael Hanselmann
    self.flush()
446 7fcffe27 Michael Hanselmann
    if self._buffer:
447 7fcffe27 Michael Hanselmann
      self._line_fn(self._buffer)
448 7ebd876f Michael Hanselmann
449 7ebd876f Michael Hanselmann
450 7ebd876f Michael Hanselmann
def IsValidShellParam(word):
451 7ebd876f Michael Hanselmann
  """Verifies is the given word is safe from the shell's p.o.v.
452 7ebd876f Michael Hanselmann

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

457 7ebd876f Michael Hanselmann
  Note that we are overly restrictive here, in order to be on the safe
458 7ebd876f Michael Hanselmann
  side.
459 7ebd876f Michael Hanselmann

460 7ebd876f Michael Hanselmann
  @type word: str
461 7ebd876f Michael Hanselmann
  @param word: the word to check
462 7ebd876f Michael Hanselmann
  @rtype: boolean
463 7ebd876f Michael Hanselmann
  @return: True if the word is 'safe'
464 7ebd876f Michael Hanselmann

465 7ebd876f Michael Hanselmann
  """
466 7ebd876f Michael Hanselmann
  return bool(_SHELLPARAM_REGEX.match(word))
467 7ebd876f Michael Hanselmann
468 7ebd876f Michael Hanselmann
469 7ebd876f Michael Hanselmann
def BuildShellCmd(template, *args):
470 7ebd876f Michael Hanselmann
  """Build a safe shell command line from the given arguments.
471 7ebd876f Michael Hanselmann

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

477 7ebd876f Michael Hanselmann
  @type template: str
478 7ebd876f Michael Hanselmann
  @param template: the string holding the template for the
479 7ebd876f Michael Hanselmann
      string formatting
480 7ebd876f Michael Hanselmann
  @rtype: str
481 7ebd876f Michael Hanselmann
  @return: the expanded command line
482 7ebd876f Michael Hanselmann

483 7ebd876f Michael Hanselmann
  """
484 7ebd876f Michael Hanselmann
  for word in args:
485 7ebd876f Michael Hanselmann
    if not IsValidShellParam(word):
486 7ebd876f Michael Hanselmann
      raise errors.ProgrammerError("Shell argument '%s' contains"
487 7ebd876f Michael Hanselmann
                                   " invalid characters" % word)
488 7ebd876f Michael Hanselmann
  return template % args