1 # -*- coding: UTF-8 -*- |
1 # -*- coding: UTF-8 -*- |
2 # Copyright (c) 2008 - 2010, Pascal Volk |
2 # Copyright (c) 2008 - 2010, Pascal Volk |
3 # See COPYING for distribution information. |
3 # See COPYING for distribution information. |
|
4 """ |
|
5 VirtualMailManager.ext.Postconf |
|
6 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
4 |
7 |
5 """A small - r/o - wrapper class for Postfix' postconf.""" |
8 Wrapper class for Postfix's postconf. |
|
9 Postconf instances can be used to read actual values of configuration |
|
10 parameters or edit the value of a configuration parameter. |
|
11 |
|
12 postconf.read(parameter) -> value |
|
13 postconf.edit(parameter, value) |
|
14 """ |
6 |
15 |
7 import re |
16 import re |
8 from subprocess import Popen, PIPE |
17 from subprocess import Popen, PIPE |
9 |
18 |
10 import VirtualMailManager.constants.ERROR as ERR |
|
11 from VirtualMailManager.errors import VMMError |
19 from VirtualMailManager.errors import VMMError |
|
20 from VirtualMailManager.constants.ERROR import VMM_ERROR |
12 |
21 |
13 RE_PC_PARAMS = """^\w+$""" |
22 _ = lambda msg: msg |
14 RE_PC_VARIABLES = r"""\$\b\w+\b""" |
23 |
15 |
24 |
16 class Postconf(object): |
25 class Postconf(object): |
17 __slots__ = ('__bin', '__val', '__varFinder') |
26 """Wrapper class for Postfix's postconf.""" |
|
27 __slots__ = ('_bin', '_val') |
|
28 _parameter_re = re.compile(r'^\w+$') |
|
29 _variables_re = re.compile(r'\$\b\w+\b') |
|
30 |
18 def __init__(self, postconf_bin): |
31 def __init__(self, postconf_bin): |
19 """Creates a new Postconf instance. |
32 """Creates a new Postconf instance. |
20 |
33 |
21 Keyword arguments: |
34 Argument: |
22 postconf_bin -- absolute path to the Postfix postconf binary (str) |
35 |
|
36 `postconf_bin` : str |
|
37 absolute path to the Postfix postconf binary. |
23 """ |
38 """ |
24 self.__bin = postconf_bin |
39 self._bin = postconf_bin |
25 self.__val = '' |
40 self._val = '' |
26 self.__varFinder = re.compile(RE_PC_VARIABLES) |
41 |
|
42 def edit(self, parameter, value): |
|
43 """Set the `parameter`'s value to `value`. |
|
44 |
|
45 Arguments: |
|
46 |
|
47 `parameter` : str |
|
48 the name of a Postfix configuration parameter |
|
49 `value` : str |
|
50 the parameter's new value. |
|
51 """ |
|
52 self._check_parameter(parameter) |
|
53 stdout, stderr = Popen((self._bin, '-e', parameter + '=' + str(value)), |
|
54 stderr=PIPE).communicate() |
|
55 if stderr: |
|
56 raise VMMError(stderr.strip(), VMM_ERROR) |
27 |
57 |
28 def read(self, parameter, expand_vars=True): |
58 def read(self, parameter, expand_vars=True): |
29 """Returns the parameters value. |
59 """Returns the parameters value. |
30 |
60 |
31 If expand_vars is True (default), all variables in the value will be |
61 If expand_vars is True (default), all variables in the value will be |
32 expanded: |
62 expanded: |
33 e.g. mydestination -> mail.example.com, localhost.example.com, localhost |
63 e.g. mydestination: mail.example.com, localhost.example.com, localhost |
34 Otherwise the value may contain one or more variables. |
64 Otherwise the value may contain one or more variables. |
35 e.g. mydestination -> $myhostname, localhost.$mydomain, localhost |
65 e.g. mydestination: $myhostname, localhost.$mydomain, localhost |
36 |
66 |
37 Keyword arguments: |
67 Arguments: |
38 parameter -- the name of a Postfix configuration parameter (str) |
68 |
39 expand_vars -- default True (bool) |
69 `parameter` : str |
|
70 the name of a Postfix configuration parameter. |
|
71 `expand_vars` : bool |
|
72 indicates if variables should be expanded or not, default True |
40 """ |
73 """ |
41 if not re.match(RE_PC_PARAMS, parameter): |
74 self._check_parameter(parameter) |
|
75 self._val = self._read(parameter) |
|
76 if expand_vars: |
|
77 self._expand_vars() |
|
78 return self._val |
|
79 |
|
80 def _check_parameter(self, parameter): |
|
81 """Check that the `parameter` looks like a configuration parameter. |
|
82 If not, a VMMError will be raised.""" |
|
83 if not self.__class__._parameter_re.match(parameter): |
42 raise VMMError(_(u"The value '%s' doesn't look like a valid " |
84 raise VMMError(_(u"The value '%s' doesn't look like a valid " |
43 u"postfix configuration parameter name.") % |
85 u"postfix configuration parameter name.") % |
44 parameter, ERR.VMM_ERROR) |
86 parameter, VMM_ERROR) |
45 self.__val = self.__read(parameter) |
|
46 if expand_vars: |
|
47 self.__expandVars() |
|
48 return self.__val |
|
49 |
87 |
50 def __expandVars(self): |
88 def _expand_vars(self): |
|
89 """Expand the $variables in self._val to their values.""" |
51 while True: |
90 while True: |
52 pvars = set(self.__varFinder.findall(self.__val)) |
91 pvars = set(self.__class__._variables_re.findall(self._val)) |
53 pvars_len = len(pvars) |
92 if not pvars: |
54 if pvars_len < 1: |
|
55 break |
93 break |
56 if pvars_len > 1: |
94 if len(pvars) > 1: |
57 self.__expandMultiVars(self.__readMulti(pvars)) |
95 self._expand_multi_vars(self._read_multi(pvars)) |
58 continue |
96 continue |
59 pvars = pvars.pop() |
97 pvars = pvars.pop() |
60 self.__val = self.__val.replace(pvars, self.__read(pvars[1:])) |
98 self._val = self._val.replace(pvars, self._read(pvars[1:])) |
61 |
99 |
62 def __expandMultiVars(self, old_new): |
100 def _expand_multi_vars(self, old_new): |
63 for old, new in old_new.items(): |
101 """Replace all $vars in self._val with their values.""" |
64 self.__val = self.__val.replace('$'+old, new) |
102 for old, new in old_new.iteritems(): |
|
103 self._val = self._val.replace('$' + old, new) |
65 |
104 |
66 def __read(self, parameter): |
105 def _read(self, parameter): |
67 out, err = Popen([self.__bin, '-h', parameter], stdout=PIPE, |
106 """Ask postconf for the value of a single configuration parameter.""" |
68 stderr=PIPE).communicate() |
107 stdout, stderr = Popen([self._bin, '-h', parameter], stdout=PIPE, |
69 if len(err): |
108 stderr=PIPE).communicate() |
70 raise VMMError(err.strip(), ERR.VMM_ERROR) |
109 if stderr: |
71 return out.strip() |
110 raise VMMError(stderr.strip(), VMM_ERROR) |
|
111 return stdout.strip() |
72 |
112 |
73 def __readMulti(self, parameters): |
113 def _read_multi(self, parameters): |
74 cmd = [self.__bin] |
114 """Ask postconf for multiple configuration parameters. Returns a dict |
|
115 parameter: value items.""" |
|
116 cmd = [self._bin] |
75 cmd.extend(parameter[1:] for parameter in parameters) |
117 cmd.extend(parameter[1:] for parameter in parameters) |
76 out, err = Popen(cmd, stdout=PIPE, stderr=PIPE).communicate() |
118 stdout, stderr = Popen(cmd, stdout=PIPE, stderr=PIPE).communicate() |
77 if len(err): |
119 if stderr: |
78 raise VMMError(err.strip(), ERR.VMM_ERROR) |
120 raise VMMError(stderr.strip(), VMM_ERROR) |
79 par_val = {} |
121 par_val = {} |
80 for line in out.splitlines(): |
122 for line in stdout.splitlines(): |
81 par, val = line.split(' = ') |
123 par, val = line.split(' = ') |
82 par_val[par] = val |
124 par_val[par] = val |
83 return par_val |
125 return par_val |
84 |
126 |
|
127 del _ |