cache
[tiramisu.git] / test / test_parsing_group.py
1 # coding: utf-8
2 import autopath
3 from tiramisu.setting import groups, owners
4 from tiramisu.config import Config
5 from tiramisu.option import ChoiceOption, BoolOption, IntOption, \
6     StrOption, OptionDescription
7 from tiramisu.error import SlaveError
8
9 from py.test import raises
10
11
12 def make_description():
13     numero_etab = StrOption('numero_etab', "identifiant de l'établissement")
14     nom_machine = StrOption('nom_machine', "nom de la machine", default="eoleng")
15     nombre_interfaces = IntOption('nombre_interfaces', "nombre d'interfaces à activer",
16                                   default=1)
17     activer_proxy_client = BoolOption('activer_proxy_client', "utiliser un proxy",
18                                       default=False)
19     mode_conteneur_actif = BoolOption('mode_conteneur_actif', "le serveur est en mode conteneur",
20                                       default=False)
21     adresse_serveur_ntp = StrOption('serveur_ntp', "adresse serveur ntp", multi=True)
22     time_zone = ChoiceOption('time_zone', 'fuseau horaire du serveur',
23                              ('Paris', 'Londres'), 'Paris')
24
25     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé")
26     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau")
27
28     master = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
29     interface1 = OptionDescription('interface1', '', [master])
30     interface1.impl_set_group_type(groups.family)
31
32     general = OptionDescription('general', '', [numero_etab, nom_machine,
33                                 nombre_interfaces, activer_proxy_client,
34                                 mode_conteneur_actif, adresse_serveur_ntp,
35                                 time_zone])
36     general.impl_set_group_type(groups.family)
37     creole = OptionDescription('creole', 'first tiramisu configuration', [general, interface1])
38     descr = OptionDescription('baseconfig', 'baseconifgdescr', [creole])
39     return descr
40
41
42 def test_base_config():
43     descr = make_description()
44     config = Config(descr)
45     config.read_write()
46     assert config.creole.general.activer_proxy_client is False
47     assert config.creole.general.nom_machine == "eoleng"
48     assert config.find_first(byname='nom_machine', type_='value') == "eoleng"
49     result = {'general.numero_etab': None, 'general.nombre_interfaces': 1,
50               'general.serveur_ntp': [], 'interface1.ip_admin_eth0.ip_admin_eth0': None,
51               'general.mode_conteneur_actif': False, 'general.time_zone': 'Paris',
52               'interface1.ip_admin_eth0.netmask_admin_eth0': None, 'general.nom_machine':
53               'eoleng', 'general.activer_proxy_client': False}
54     assert config.creole.make_dict() == result
55     result = {'serveur_ntp': [], 'mode_conteneur_actif': False,
56               'ip_admin_eth0': None, 'time_zone': 'Paris', 'numero_etab': None,
57               'netmask_admin_eth0': None, 'nom_machine': 'eoleng', 'activer_proxy_client':
58               False, 'nombre_interfaces': 1}
59     assert config.creole.make_dict(flatten=True) == result
60
61
62 def test_make_dict_filter():
63     descr = make_description()
64     config = Config(descr)
65     config.read_write()
66     subresult = {'numero_etab': None, 'nombre_interfaces': 1,
67                  'serveur_ntp': [], 'mode_conteneur_actif': False,
68                  'time_zone': 'Paris', 'nom_machine': 'eoleng',
69                  'activer_proxy_client': False}
70     result = {}
71     for key, value in subresult.items():
72         result['general.' + key] = value
73     assert config.creole.make_dict(withoption='numero_etab') == result
74     raises(AttributeError, "config.creole.make_dict(withoption='numero_etab', withvalue='toto')")
75     assert config.creole.make_dict(withoption='numero_etab', withvalue=None) == result
76     assert config.creole.general.make_dict(withoption='numero_etab') == subresult
77
78
79 def test_get_group_type():
80     descr = make_description()
81     config = Config(descr)
82     config.read_write()
83     grp = config.unwrap_from_path('creole.general')
84     assert grp.impl_get_group_type() == groups.family
85     assert grp.impl_get_group_type() == 'family'
86     assert isinstance(grp.impl_get_group_type(), groups.GroupType)
87     raises(TypeError, 'grp.impl_set_group_type(groups.default)')
88
89
90 def test_iter_on_groups():
91     descr = make_description()
92     config = Config(descr)
93     config.read_write()
94     result = list(config.creole.iter_groups(group_type=groups.family))
95     group_names = [res[0] for res in result]
96     #FIXME pourquoi inversé ??
97     #assert group_names == ['general', 'interface1']
98     assert group_names == ['interface1', 'general']
99
100
101 def test_iter_on_empty_group():
102     config = Config(OptionDescription("name", "descr", []))
103     config.read_write()
104     result = list(config.iter_groups())
105     assert result == []
106     for i in config.iter_groups():
107         pass
108     for i in config:
109         pass
110     assert [] == list(config)
111
112
113 def test_iter_not_group():
114     config = Config(OptionDescription("name", "descr", []))
115     config.read_write()
116     raises(TypeError, "list(config.iter_groups(group_type='family'))")
117
118
119 def test_groups_with_master():
120     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
121     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
122     interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
123     interface1.impl_set_group_type(groups.master)
124     assert interface1.impl_get_group_type() == groups.master
125
126
127 def test_groups_with_master_in_config():
128     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
129     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
130     interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
131     interface1.impl_set_group_type(groups.master)
132     Config(interface1)
133     assert interface1.impl_get_group_type() == groups.master
134
135
136 def test_allowed_groups():
137     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
138     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
139     interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
140     raises(ValueError, "interface1.impl_set_group_type('toto')")
141
142
143 def test_master_not_valid_name():
144     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
145     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
146     invalid_group = OptionDescription('interface1', '', [ip_admin_eth0, netmask_admin_eth0])
147     raises(ValueError, "invalid_group.impl_set_group_type(groups.master)")
148
149
150 def test_sub_group_in_master_group():
151     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
152     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
153     subgroup = OptionDescription("subgroup", '', [])
154     invalid_group = OptionDescription('ip_admin_eth0', '', [subgroup, ip_admin_eth0, netmask_admin_eth0])
155     raises(ValueError, "invalid_group.impl_set_group_type(groups.master)")
156
157
158 def test_group_always_has_multis():
159     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
160     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau")
161     group = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
162     raises(ValueError, "group.impl_set_group_type(groups.master)")
163
164
165 #____________________________________________________________
166 def test_values_with_master_and_slaves():
167     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
168     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
169     interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
170     interface1.impl_set_group_type(groups.master)
171     maconfig = OptionDescription('toto', '', [interface1])
172     cfg = Config(maconfig)
173     cfg.read_write()
174     owner = cfg.cfgimpl_get_settings().getowner()
175     assert interface1.impl_get_group_type() == groups.master
176     assert cfg.getowner(ip_admin_eth0) == owners.default
177     assert cfg.getowner(netmask_admin_eth0) == owners.default
178     assert cfg.ip_admin_eth0.netmask_admin_eth0 == []
179     cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
180     assert cfg.ip_admin_eth0.ip_admin_eth0 == ["192.168.230.145"]
181     assert cfg.ip_admin_eth0.netmask_admin_eth0 == [None]
182     assert cfg.getowner(ip_admin_eth0) == owner
183     assert cfg.getowner(netmask_admin_eth0) == owners.default
184     cfg.ip_admin_eth0.ip_admin_eth0 = ["192.168.230.145", "192.168.230.147"]
185     raises(SlaveError, 'cfg.ip_admin_eth0.netmask_admin_eth0.append(None)')
186     raises(SlaveError, 'cfg.ip_admin_eth0.netmask_admin_eth0.pop(0)')
187
188
189 def test_reset_values_with_master_and_slaves():
190     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
191     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
192     interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
193     interface1.impl_set_group_type(groups.master)
194     maconfig = OptionDescription('toto', '', [interface1])
195     cfg = Config(maconfig)
196     cfg.read_write()
197     owner = cfg.cfgimpl_get_settings().getowner()
198     assert interface1.impl_get_group_type() == groups.master
199     assert cfg.getowner(ip_admin_eth0) == owners.default
200     assert cfg.getowner(netmask_admin_eth0) == owners.default
201     cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
202     assert cfg.getowner(ip_admin_eth0) == owner
203     assert cfg.getowner(netmask_admin_eth0) == owners.default
204     del(cfg.ip_admin_eth0.ip_admin_eth0)
205     assert cfg.getowner(ip_admin_eth0) == owners.default
206     assert cfg.getowner(netmask_admin_eth0) == owners.default
207     assert cfg.ip_admin_eth0.ip_admin_eth0 == []
208     assert cfg.ip_admin_eth0.netmask_admin_eth0 == []
209
210
211 def test_values_with_master_and_slaves_slave():
212     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
213     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
214     interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
215     interface1.impl_set_group_type(groups.master)
216     maconfig = OptionDescription('toto', '', [interface1])
217     cfg = Config(maconfig)
218     cfg.read_write()
219     assert cfg.ip_admin_eth0.netmask_admin_eth0 == []
220     raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0 = ['255.255.255.0']")
221     cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
222     cfg.ip_admin_eth0.netmask_admin_eth0 = ['255.255.255.0']
223     cfg.ip_admin_eth0.netmask_admin_eth0[0] = '255.255.255.0'
224     raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0 = ['255.255.255.0', '255.255.255.0']")
225     raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0 = []")
226     del(cfg.ip_admin_eth0.netmask_admin_eth0)
227     cfg.ip_admin_eth0.netmask_admin_eth0 = ['255.255.255.0']
228     cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
229     assert cfg.ip_admin_eth0.netmask_admin_eth0 == ['255.255.255.0', None]
230     cfg.ip_admin_eth0.netmask_admin_eth0 = ['255.255.255.0', '255.255.255.0']
231     raises(SlaveError, 'cfg.ip_admin_eth0.netmask_admin_eth0.pop(1)')
232
233
234 def test_values_with_master_and_slaves_master():
235     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
236     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
237     interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
238     interface1.impl_set_group_type(groups.master)
239     maconfig = OptionDescription('toto', '', [interface1])
240     cfg = Config(maconfig)
241     cfg.read_write()
242     cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
243     cfg.ip_admin_eth0.ip_admin_eth0 = ["192.168.230.145"]
244     cfg.ip_admin_eth0.ip_admin_eth0 = ["192.168.230.145", "192.168.230.145"]
245     cfg.ip_admin_eth0.netmask_admin_eth0 = ['255.255.255.0', '255.255.255.0']
246     raises(SlaveError, 'cfg.ip_admin_eth0.ip_admin_eth0 = ["192.168.230.145"]')
247     assert cfg.ip_admin_eth0.netmask_admin_eth0 == ['255.255.255.0', '255.255.255.0']
248     cfg.ip_admin_eth0.ip_admin_eth0.pop(1)
249     assert cfg.ip_admin_eth0.ip_admin_eth0 == ["192.168.230.145"]
250     assert cfg.ip_admin_eth0.netmask_admin_eth0 == ['255.255.255.0']
251     del(cfg.ip_admin_eth0.ip_admin_eth0)
252     assert cfg.ip_admin_eth0.ip_admin_eth0 == []
253     assert cfg.ip_admin_eth0.netmask_admin_eth0 == []
254
255
256 def test_values_with_master_and_slaves_master_error():
257     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
258     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
259     interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
260     interface1.impl_set_group_type(groups.master)
261     maconfig = OptionDescription('toto', '', [interface1])
262     cfg = Config(maconfig)
263     cfg.read_write()
264     cfg.ip_admin_eth0.ip_admin_eth0 = ["192.168.230.145", "192.168.230.145"]
265     raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0 = ['255.255.255.0']")
266     raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0 = ['255.255.255.0', '255.255.255.0', '255.255.255.0']")
267     cfg.ip_admin_eth0.netmask_admin_eth0 = ['255.255.255.0', '255.255.255.0']
268     raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0 = ['255.255.255.0']")
269     raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0 = ['255.255.255.0', '255.255.255.0', '255.255.255.0']")
270
271
272 def test_values_with_master_owner():
273     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
274     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
275     interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
276     interface1.impl_set_group_type(groups.master)
277     maconfig = OptionDescription('toto', '', [interface1])
278     cfg = Config(maconfig)
279     cfg.read_write()
280     owner = cfg.cfgimpl_get_settings().getowner()
281     assert cfg.getowner(ip_admin_eth0) == owners.default
282     assert cfg.getowner(netmask_admin_eth0) == owners.default
283     cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
284     assert cfg.getowner(ip_admin_eth0) == owner
285     assert cfg.getowner(netmask_admin_eth0) == owners.default
286     cfg.ip_admin_eth0.ip_admin_eth0.pop(0)
287     assert cfg.getowner(ip_admin_eth0) == owner
288     assert cfg.getowner(netmask_admin_eth0) == owners.default
289
290
291 def test_values_with_master_disabled():
292     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
293     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
294     interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
295     interface1.impl_set_group_type(groups.master)
296     maconfig = OptionDescription('toto', '', [interface1])
297     cfg = Config(maconfig)
298     cfg.read_write()
299     cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
300     cfg.ip_admin_eth0.ip_admin_eth0.pop(0)
301     cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
302     cfg.ip_admin_eth0.netmask_admin_eth0 = ["192.168.230.145"]
303     cfg.ip_admin_eth0.ip_admin_eth0.pop(0)
304     del(cfg.ip_admin_eth0.netmask_admin_eth0)
305     cfg.cfgimpl_get_settings()[netmask_admin_eth0].append('disabled')
306     cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
307     cfg.ip_admin_eth0.ip_admin_eth0.pop(0)
308
309     #delete with value in disabled var
310     cfg.cfgimpl_get_settings()[netmask_admin_eth0].remove('disabled')
311     cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
312     cfg.ip_admin_eth0.netmask_admin_eth0 = ["192.168.230.145"]
313     cfg.cfgimpl_get_settings()[netmask_admin_eth0].append('disabled')
314     cfg.ip_admin_eth0.ip_admin_eth0.pop(0)
315
316     #append with value in disabled var
317     cfg.cfgimpl_get_settings()[netmask_admin_eth0].remove('disabled')
318     cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
319     cfg.ip_admin_eth0.netmask_admin_eth0 = ["192.168.230.145"]
320     cfg.cfgimpl_get_settings()[netmask_admin_eth0].append('disabled')
321     cfg.ip_admin_eth0.ip_admin_eth0.append('192.168.230.43')
322
323
324 def test_multi_insert():
325     var = StrOption('var', '', ['ok'], multi=True)
326     od = OptionDescription('od', '', [var])
327     c = Config(od)
328     c.read_write()
329     assert c.var == ['ok']
330     assert c.getowner(var) == owners.default
331     c.var.insert(0, 'nok')
332     assert c.var == ['nok', 'ok']
333     assert c.getowner(var) != owners.default
334
335
336 def test_multi_insert_master():
337     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
338     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
339     interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
340     interface1.impl_set_group_type(groups.master)
341     maconfig = OptionDescription('toto', '', [interface1])
342     cfg = Config(maconfig)
343     cfg.read_write()
344     raises(SlaveError, "cfg.ip_admin_eth0.ip_admin_eth0.insert(0, 'nok')")
345     raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0.insert(0, 'nok')")
346
347
348 def test_multi_sort():
349     var = StrOption('var', '', ['ok', 'nok'], multi=True)
350     od = OptionDescription('od', '', [var])
351     c = Config(od)
352     c.read_write()
353     assert c.var == ['ok', 'nok']
354     assert c.getowner(var) == owners.default
355     c.var.sort()
356     assert c.var == ['nok', 'ok']
357     assert c.getowner(var) != owners.default
358
359
360 def test_multi_sort_master():
361     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
362     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
363     interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
364     interface1.impl_set_group_type(groups.master)
365     maconfig = OptionDescription('toto', '', [interface1])
366     cfg = Config(maconfig)
367     cfg.read_write()
368     raises(SlaveError, "cfg.ip_admin_eth0.ip_admin_eth0.sort()")
369     raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0.sort()")
370
371
372 def test_multi_reverse():
373     var = StrOption('var', '', ['ok', 'nok'], multi=True)
374     od = OptionDescription('od', '', [var])
375     c = Config(od)
376     c.read_write()
377     assert c.var == ['ok', 'nok']
378     assert c.getowner(var) == owners.default
379     c.var.reverse()
380     assert c.var == ['nok', 'ok']
381     assert c.getowner(var) != owners.default
382
383
384 def test_multi_reverse_master():
385     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
386     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
387     interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
388     interface1.impl_set_group_type(groups.master)
389     maconfig = OptionDescription('toto', '', [interface1])
390     cfg = Config(maconfig)
391     cfg.read_write()
392     raises(SlaveError, "cfg.ip_admin_eth0.ip_admin_eth0.reverse()")
393     raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0.reverse()")
394
395
396 def test_multi_extend():
397     var = StrOption('var', '', ['ok', 'nok'], multi=True)
398     od = OptionDescription('od', '', [var])
399     c = Config(od)
400     c.read_write()
401     assert c.var == ['ok', 'nok']
402     assert c.getowner(var) == owners.default
403     c.var.extend(['pok'])
404     assert c.var == ['ok', 'nok', 'pok']
405     assert c.getowner(var) != owners.default
406
407
408 def test_multi_extend_master():
409     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
410     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
411     interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
412     interface1.impl_set_group_type(groups.master)
413     maconfig = OptionDescription('toto', '', [interface1])
414     cfg = Config(maconfig)
415     cfg.read_write()
416     raises(SlaveError, "cfg.ip_admin_eth0.ip_admin_eth0.extend(['ok'])")
417     raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0.extend(['ok'])")
418
419
420 def test_multi_non_valid_value():
421     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
422     maconfig = OptionDescription('toto', '', [ip_admin_eth0])
423     cfg = Config(maconfig)
424     cfg.read_write()
425     cfg.ip_admin_eth0 = ['a']
426     raises(ValueError, 'cfg.ip_admin_eth0[0] = 1')