bootstrap: Add OpenSUSE 15.1 image
[samba.git] / bootstrap / config.py
1 #!/usr/bin/env python3
2
3 # Copyright (C) Catalyst.Net Ltd 2019
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18 """
19 Manage dependencies and bootstrap environments for Samba.
20
21 Config file for packages and templates.
22
23 Author: Joe Guo <joeg@catalyst.net.nz>
24 """
25 import os
26 from os.path import abspath, dirname, join
27 HERE = abspath(dirname(__file__))
28 # output dir for rendered files
29 OUT = join(HERE, 'generated-dists')
30
31
32 # pkgs with same name in all packaging systems
33 COMMON = [
34     'acl',
35     'attr',
36     'autoconf',
37     'binutils',
38     'bison',
39     'curl',
40     'flex',
41     'gcc',
42     'gdb',
43     'git',
44     'gzip',
45     'hostname',
46     'htop',
47     'lcov',
48     'make',
49     'patch',
50     'perl',
51     'psmisc',  # for pstree in test
52     'rng-tools',
53     'rsync',
54     'sed',
55     'sudo',  # docker images has no sudo by default
56     'tar',
57     'tree',
58 ]
59
60
61 # define pkgs for all packaging systems in parallel
62 # make it easier to find missing ones
63 # use latest ubuntu and fedora as defaults
64 # deb, rpm, ...
65 PKGS = [
66     # NAME1-dev, NAME2-devel
67     ('lmdb-utils', 'lmdb'),
68     ('mingw-w64', 'mingw64-gcc'),
69     ('nettle-dev', 'nettle-devel'),
70     ('zlib1g-dev', 'zlib-devel'),
71     ('libbsd-dev', 'libbsd-devel'),
72     ('libaio-dev', 'libaio-devel'),
73     ('libarchive-dev', 'libarchive-devel'),
74     ('libblkid-dev', 'libblkid-devel'),
75     ('libcap-dev', 'libcap-devel'),
76     ('libacl1-dev', 'libacl-devel'),
77     ('libattr1-dev', 'libattr-devel'),
78
79     # libNAME1-dev, NAME2-devel
80     ('libpopt-dev', 'popt-devel'),
81     ('libreadline-dev', 'readline-devel'),
82     ('libjansson-dev', 'jansson-devel'),
83     ('liblmdb-dev', 'lmdb-devel'),
84     ('libncurses5-dev', 'ncurses-devel'),
85     # NOTE: Debian 7+ or Ubuntu 16.04+
86     ('libsystemd-dev', 'systemd-devel'),
87     ('libkrb5-dev', 'krb5-devel'),
88     ('libldap2-dev', 'openldap-devel'),
89     ('libcups2-dev', 'cups-devel'),
90     ('libpam0g-dev', 'pam-devel'),
91     ('libgpgme11-dev', 'gpgme-devel'),
92     # NOTE: Debian 8+ and Ubuntu 14.04+
93     ('libgnutls28-dev', 'gnutls-devel'),
94     ('libtasn1-bin', 'libtasn1-tools'),
95     ('libtasn1-dev', 'libtasn1-devel'),
96     ('', 'quota-devel'),
97     ('uuid-dev', 'libuuid-devel'),
98     ('libjs-jquery', ''),
99     ('libavahi-common-dev', 'avahi-devel'),
100     ('libdbus-1-dev', 'dbus-devel'),
101     ('libpcap-dev', 'libpcap-devel'),
102     ('libunwind-dev', 'libunwind-devel'),  # for back trace
103     ('libglib2.0-dev', 'glib2-devel'),
104     ('libicu-dev', 'libicu-devel'),
105
106     # NAME1, NAME2
107     # for debian, locales provide locale support with language packs
108     # ubuntu split language packs to language-pack-xx
109     # for centos, glibc-common provide locale support with language packs
110     # fedora split language packs  to glibc-langpack-xx
111     ('locales', 'glibc-common'),  # required for locale
112     ('language-pack-en', 'glibc-langpack-en'),  # we need en_US.UTF-8
113     ('bind9utils', 'bind-utils'),
114     ('dnsutils', ''),
115     ('xsltproc', 'libxslt'),
116     ('krb5-user', ''),
117     ('krb5-config', ''),
118     ('krb5-kdc', 'krb5-server'),
119     ('apt-utils', 'yum-utils'),
120     ('pkg-config', 'pkgconfig'),
121     ('procps', 'procps-ng'),  # required for the free cmd in tests
122     ('lsb-release', 'lsb-release'),  # we need lsb_relase to show info
123     ('', 'rpcgen'),  # required for test
124     # refer: https://fedoraproject.org/wiki/Changes/SunRPCRemoval
125     ('', 'libtirpc-devel'),  # for <rpc/rpc.h> header on fedora
126     ('', 'libnsl2-devel'),  # for <rpcsvc/yp_prot.h> header on fedora
127     ('', 'rpcsvc-proto-devel'), # for <rpcsvc/rquota.h> header
128     ('mawk', 'gawk'),
129
130     # python
131     ('python-dev', 'python-devel'),
132     ('python-dbg', ''),
133     ('python-iso8601', ''),
134     ('python-gpg', 'python2-gpg'),  # defaults to ubuntu/fedora latest
135     ('python-crypto', 'python-crypto'),
136     ('python-markdown', 'python-markdown'),
137     ('python-dnspython', 'python-dns'),
138     ('python-pexpect', ''),  # for wintest only
139
140     ('python3', 'python3'),
141     ('python3-dev', 'python3-devel'),
142     ('python3-dbg', ''),
143     ('python3-iso8601', ''),
144     ('python3-gpg', 'python3-gpg'),  # defaults to ubuntu/fedora latest
145     ('python3-crypto', 'python3-crypto'),
146     ('python3-markdown', 'python3-markdown'),
147     ('python3-matplotlib', ''),
148     ('python3-dnspython', 'python3-dns'),
149     ('python3-pexpect', ''),  # for wintest only
150
151     ('', 'libsemanage-python'),
152     ('', 'policycoreutils-python'),
153
154     # perl
155     ('libparse-yapp-perl', 'perl-Parse-Yapp'),
156     ('libjson-perl', 'perl-JSON-Parse'),
157     ('perl-modules', ''),
158     ('', 'perl-Archive-Tar'),
159     ('', 'perl-ExtUtils-MakeMaker'),
160     ('', 'perl-Test-Base'),
161     ('', 'perl-generators'),
162     ('', 'perl-interpreter'),
163
164     # fs
165     ('xfslibs-dev', 'xfsprogs-devel'), # for xfs quota support
166     ('', 'glusterfs-api-devel'),
167     ('glusterfs-common', 'glusterfs-devel'),
168     ('libcephfs-dev', 'libcephfs-devel'),
169
170     # misc
171     # @ means group for rpm, use fedora as rpm default
172     ('build-essential', '@development-tools'),
173     ('debhelper', ''),
174     # rpm has no pkg for docbook-xml
175     ('docbook-xml', 'docbook-dtds'),
176     ('docbook-xsl', 'docbook-style-xsl'),
177     ('', 'keyutils-libs-devel'),
178     ('', 'which'),
179 ]
180
181
182 DEB_PKGS = COMMON + [pkg for pkg, _ in PKGS if pkg]
183 RPM_PKGS = COMMON + [pkg for _, pkg in PKGS if pkg]
184
185 GENERATED_MARKER = r"""
186 #
187 # This file is generated by 'bootstrap/template.py --render'
188 # See also bootstrap/config.py
189 #
190 """
191
192
193 APT_BOOTSTRAP = r"""
194 #!/bin/bash
195 {GENERATED_MARKER}
196 set -xueo pipefail
197
198 export DEBIAN_FRONTEND=noninteractive
199 apt-get -y update
200
201 apt-get -y install \
202     {pkgs}
203
204 apt-get -y autoremove
205 apt-get -y autoclean
206 apt-get -y clean
207 """
208
209
210 YUM_BOOTSTRAP = r"""
211 #!/bin/bash
212 {GENERATED_MARKER}
213 set -xueo pipefail
214
215 yum update -y
216 yum install -y epel-release
217 yum update -y
218
219 yum install -y \
220     {pkgs}
221
222 yum clean all
223
224 if [ ! -f /usr/bin/python3 ]; then
225     ln -sf /usr/bin/python3.6 /usr/bin/python3
226 fi
227 """
228
229
230 DNF_BOOTSTRAP = r"""
231 #!/bin/bash
232 {GENERATED_MARKER}
233 set -xueo pipefail
234
235 dnf update -y
236
237 dnf install -y \
238     --setopt=install_weak_deps=False \
239     {pkgs}
240
241 dnf clean all
242 """
243
244 ZYPPER_BOOTSTRAP = r"""
245 #!/bin/bash
246 {GENERATED_MARKER}
247 set -xueo pipefail
248
249 zypper --non-interactive refresh
250 zypper --non-interactive update
251 zypper --non-interactive install \
252     --no-recommends \
253     system-user-nobody \
254     {pkgs}
255
256 zypper --non-interactive clean
257
258 if [ -f /usr/lib/mit/bin/krb5-config ]; then
259     ln -sf /usr/lib/mit/bin/krb5-config /usr/bin/krb5-config
260 fi
261 """
262
263 # A generic shell script to setup locale
264 LOCALE_SETUP = r"""
265 #!/bin/bash
266 {GENERATED_MARKER}
267 set -xueo pipefail
268
269 # refer to /usr/share/i18n/locales
270 INPUTFILE=en_US
271 # refer to /usr/share/i18n/charmaps
272 CHARMAP=UTF-8
273 # locale to generate in /usr/lib/locale
274 # glibc/localedef will normalize UTF-8 to utf8, follow the naming style
275 LOCALE=$INPUTFILE.utf8
276
277 # if locale is already correct, exit
278 ( locale | grep LC_ALL | grep -i $LOCALE ) && exit 0
279
280 # if locale not available, generate locale into /usr/lib/locale
281 if ! ( locale --all-locales | grep -i $LOCALE )
282 then
283     # no-archive means create its own dir
284     localedef --inputfile $INPUTFILE --charmap $CHARMAP --no-archive $LOCALE
285 fi
286
287 # update locale conf and global env file
288 # set both LC_ALL and LANG for safe
289
290 # update conf for Debian family
291 FILE=/etc/default/locale
292 if [ -f $FILE ]
293 then
294     echo LC_ALL="$LOCALE" > $FILE
295     echo LANG="$LOCALE" >> $FILE
296 fi
297
298 # update conf for RedHat family
299 FILE=/etc/locale.conf
300 if [ -f $FILE ]
301 then
302     # LC_ALL is not valid in this file, set LANG only
303     echo LANG="$LOCALE" > $FILE
304 fi
305
306 # update global env file
307 FILE=/etc/environment
308 if [ -f $FILE ]
309 then
310     # append LC_ALL if not exist
311     grep LC_ALL $FILE || echo LC_ALL="$LOCALE" >> $FILE
312     # append LANG if not exist
313     grep LANG $FILE || echo LANG="$LOCALE" >> $FILE
314 fi
315 """
316
317
318 DOCKERFILE = r"""
319 {GENERATED_MARKER}
320 FROM {docker_image}
321
322 # pass in with --build-arg while build
323 ARG SHA1SUM
324 RUN [ -n $SHA1SUM ] && echo $SHA1SUM > /sha1sum.txt
325
326 ADD *.sh /tmp/
327 # need root permission, do it before USER samba
328 RUN /tmp/bootstrap.sh && /tmp/locale.sh
329
330 # if ld.gold exists, force link it to ld
331 RUN set -x; LD=$(which ld); LD_GOLD=$(which ld.gold); test -x $LD_GOLD && ln -sf $LD_GOLD $LD && test -x $LD && echo "$LD is now $LD_GOLD"
332
333 # make test can not work with root, so we have to create a new user
334 RUN useradd -m -U -s /bin/bash samba && \
335     mkdir -p /etc/sudoers.d && \
336     echo "samba ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/samba
337
338 USER samba
339 WORKDIR /home/samba
340 # samba tests rely on this
341 ENV USER=samba LC_ALL=en_US.utf8 LANG=en_US.utf8
342 """
343
344 # Vagrantfile snippet for each dist
345 VAGRANTFILE_SNIPPET = r"""
346     config.vm.define "{name}" do |v|
347         v.vm.box = "{vagrant_box}"
348         v.vm.hostname = "{name}"
349         v.vm.provision :shell, path: "{name}/bootstrap.sh"
350         v.vm.provision :shell, path: "{name}/locale.sh"
351     end
352 """
353
354 # global Vagrantfile with snippets for all dists
355 VAGRANTFILE_GLOBAL = r"""
356 {GENERATED_MARKER}
357
358 Vagrant.configure("2") do |config|
359     config.ssh.insert_key = false
360
361 {vagrantfile_snippets}
362
363 end
364 """
365
366
367 DEB_DISTS = {
368     'debian7': {
369         'docker_image': 'debian:7',
370         'vagrant_box': 'debian/wheezy64',
371         'replace': {
372             'libgnutls28-dev': 'libgnutls-dev',
373             'libsystemd-dev': '',  # not available, remove
374             'lmdb-utils': '',  # not available, remove
375             'liblmdb-dev': '',  # not available, remove
376             'python-gpg': 'python-gpgme',
377             'python3-gpg': '',  # no python3 gpg pkg available, remove
378             'language-pack-en': '',   # included in locales
379         }
380     },
381     'debian8': {
382         'docker_image': 'debian:8',
383         'vagrant_box': 'debian/jessie64',
384         'replace': {
385             'python-gpg': 'python-gpgme',
386             'python3-gpg': 'python3-gpgme',
387             'language-pack-en': '',   # included in locales
388         }
389     },
390     'debian9': {
391         'docker_image': 'debian:9',
392         'vagrant_box': 'debian/stretch64',
393         'replace': {
394             'language-pack-en': '',   # included in locales
395         }
396     },
397     'ubuntu1404': {
398         'docker_image': 'ubuntu:14.04',
399         'vagrant_box': 'ubuntu/trusty64',
400         'replace': {
401             'libsystemd-dev': '',  # remove
402             'libgnutls28-dev': 'libgnutls-dev',
403             'python-gpg': 'python-gpgme',
404             'python3-gpg': 'python3-gpgme',
405             'lmdb-utils': 'lmdb-utils/trusty-backports',
406             'liblmdb-dev': 'liblmdb-dev/trusty-backports',
407             'libunwind-dev': 'libunwind8-dev',
408             'glusterfs-common': '',
409             'libcephfs-dev': '',
410         }
411     },
412     'ubuntu1604': {
413         'docker_image': 'ubuntu:16.04',
414         'vagrant_box': 'ubuntu/xenial64',
415         'replace': {
416             'python-gpg': 'python-gpgme',
417             'python3-gpg': 'python3-gpgme',
418             'glusterfs-common': '',
419             'libcephfs-dev': '',
420         }
421     },
422     'ubuntu1804': {
423         'docker_image': 'ubuntu:18.04',
424         'vagrant_box': 'ubuntu/bionic64',
425     },
426 }
427
428
429 RPM_DISTS = {
430     'centos6': {
431         'docker_image': 'centos:6',
432         'vagrant_box': 'centos/6',
433         'bootstrap': YUM_BOOTSTRAP,
434         'replace': {
435             'lsb-release': 'redhat-lsb',
436             'python3': 'python36',
437             'python3-devel': 'python36-devel',
438             'python2-gpg': 'pygpgme',
439             'python3-gpg': '',  # no python3-gpg yet
440             '@development-tools': '"@Development Tools"',  # add quotes
441             'glibc-langpack-en': '',  # included in glibc-common
442             'glibc-locale-source': '',  # included in glibc-common
443             'procps-ng': 'procps',  # centos6 still use old name
444             # update perl core modules on centos
445             # fix: Can't locate Archive/Tar.pm in @INC
446             'perl': 'perl-core',
447             'rpcsvc-proto-devel': '',
448             'glusterfs-api-devel': '',
449             'glusterfs-devel': '',
450             'libcephfs-devel': '',
451         }
452     },
453     'centos7': {
454         'docker_image': 'centos:7',
455         'vagrant_box': 'centos/7',
456         'bootstrap': YUM_BOOTSTRAP,
457         'replace': {
458             'lsb-release': 'redhat-lsb',
459             'python3': 'python36',
460             'python3-crypto': 'python36-crypto',
461             'python3-devel': 'python36-devel',
462             'python3-dns': 'python36-dns',
463             'python3-gpg': 'python36-gpg',
464             'python3-iso8601' : 'python36-iso8601',
465             'python3-markdown': 'python36-markdown',
466             # although python36-devel is available
467             # after epel-release installed
468             # however, all other python3 pkgs are still python36-ish
469             'python2-gpg': 'pygpgme',
470             'python3-gpg': '',  # no python3-gpg yet
471             '@development-tools': '"@Development Tools"',  # add quotes
472             'glibc-langpack-en': '',  # included in glibc-common
473             'glibc-locale-source': '',  # included in glibc-common
474             # update perl core modules on centos
475             # fix: Can't locate Archive/Tar.pm in @INC
476             'perl': 'perl-core',
477             'rpcsvc-proto-devel': '',
478             'glusterfs-api-devel': '',
479             'glusterfs-devel': '',
480             'libcephfs-devel': '',
481         }
482     },
483     'fedora28': {
484         'docker_image': 'fedora:28',
485         'vagrant_box': 'fedora/28-cloud-base',
486         'bootstrap': DNF_BOOTSTRAP,
487         'replace': {
488             'lsb-release': 'redhat-lsb',
489         }
490     },
491     'fedora29': {
492         'docker_image': 'fedora:29',
493         'vagrant_box': 'fedora/29-cloud-base',
494         'bootstrap': DNF_BOOTSTRAP,
495         'replace': {
496             'lsb-release': 'redhat-lsb',
497         }
498     },
499     'fedora30': {
500         'docker_image': 'fedora:30',
501         'vagrant_box': 'fedora/30-cloud-base',
502         'bootstrap': DNF_BOOTSTRAP,
503         'replace': {
504             'lsb-release': 'redhat-lsb',
505         }
506     },
507     'opensuse150': {
508         'docker_image': 'opensuse/leap:15.0',
509         'vagrant_box': 'opensuse/openSUSE-15.0-x86_64',
510         'bootstrap': ZYPPER_BOOTSTRAP,
511         'replace': {
512             '@development-tools': '',
513             'dbus-devel': 'dbus-1-devel',
514             'docbook-style-xsl': 'docbook-xsl-stylesheets',
515             'glibc-common': 'glibc-locale',
516             'glibc-locale-source': 'glibc-i18ndata',
517             'glibc-langpack-en': '',
518             'jansson-devel': 'libjansson-devel',
519             'keyutils-libs-devel': 'keyutils-devel',
520             'krb5-workstation': 'krb5-client',
521             'libnsl2-devel': 'libnsl-devel',
522             'libsemanage-python': 'python2-semanage',
523             'nettle-devel': 'libnettle-devel',
524             'openldap-devel': 'openldap2-devel',
525             'perl-Archive-Tar': 'perl-Archive-Tar-Wrapper',
526             'perl-JSON-Parse': 'perl-JSON-XS',
527             'perl-generators': '',
528             'perl-interpreter': '',
529             'procps-ng': 'procps',
530             'python-dns': 'python2-dnspython',
531             'python3-crypto': 'python3-pycrypto',
532             'python3-dns': 'python3-dnspython',
533             'python3-markdown': 'python3-Markdown',
534             'quota-devel': '',
535             'glusterfs-api-devel': '',
536             'libtasn1-tools': '', # asn1Parser is part of libtasn1
537             'mingw64-gcc': '', # doesn't exist
538         }
539     },
540     'opensuse151': {
541         'docker_image': 'opensuse/leap:15.1',
542         'vagrant_box': 'opensuse/openSUSE-15.1-x86_64',
543         'bootstrap': ZYPPER_BOOTSTRAP,
544         'replace': {
545             '@development-tools': '',
546             'dbus-devel': 'dbus-1-devel',
547             'docbook-style-xsl': 'docbook-xsl-stylesheets',
548             'glibc-common': 'glibc-locale',
549             'glibc-locale-source': 'glibc-i18ndata',
550             'glibc-langpack-en': '',
551             'jansson-devel': 'libjansson-devel',
552             'keyutils-libs-devel': 'keyutils-devel',
553             'krb5-workstation': 'krb5-client',
554             'libnsl2-devel': 'libnsl-devel',
555             'libsemanage-python': 'python2-semanage',
556             'nettle-devel': 'libnettle-devel',
557             'openldap-devel': 'openldap2-devel',
558             'perl-Archive-Tar': 'perl-Archive-Tar-Wrapper',
559             'perl-JSON-Parse': 'perl-JSON-XS',
560             'perl-generators': '',
561             'perl-interpreter': '',
562             'procps-ng': 'procps',
563             'python-dns': 'python2-dnspython',
564             'python3-crypto': 'python3-pycrypto',
565             'python3-dns': 'python3-dnspython',
566             'python3-markdown': 'python3-Markdown',
567             'quota-devel': '',
568             'glusterfs-api-devel': '',
569             'libtasn1-tools': '', # asn1Parser is part of libtasn1
570             'mingw64-gcc': '', # doesn't exist
571         }
572     }
573 }
574
575
576 DEB_FAMILY = {
577     'name': 'deb',
578     'pkgs': DEB_PKGS,
579     'bootstrap': APT_BOOTSTRAP,  # family default
580     'dists': DEB_DISTS,
581 }
582
583
584 RPM_FAMILY = {
585     'name': 'rpm',
586     'pkgs': RPM_PKGS,
587     'bootstrap': YUM_BOOTSTRAP,  # family default
588     'dists': RPM_DISTS,
589 }
590
591
592 YML_HEADER = r"""
593 ---
594 packages:
595 """
596
597
598 def expand_family_dists(family):
599     dists = {}
600     for name, config in family['dists'].items():
601         config = config.copy()
602         config['name'] = name
603         config['home'] = join(OUT, name)
604         config['family'] = family['name']
605         config['GENERATED_MARKER'] = GENERATED_MARKER
606
607         # replace dist specific pkgs
608         replace = config.get('replace', {})
609         pkgs = []
610         for pkg in family['pkgs']:
611             pkg = replace.get(pkg, pkg)  # replace if exists or get self
612             if pkg:
613                 pkgs.append(pkg)
614         pkgs.sort()
615
616         lines = ['  - {}'.format(pkg) for pkg in pkgs]
617         config['packages.yml'] = YML_HEADER.lstrip() + os.linesep.join(lines)
618
619         sep = ' \\' + os.linesep + '    '
620         config['pkgs'] = sep.join(pkgs)
621
622         # get dist bootstrap template or fall back to family default
623         bootstrap_template = config.get('bootstrap', family['bootstrap'])
624         config['bootstrap.sh'] = bootstrap_template.format(**config).strip()
625         config['locale.sh'] = LOCALE_SETUP.format(**config).strip()
626
627         config['Dockerfile'] = DOCKERFILE.format(**config).strip()
628         # keep the indent, no strip
629         config['vagrantfile_snippet'] = VAGRANTFILE_SNIPPET.format(**config)
630
631         dists[name] = config
632     return dists
633
634
635 # expanded config for dists
636 DEB_DISTS_EXP = expand_family_dists(DEB_FAMILY)
637 RPM_DISTS_EXP = expand_family_dists(RPM_FAMILY)
638
639 # assemble all together
640 DISTS = {}
641 DISTS.update(DEB_DISTS_EXP)
642 DISTS.update(RPM_DISTS_EXP)
643
644
645 def render_vagrantfile(dists):
646     """
647     Render all snippets for each dist into global Vagrantfile.
648
649     Vagrant supports multiple vms in one Vagrantfile.
650     This make it easier to manage the fleet, e.g:
651
652     start all: vagrant up
653     start one: vagrant up ubuntu1804
654
655     All other commands apply to above syntax, e.g.: status, destroy, provision
656     """
657     # sort dists by name and put all vagrantfile snippets together
658     snippets = [
659         dists[dist]['vagrantfile_snippet']
660         for dist in sorted(dists.keys())]
661
662     return VAGRANTFILE_GLOBAL.format(
663             vagrantfile_snippets=''.join(snippets),
664             GENERATED_MARKER=GENERATED_MARKER
665             )
666
667
668 VAGRANTFILE = render_vagrantfile(DISTS)
669
670
671 # data we need to expose
672 __all__ = ['DISTS', 'VAGRANTFILE', 'OUT']