make the comment about slice creation a bit more explicit
[myplc.git] / db-config
1 #!/usr/bin/env /usr/bin/plcsh
2 #
3 # Bootstraps the PLC database with a default administrator account and
4 # a default site, defines default slice attribute types, and
5 # creates/updates default system slices.
6 #
7 # Mark Huang <mlhuang@cs.princeton.edu>
8 # Copyright (C) 2006 The Trustees of Princeton University
9 #
10 # $Id: db-config,v 1.11 2006/12/12 10:13:31 thierry Exp $
11 #
12
13 from plc_config import PLCConfiguration
14 import sys
15
16 def main():
17     cfg = PLCConfiguration()
18     cfg.load()
19     variables = cfg.variables()
20
21     # Load variables into dictionaries
22     for category_id, (category, variablelist) in variables.iteritems():
23         globals()[category_id] = dict(zip(variablelist.keys(),
24                                        [variable['value'] for variable in variablelist.values()]))
25
26     # Create/update the default administrator account (should be
27     # person_id 2).
28     admin = { 'person_id': 2,
29               'first_name': "Default",
30               'last_name': "Administrator",
31               'email': plc['root_user'],
32               'password': plc['root_password'] }
33     persons = GetPersons([admin['person_id']])
34     if not persons:
35         person_id = AddPerson(admin)
36         if person_id != admin['person_id']:
37             # Huh? Someone deleted the account manually from the database.
38             DeletePerson(person_id)
39             raise Exception, "Someone deleted the \"%s %s\" account from the database!" % \
40                   (admin['first_name'], admin['last_name'])
41         UpdatePerson(person_id, { 'enabled': True })
42     else:
43         person_id = persons[0]['person_id']
44         UpdatePerson(person_id, admin)
45
46     # Create/update the default site (should be site_id 1)
47     if plc_www['port'] == '80':
48         url = "http://" + plc_www['host'] + "/"
49     elif plc_www['port'] == '443':
50         url = "https://" + plc_www['host'] + "/"
51     else:
52         url = "http://" + plc_www['host'] + ":" + plc_www['port'] + "/"
53     site = { 'site_id': 1,
54              'name': plc['name'] + " Central",
55              'abbreviated_name': plc['name'],
56              'login_base': plc['slice_prefix'],
57              'is_public': False,
58              'url': url,
59              'max_slices': 100 }
60
61     sites = GetSites([site['site_id']])
62     if not sites:
63         site_id = AddSite(site['name'], site['abbreviated_name'], site['login_base'], site)
64         if site_id != site['site_id']:
65             DeleteSite(site_id)
66             raise Exception, "Someone deleted the \"%s\" site from the database!" % \
67                   site['name']
68         sites = [site]
69
70     # Must call UpdateSite() even after AddSite() to update max_slices
71     site_id = sites[0]['site_id']
72     UpdateSite(site_id, site)
73
74     # The default administrator account must be associated with a site
75     # in order to login.
76     AddPersonToSite(admin['person_id'], site['site_id'])
77     SetPersonPrimarySite(admin['person_id'], site['site_id'])
78
79     # Grant admin and PI roles to the default administrator account
80     AddRoleToPerson(10, admin['person_id'])
81     AddRoleToPerson(20, admin['person_id'])
82
83     # Setup default PlanetLabConf entries
84     default_conf_files = [
85         # NTP configuration
86         {'enabled': True,
87          'source': 'PlanetLabConf/ntp.conf.php',
88          'dest': '/etc/ntp.conf',
89          'file_permissions': '644',
90          'file_owner': 'root',
91          'file_group': 'root',
92          'preinstall_cmd': '',
93          'postinstall_cmd': '/etc/rc.d/init.d/ntpd restart',
94          'error_cmd': '',
95          'ignore_cmd_errors': False,
96          'always_update': False},
97         {'enabled': True,
98          'source': 'PlanetLabConf/ntp/step-tickers.php',
99          'dest': '/etc/ntp/step-tickers',
100          'file_permissions': '644',
101          'file_owner': 'root',
102          'file_group': 'root',
103          'preinstall_cmd': '',
104          'postinstall_cmd': '/etc/rc.d/init.d/ntpd restart',
105          'error_cmd': '',
106          'ignore_cmd_errors': False,
107          'always_update': False},
108
109         # SSH server configuration
110         {'enabled': True,
111          'source': 'PlanetLabConf/sshd_config',
112          'dest': '/etc/ssh/sshd_config',
113          'file_permissions': '600',
114          'file_owner': 'root',
115          'file_group': 'root',
116          'preinstall_cmd': '',
117          'postinstall_cmd': '/etc/init.d/sshd restart',
118          'error_cmd': '',
119          'ignore_cmd_errors': False,
120          'always_update': False},
121
122         # Administrative SSH keys
123         {'enabled': True,
124          'source': 'PlanetLabConf/keys.php?root',
125          'dest': '/root/.ssh/authorized_keys',
126          'file_permissions': '644',
127          'file_owner': 'root',
128          'file_group': 'root',
129          'preinstall_cmd': '',
130          'postinstall_cmd': '/bin/chmod 700 /root/.ssh',
131          'error_cmd': '',
132          'ignore_cmd_errors': False,
133          'always_update': False},
134         {'enabled': True,
135          'source': 'PlanetLabConf/keys.php?site_admin',
136          'dest': '/home/site_admin/.ssh/authorized_keys',
137          'file_permissions': '644',
138          'file_owner': 'site_admin',
139          'file_group': 'site_admin',
140          'preinstall_cmd': 'grep -q site_admin /etc/passwd',
141          'postinstall_cmd': '/bin/chmod 700 /home/site_admin/.ssh',
142          'error_cmd': '',
143          'ignore_cmd_errors': False,
144          'always_update': False},
145         {'enabled': True,
146          'source': 'PlanetLabConf/keys.php?role=admin',
147          'dest': '/home/pl_admin/.ssh/authorized_keys',
148          'file_permissions': '644',
149          'file_owner': 'pl_admin',
150          'file_group': 'pl_admin',
151          'preinstall_cmd': 'grep -q pl_admin /etc/passwd',
152          'postinstall_cmd': '/bin/chmod 700 /home/pl_admin/.ssh',
153          'error_cmd': '',
154          'ignore_cmd_errors': False,
155          'always_update': False},
156
157         # Log rotation configuration
158         {'enabled': True,
159          'source': 'PlanetLabConf/logrotate.conf',
160          'dest': '/etc/logrotate.conf',
161          'file_permissions': '644',
162          'file_owner': 'root',
163          'file_group': 'root',
164          'preinstall_cmd': '',
165          'postinstall_cmd': '',
166          'error_cmd': '',
167          'ignore_cmd_errors': False,
168          'always_update': False},
169
170         # updatedb/locate nightly cron job
171         {'enabled': True,
172          'source': 'PlanetLabConf/slocate.cron',
173          'dest': '/etc/cron.daily/slocate.cron',
174          'file_permissions': '755',
175          'file_owner': 'root',
176          'file_group': 'root',
177          'preinstall_cmd': '',
178          'postinstall_cmd': '',
179          'error_cmd': '',
180          'ignore_cmd_errors': False,
181          'always_update': False},
182
183         # YUM configuration
184         {'enabled': True,
185          'source': 'PlanetLabConf/yum.conf.php?gpgcheck=1',
186          'dest': '/etc/yum.conf',
187          'file_permissions': '644',
188          'file_owner': 'root',
189          'file_group': 'root',
190          'preinstall_cmd': '',
191          'postinstall_cmd': '',
192          'error_cmd': '',
193          'ignore_cmd_errors': False,
194          'always_update': False},
195         {'enabled': True,
196          'source': 'PlanetLabConf/delete-rpm-list-production',
197          'dest': '/etc/planetlab/delete-rpm-list',
198          'file_permissions': '644',
199          'file_owner': 'root',
200          'file_group': 'root',
201          'preinstall_cmd': '',
202          'postinstall_cmd': '',
203          'error_cmd': '',
204          'ignore_cmd_errors': False,
205          'always_update': False},
206
207         # PLC configuration
208         {'enabled': True,
209          'source': 'PlanetLabConf/get_plc_config.php',
210          'dest': '/etc/planetlab/plc_config',
211          'file_permissions': '644',
212          'file_owner': 'root',
213          'file_group': 'root',
214          'preinstall_cmd': '',
215          'postinstall_cmd': '',
216          'error_cmd': '',
217          'ignore_cmd_errors': False,
218          'always_update': False},
219         {'enabled': True,
220          'source': 'PlanetLabConf/get_plc_config.php?python',
221          'dest': '/etc/planetlab/plc_config.py',
222          'file_permissions': '644',
223          'file_owner': 'root',
224          'file_group': 'root',
225          'preinstall_cmd': '',
226          'postinstall_cmd': '',
227          'error_cmd': '',
228          'ignore_cmd_errors': False,
229          'always_update': False},
230         {'enabled': True,
231          'source': 'PlanetLabConf/get_plc_config.php?perl',
232          'dest': '/etc/planetlab/plc_config.pl',
233          'file_permissions': '644',
234          'file_owner': 'root',
235          'file_group': 'root',
236          'preinstall_cmd': '',
237          'postinstall_cmd': '',
238          'error_cmd': '',
239          'ignore_cmd_errors': False,
240          'always_update': False},
241         {'enabled': True,
242          'source': 'PlanetLabConf/get_plc_config.php?php',
243          'dest': '/etc/planetlab/php/plc_config.php',
244          'file_permissions': '644',
245          'file_owner': 'root',
246          'file_group': 'root',
247          'preinstall_cmd': '',
248          'postinstall_cmd': '',
249          'error_cmd': '',
250          'ignore_cmd_errors': False,
251          'always_update': False},
252
253         # XXX Required for old Node Manager
254         # Node Manager configuration
255         {'enabled': True,
256          'source': 'PlanetLabConf/pl_nm.conf',
257          'dest': '/etc/planetlab/pl_nm.conf',
258          'file_permissions': '644',
259          'file_owner': 'root',
260          'file_group': 'root',
261          'preinstall_cmd': '',
262          'postinstall_cmd': '/etc/init.d/pl_nm restart',
263          'error_cmd': '',
264          'ignore_cmd_errors': False,
265          'always_update': False},
266         {'enabled': True,
267          'source': 'PlanetLabConf/RootResources/plc_slice_pool.php',
268          'dest': '/home/pl_nm/RootResources/plc_slice_pool',
269          'file_permissions': '644',
270          'file_owner': 'pl_nm',
271          'file_group': 'pl_nm',
272          'preinstall_cmd': '',
273          'postinstall_cmd': '',
274          'error_cmd': '',
275          'ignore_cmd_errors': False,
276          'always_update': False},
277         {'enabled': True,
278          'source': 'PlanetLabConf/RootResources/pl_conf.py',
279          'dest': '/home/pl_nm/RootResources/pl_conf',
280          'file_permissions': '644',
281          'file_owner': 'pl_nm',
282          'file_group': 'pl_nm',
283          'preinstall_cmd': '',
284          'postinstall_cmd': '/etc/init.d/pl_nm restart',
285          'error_cmd': '',
286          'ignore_cmd_errors': False,
287          'always_update': False},
288         {'enabled': True,
289          'source': 'PlanetLabConf/RootResources/pl_netflow.py',
290          'dest': '/home/pl_nm/RootResources/pl_netflow',
291          'file_permissions': '644',
292          'file_owner': 'pl_nm',
293          'file_group': 'pl_nm',
294          'preinstall_cmd': '',
295          'postinstall_cmd': '',
296          'error_cmd': '',
297          'ignore_cmd_errors': False,
298          'always_update': False},
299
300         # XXX Required for old Node Manager
301         # Proper configuration
302         {'enabled': True,
303          'source': 'PlanetLabConf/propd.conf',
304          'dest': '/etc/proper/propd.conf',
305          'file_permissions': '644',
306          'file_owner': 'root',
307          'file_group': 'root',
308          'preinstall_cmd': '',
309          'postinstall_cmd': '/etc/init.d/proper restart',
310          'error_cmd': '',
311          'ignore_cmd_errors': True,
312          'always_update': False},
313
314         # XXX Required for old Node Manager
315         # Bandwidth cap
316         {'enabled': True,
317          'source': 'PlanetLabConf/bwlimit.php',
318          'dest': '/etc/planetlab/bwcap',
319          'file_permissions': '644',
320          'file_owner': 'root',
321          'file_group': 'root',
322          'preinstall_cmd': '',
323          'postinstall_cmd': '/etc/init.d/pl_nm restart',
324          'error_cmd': '',
325          'ignore_cmd_errors': True,
326          'always_update': False},
327
328         # Proxy ARP setup
329         {'enabled': True,
330          'source': 'PlanetLabConf/proxies.php',
331          'dest': '/etc/planetlab/proxies',
332          'file_permissions': '644',
333          'file_owner': 'root',
334          'file_group': 'root',
335          'preinstall_cmd': '',
336          'postinstall_cmd': '',
337          'error_cmd': '',
338          'ignore_cmd_errors': False,
339          'always_update': False},
340
341         # Firewall configuration
342         {'enabled': True,
343          'source': 'PlanetLabConf/iptables',
344          'dest': '/etc/sysconfig/iptables',
345          'file_permissions': '600',
346          'file_owner': 'root',
347          'file_group': 'root',
348          'preinstall_cmd': '',
349          'postinstall_cmd': '',
350          'error_cmd': '',
351          'ignore_cmd_errors': False,
352          'always_update': False},
353         {'enabled': True,
354          'source': 'PlanetLabConf/blacklist.php',
355          'dest': '/etc/planetlab/blacklist',
356          'file_permissions': '600',
357          'file_owner': 'root',
358          'file_group': 'root',
359          'preinstall_cmd': '',
360          'postinstall_cmd': '/sbin/iptables-restore --noflush < /etc/planetlab/blacklist',
361          'error_cmd': '',
362          'ignore_cmd_errors': True,
363          'always_update': False},
364
365         # /etc/issue
366         {'enabled': True,
367          'source': 'PlanetLabConf/issue.php',
368          'dest': '/etc/issue',
369          'file_permissions': '644',
370          'file_owner': 'root',
371          'file_group': 'root',
372          'preinstall_cmd': '',
373          'postinstall_cmd': '',
374          'error_cmd': '',
375          'ignore_cmd_errors': False,
376          'always_update': False},
377
378         # Kernel parameters
379         {'enabled': True,
380          'source': 'PlanetLabConf/sysctl.php',
381          'dest': '/etc/sysctl.conf',
382          'file_permissions': '644',
383          'file_owner': 'root',
384          'file_group': 'root',
385          'preinstall_cmd': '',
386          'postinstall_cmd': '/sbin/sysctl -e -p /etc/sysctl.conf',
387          'error_cmd': '',
388          'ignore_cmd_errors': False,
389          'always_update': False},
390
391         # Sendmail configuration
392         {'enabled': True,
393          'source': 'PlanetLabConf/sendmail.mc',
394          'dest': '/etc/mail/sendmail.mc',
395          'file_permissions': '644',
396          'file_owner': 'root',
397          'file_group': 'root',
398          'preinstall_cmd': '',
399          'postinstall_cmd': '',
400          'error_cmd': '',
401          'ignore_cmd_errors': False,
402          'always_update': False},
403         {'enabled': True,
404          'source': 'PlanetLabConf/sendmail.cf',
405          'dest': '/etc/mail/sendmail.cf',
406          'file_permissions': '644',
407          'file_owner': 'root',
408          'file_group': 'root',
409          'preinstall_cmd': '',
410          'postinstall_cmd': 'service sendmail restart',
411          'error_cmd': '',
412          'ignore_cmd_errors': False,
413          'always_update': False},
414
415         # GPG signing keys
416         {'enabled': True,
417          'source': 'PlanetLabConf/RPM-GPG-KEY-fedora',
418          'dest': '/etc/pki/rpm-gpg/RPM-GPG-KEY-fedora',
419          'file_permissions': '644',
420          'file_owner': 'root',
421          'file_group': 'root',
422          'preinstall_cmd': '',
423          'postinstall_cmd': 'rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-fedora',
424          'error_cmd': '',
425          'ignore_cmd_errors': False,
426          'always_update': False},
427         {'enabled': True,
428          'source': 'PlanetLabConf/get_gpg_key.php',
429          'dest': '/etc/pki/rpm-gpg/RPM-GPG-KEY-planetlab',
430          'file_permissions': '644',
431          'file_owner': 'root',
432          'file_group': 'root',
433          'preinstall_cmd': '',
434          'postinstall_cmd': 'rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-planetlab',
435          'error_cmd': '',
436          'ignore_cmd_errors': False,
437          'always_update': False},
438
439         # Ping of death configuration
440         {'enabled': True,
441          'source': 'PlanetLabConf/ipod.conf.php',
442          'dest': '/etc/ipod.conf',
443          'file_permissions': '644',
444          'file_owner': 'root',
445          'file_group': 'root',
446          'preinstall_cmd': '',
447          'postinstall_cmd': '',
448          'error_cmd': '',
449          'ignore_cmd_errors': False,
450          'always_update': False},
451
452         # sudo configuration
453         {'enabled': True,
454          'source': 'PlanetLabConf/sudoers',
455          'dest': '/etc/sudoers',
456          'file_permissions': '440',
457          'file_owner': 'root',
458          'file_group': 'root',
459          'preinstall_cmd': '',
460          'postinstall_cmd': '/usr/sbin/visudo -c',
461          'error_cmd': '',
462          'ignore_cmd_errors': False,
463          'always_update': False}
464         ]
465
466     # Get list of existing (enabled, global) files
467     conf_files = GetConfFiles()
468     conf_files = filter(lambda conf_file: conf_file['enabled'] and \
469                                           not conf_file['node_ids'] and \
470                                           not conf_file['nodegroup_ids'],
471                         conf_files)
472     dests = [conf_file['dest'] for conf_file in conf_files]
473     conf_files = dict(zip(dests, conf_files))
474
475     # Create/update default PlanetLabConf entries
476     for default_conf_file in default_conf_files:
477         if default_conf_file['dest'] not in dests:
478             AddConfFile(default_conf_file)
479         else:
480             conf_file = conf_files[default_conf_file['dest']]
481             UpdateConfFile(conf_file['conf_file_id'], default_conf_file)
482
483     # Setup default slice attribute types
484     default_attribute_types = [
485         # Slice type (only vserver is supported)
486         {'name': "type",
487          'description': "Type of slice (e.g. vserver)",
488          'min_role_id': 20},
489
490         # System slice
491         {'name': "system",
492          'description': "Is a default system slice (1) or not (0 or unset)",
493          'min_role_id': 10},
494
495         # Slice enabled (1) or suspended (0)
496         {'name': "enabled",
497          'description': "Slice enabled (1 or unset) or suspended (0)",
498          'min_role_id': 10},
499
500         # Slice reference image
501         {'name': "vref",
502          'description': "Reference image",
503          'min_role_id': 30},
504
505         # Slice initialization script
506         {'name': "initscript",
507          'description': "Slice initialization script",
508          'min_role_id': 10},
509
510         # CPU share
511         {'name': "cpu_min",
512          'description': "Minimum CPU share (ms/s)",
513          'min_role_id': 10},
514         {'name': "cpu_share",
515          'description': "Number of CPU shares",
516          'min_role_id': 10},
517
518         # Bandwidth limits
519         {'name': "net_min",
520          'description': "Minimum bandwidth (bps)",
521          'min_role_id': 10},
522         {'name': "net_max",
523          'description': "Maximum bandwidth (bps)",
524          'min_role_id': 10},
525         {'name': "net_avg",
526          'description': "Average bandwidth (bps)",
527          'min_role_id': 10},
528         {'name': "net_share",
529          'description': "Number of bandwidth shares",
530          'min_role_id': 10},
531         {'name': "net2_min",
532          'description': "Minimum bandwidth over routes exempt from node bandwidth limits (bps)",
533          'min_role_id': 10},
534         {'name': "net2_max",
535          'description': "Maximum bandwidth over routes exempt from node bandwidth limits (bps)",
536          'min_role_id': 10},
537         {'name': "net2_avg",
538          'description': "Average bandwidth over routes exempt from node bandwidth limits (bps)",
539          'min_role_id': 10},
540         {'name': "net2_share",
541          'description': "Number of bandwidth shares over routes exempt from node bandwidth limits",
542          'min_role_id': 10},
543
544         # Disk quota
545         {'name': "disk_max",
546          'description': "Disk quota (1k disk blocks)",
547          'min_role_id': 10},
548
549         # Proper operations
550         {'name': "proper_op",
551          'description': "Proper operation (e.g. bind_socket)",
552          'min_role_id': 10},
553
554         # XXX Required for old Node Manager
555         # Special attributes applicable to Slice Creation Service (pl_conf) slice
556         {'name': "plc_slice_type",
557          'description': "Type of slice rspec to be created",
558          'min_role_id': 20},
559         {'name': "plc_agent_version",
560          'description': "Version of PLC agent (slice creation service) software to be deployed",
561          'min_role_id': 10},
562         {'name': "plc_ticket_pubkey",
563          'description': "Public key used to verify PLC-signed tickets",
564          'min_role_id': 10}
565         ]
566
567     # Get list of existing attribute types
568     attribute_types = GetSliceAttributeTypes()
569     attribute_types = [attribute_type['name'] for attribute_type in attribute_types]
570
571     # Create/update default slice attribute types
572     for default_attribute_type in default_attribute_types:
573         if default_attribute_type['name'] not in attribute_types:
574             AddSliceAttributeType(default_attribute_type)
575         else:
576             UpdateSliceAttributeType(default_attribute_type['name'], default_attribute_type)
577
578     # Get contents of SSL public certificate used for signing slice tickets
579     try:
580         plc_ticket_pubkey = ""
581         for line in file(plc_ma_sa['ca_ssl_key_pub']):
582             # Skip comments
583             if line[0:5] != "-----":
584                 # XXX The embedded newlines matter, do not strip()!
585                 plc_ticket_pubkey += line
586     except:
587         plc_ticket_pubkey = '%KEY%'
588
589     # Create/update system slices
590     legacy_slices = [
591         # XXX Required for old Node Manager
592         {'name': "pl_conf",
593          'description': "PlanetLab Slice Creation Service (SCS)",
594          'url': url,
595          'instantiation': "plc-instantiated",
596          # Renew forever
597          'expires': sys.maxint,
598          'attributes': [('plc_slice_type', "VServerSlice"),
599                         ('plc_agent_version', "1.0"),
600                         ('plc_ticket_pubkey', plc_ticket_pubkey)]},
601
602         # XXX Required for old Node Manager
603         {'name': "pl_conf_vserverslice",
604          'description': "Default attributes for vserver slices",
605          'url': url,
606          'instantiation': "plc-instantiated",
607          # Renew forever
608          'expires': sys.maxint,
609          'attributes': [('cpu_share', "32"),
610                         ('plc_slice_type', "VServerSlice"),
611                         ('disk_max', "5000000")]},
612         ]
613     default_slices = [
614          # PlanetFlow
615         {'name': plc['slice_prefix'] + "_netflow",
616          'description': "PlanetFlow Traffic Auditing Service",
617          'url': url,
618          'instantiation': "plc-instantiated",
619          # Renew forever
620          'expires': sys.maxint,
621          'attributes': [('system', "1"),
622                         ('vref', "planetflow"),
623                         ('proper_op', "open file=/etc/passwd, flags=r"),
624                         ('proper_op', "create_socket"),
625                         ('proper_op', "bind_socket")]},
626         ]
627          
628     ### xxx - to review once new node manager rolls out
629     # if PLC_SLICE_PREFIX is left to default - this is meant for the public PL only
630     if plc['slice_prefix'] == 'pl':
631         # create both legacy slices together with netflow through default_slices
632         default_slices += legacy_slices
633     else:
634         # we use another slice prefix : disable legacy slices if already created
635         for legacy_slice in legacy_slices:
636             try:
637                 DeleteSlice(legacy_slice['name'])
638             except:
639                 pass
640     
641     for default_slice in default_slices:
642         slices = GetSlices([default_slice['name']])
643         if slices:
644             slice = slices[0]
645             UpdateSlice(slice['slice_id'], default_slice)
646         else:
647             AddSlice(default_slice)
648             slice = GetSlices([default_slice['name']])[0]
649
650         # Create/update all attributes
651         slice_attributes = []
652         if slice['slice_attribute_ids']:
653             # Delete unknown attributes
654             for slice_attribute in GetSliceAttributes(slice['slice_attribute_ids']):
655                 if (slice_attribute['name'], slice_attribute['value']) \
656                    not in default_slice['attributes']:
657                     DeleteSliceAttribute(slice_attribute['slice_attribute_id'])
658                 else:
659                     slice_attributes.append((slice_attribute['name'], slice_attribute['value']))
660
661         for (name, value) in default_slice['attributes']:
662             if (name, value) not in slice_attributes:
663                 AddSliceAttribute(slice['name'], name, value)
664
665 if __name__ == '__main__':
666     main()
667
668 # Local variables:
669 # tab-width: 4
670 # mode: python
671 # End: