~townsend/unity8-preview-lxc/fix-setup

« back to all changes in this revision

Viewing changes to bin/unity8-lxc-setup

  • Committer: Chris Townsend
  • Date: 2015-10-22 12:49:51 UTC
  • Revision ID: christopher.townsend@canonical.com-20151022124951-mqnjg81sqjwx4x79
Rework the creation of the unity8-lxc container since the Ubuntu Desktop Next ISO has been deprecated.
Many fixes for working with the systemd based containers.

Show diffs side-by-side

added added

removed removed

Lines of Context:
30
30
from distro_info import UbuntuDistroInfo, DistroDataOutdated
31
31
 
32
32
CONTAINER_NAME = "unity8-lxc"
33
 
ISO_URL = "http://cdimage.ubuntu.com/ubuntu-desktop-next/daily-live/" \
34
 
          "current/RELEASE-desktop-ARCH.iso.zsync"
35
33
SKIP_JOB = ["bluetooth", "lightdm", "plymouth", "plymouth-upstart-bridge",
36
34
            "plymouth-shutdown", "ubiquity"]
37
35
 
40
38
parser = argparse.ArgumentParser(description="Unity8 inside LXC")
41
39
parser.add_argument(
42
40
    "--rebuild-all", action="store_true",
43
 
    help=("Wipe and replace the container rootfs and configuration."))
44
 
parser.add_argument(
45
 
    "--rebuild-config", action="store_true",
46
 
    help=("Wipe and regenerate the container configuration."))
 
41
    help=("Wipe and replace the container rootfs."))
47
42
parser.add_argument(
48
43
    "--rebuild-rootfs", action="store_true",
49
44
    help=("Wipe and replace the container rootfs."))
50
45
parser.add_argument(
51
 
    "--redownload", action="store_true",
52
 
    help=("Re-download the ISO image instead of using the local version."))
53
 
parser.add_argument(
54
46
    "--test", type=int, metavar="SECONDS", default=None,
55
47
    help=("Test mode, argument is time in seconds for the session."))
56
48
parser.add_argument(
68
60
container = lxc.Container(CONTAINER_NAME)
69
61
container_path = "%s/%s" % (lxc.default_config_path, CONTAINER_NAME)
70
62
rootfs_path = "%s/rootfs" % container_path
71
 
iso_filename = "%s/ubuntu-next.iso" % container_path
72
 
iso_path = "%s/iso" % container_path
73
 
squashfs_path = "%s/squashfs" % container_path
74
63
 
75
64
(distro, version, codename) = platform.linux_distribution()
76
65
 
77
 
if not os.path.exists(iso_filename):
78
 
    args.redownload = True
79
 
 
80
66
# Actions
81
 
generate_config = True
82
67
generate_rootfs = True
83
68
 
84
69
def start_container(wait_for_network=True):
100
85
 
101
86
# Deal with existing containers
102
87
if container.defined:
103
 
    generate_config = False
104
88
    generate_rootfs = False
105
89
 
106
90
    if not args.test and container.running:
108
92
        container.stop()
109
93
 
110
94
    if args.rebuild_all:
111
 
        args.rebuild_config = True
112
95
        args.rebuild_rootfs = True
113
96
 
114
 
    if args.rebuild_config:
115
 
        generate_config = True
116
 
        container.clear_config()
117
 
 
118
97
    if args.rebuild_rootfs:
 
98
        print("Deleting current unity8-lxc container...")
119
99
        generate_rootfs = True
120
 
        if os.path.exists(rootfs_path):
121
 
            shutil.rmtree(rootfs_path)
 
100
        container.destroy()
 
101
        del container
 
102
        container = lxc.Container(CONTAINER_NAME)
 
103
        #if os.path.exists(container_path):
 
104
        #    shutil.rmtree(container_path)
122
105
 
123
 
if not generate_config and not generate_rootfs and not args.test and not args.update_lxc:
 
106
if not generate_rootfs and not args.test and not args.update_lxc:
124
107
    parser.error("The container already exists.")
125
108
 
126
 
 
127
 
if generate_config:
128
 
    # Setup the container config
129
 
    ## Load the default LXC config keys
130
 
    container.load_config("/usr/share/lxc/config/ubuntu.common.conf")
131
 
 
132
 
    ## Setup the network ##
133
 
    container.append_config_item("lxc.network.type", "veth")
134
 
    container.append_config_item("lxc.network.link", "lxcbr0")
135
 
    container.append_config_item("lxc.network.hwaddr", "00:16:3e:xx:xx:xx")
136
 
 
137
 
    ## Architecture
138
 
    container.set_config_item("lxc.arch", "x86_64")
139
 
 
140
 
    ## Rootfs path
141
 
    container.set_config_item("lxc.rootfs", rootfs_path)
142
 
 
143
 
    ## Hostname
144
 
    container.set_config_item("lxc.utsname", CONTAINER_NAME)
145
 
 
146
 
    ## Devices
147
 
    ### /dev/tty*
148
 
    container.append_config_item("lxc.cgroup.devices.allow", "c 4:* rwm")
149
 
 
150
 
    ### /dev/input/*
151
 
    container.append_config_item("lxc.cgroup.devices.allow", "c 13:* rwm")
152
 
    container.append_config_item("lxc.mount.entry",
153
 
                                 "/dev/input dev/input none bind,create=dir")
154
 
 
155
 
    ### /dev/dri/*
156
 
    container.append_config_item("lxc.cgroup.devices.allow", "c 226:* rwm")
157
 
    container.append_config_item("lxc.mount.entry",
158
 
                                 "/dev/dri dev/dri none bind,create=dir")
159
 
 
160
 
    ### /dev/snd/*
161
 
    container.append_config_item("lxc.cgroup.devices.allow", "c 116:* rwm")
162
 
    container.append_config_item("lxc.mount.entry",
163
 
                                 "/dev/snd dev/snd none bind,create=dir")
164
 
 
165
 
    ## Enable cgmanager access
166
 
    container.set_config_item("lxc.mount.auto", "cgroup:mixed proc:mixed sys:mixed")
167
 
 
168
 
    ## Allow nested containers
169
 
    container.set_config_item("lxc.aa_profile", "lxc-container-default-with-nesting")
170
 
 
171
 
    ## Disable the lxc autodev
172
 
    container.append_config_item("lxc.autodev", "0")
173
 
 
174
 
    # Setup the /home bind-mount
175
 
    container.append_config_item("lxc.mount.entry", "/home home none bind")
176
 
 
177
 
    # Setup the /etc/shadow bind-mount
178
 
    container.append_config_item("lxc.mount.entry",
179
 
                                 "/etc/shadow etc/shadow none bind")
180
 
 
181
 
    # Setup the local time and timezone
182
 
    container.append_config_item("lxc.mount.entry",
183
 
                                 "/etc/localtime etc/localtime none bind")
184
 
    container.append_config_item("lxc.mount.entry",
185
 
                                 "/etc/timezone etc/timezone none bind")
186
 
 
187
 
    # Setup up /run/udev
188
 
    container.append_config_item("lxc.mount.entry",
189
 
                                 "/run/udev var/lib/host-udev none bind,ro,create=dir")
190
 
 
191
 
    # Setup /run/systemd
192
 
    container.append_config_item("lxc.mount.entry",
193
 
                                 "/run/systemd var/lib/host-systemd none bind,ro,create=dir")
194
 
 
195
 
    ## Dump it all to disk
196
 
    container.save_config()
197
 
 
198
 
 
199
109
if generate_rootfs:
200
110
    # Setup the container rootfs
201
111
 
213
123
    except DistroDataOutdated:
214
124
        current_release = UbuntuDistroInfo().stable()
215
125
 
216
 
    ## Download the ISO image
217
 
    if args.redownload:
218
 
        iso_url = ISO_URL.replace("ARCH", architecture)
219
 
        iso_url = iso_url.replace("RELEASE", current_release)
220
 
 
221
 
        ### Use zsync to fetch the ISO
222
 
        cmd = "cd %s && zsync -i %s -o %s %s" % (container_path, iso_filename, iso_filename, iso_url)
223
 
        os.system(cmd)
224
 
 
225
 
        ### Remove the ISO backup since it's not needed
226
 
        if os.path.exists(iso_filename+".zs-old"):
227
 
            os.remove(iso_filename+".zs-old")
228
 
 
229
 
    if not os.path.exists(iso_filename):
230
 
        parser.error("No Unity 8 Desktop Next ISO exists!")
231
 
 
232
 
    ## Mount the ISO
233
 
    if not os.path.exists(iso_path):
234
 
        os.mkdir(iso_path)
235
 
    subprocess.call(["mount", "-o", "loop,ro", iso_filename, iso_path])
236
 
 
237
 
    if not os.path.exists(squashfs_path):
238
 
        os.mkdir(squashfs_path)
239
 
    subprocess.call(["mount", "-o", "loop,ro",
240
 
                     os.path.join(iso_path, "casper", "filesystem.squashfs"),
241
 
                     squashfs_path])
242
 
 
243
 
    ## Unpack the ISO
244
 
    print("Unpacking the ISO image...")
245
 
    subprocess.call(["rsync", "-aA", "--hard-links", "--numeric-ids",
246
 
                     "%s/" % squashfs_path, "%s/" % rootfs_path])
247
 
 
248
 
    ## Unmount the ISO
249
 
    subprocess.call(["umount", squashfs_path])
250
 
    os.rmdir(squashfs_path)
251
 
 
252
 
    subprocess.call(["umount", iso_path])
253
 
    os.rmdir(iso_path)
 
126
    print("Creating the new unity8-lxc container...")
 
127
    container.create("download", 0,
 
128
                     {"dist": "ubuntu",
 
129
                      "release": current_release,
 
130
                      "arch": architecture})
254
131
 
255
132
    ## Configure
256
133
    print("Configuring the Unity8 LXC...")
262
139
    ### Generate /etc/hosts
263
140
    with open(os.path.join(rootfs_path, "etc", "hosts"), "w+") as fd:
264
141
        fd.write("""127.0.0.1   localhost
265
 
127.0.1.1   unity8-mir
 
142
127.0.1.1   unity8-lxc
266
143
 
267
144
# The following lines are desirable for IPv6 capable hosts
268
145
::1     localhost ip6-localhost ip6-loopback
270
147
ff02::2 ip6-allrouters
271
148
""")
272
149
 
273
 
    ### Disable some upstart jobs
 
150
    # Generate the package install policy override
 
151
    with open(os.path.join(rootfs_path, 'usr', 'sbin', 'policy-rc.d'), 'w+') as fd:
 
152
        fd.write("""#!/bin/sh
 
153
while true; do
 
154
case \"$1\" in
 
155
  -*) shift ;;
 
156
  makedev) exit 0;;
 
157
  *)  exit 101;;
 
158
esac
 
159
done
 
160
""")
 
161
        fd.close()
 
162
 
 
163
    os.chmod(os.path.join(rootfs_path, 'usr', 'sbin', 'policy-rc.d'), 0o755)
 
164
 
 
165
    start_container()
 
166
 
 
167
    # Copy the ubuntu-desktop-next metapackage to the rootfs
 
168
    lxc_archive = os.path.join(rootfs_path, 'tmp', 'ubuntu-desktop-next.deb')
 
169
    glob_name = "ubuntu-desktop-next_*_%s.deb" % architecture
 
170
    metapackage = glob.glob(os.path.join('/', 'usr', 'share', 'unity8-lxc', glob_name))
 
171
    shutil.copy(metapackage[0], lxc_archive)
 
172
 
 
173
    # Remove the default ubuntu user in the container
 
174
    container.attach_wait(lxc.attach_run_command,
 
175
                          ["userdel", "-r", "ubuntu"])
 
176
 
 
177
    container.attach_wait(lxc.attach_run_command,
 
178
                          ["apt-get", "update"])
 
179
 
 
180
    container.attach_wait(lxc.attach_run_command,
 
181
                          ["dpkg", "-i", os.path.join('/', 'tmp', 'ubuntu-desktop-next.deb')])
 
182
    container.attach_wait(lxc.attach_run_command,
 
183
                          ["apt-get", "install", "-f", "-y"])
 
184
 
 
185
    container.attach_wait(lxc.attach_run_command,
 
186
                          ["systemctl", "disable", "lightdm.service"]) 
 
187
    container.stop()
 
188
 
 
189
    # Disable some upstart jobs
274
190
    for job in glob.glob("%s/etc/init/*.conf" % rootfs_path):
275
191
        if os.path.basename(job).rsplit(".conf", 1)[0] in SKIP_JOB:
276
192
            with open("%s.override" % job.rsplit(".conf", 1)[0], "w+") as fd:
277
193
                fd.write("manual")
278
194
 
279
 
    ### Create any missing devices
280
 
    for i in range(64):
281
 
        tty_path = os.path.join(rootfs_path, "dev", "tty%s" % i)
282
 
        if not os.path.exists(tty_path):
283
 
            os.mknod(tty_path, 0o620 | stat.S_IFCHR, device=os.makedev(4, i))
284
 
            os.chown(tty_path, 0, 5)
285
 
 
286
 
    ### Generate /run/udev
287
 
    with open(os.path.join(rootfs_path, "etc", "init", "udev-db.conf"), "w+") as fd:
288
 
        fd.write("""start on starting udev
289
 
pre-start script
290
 
    rm -Rf /run/udev
291
 
    cp -R /var/lib/host-udev /run/udev
292
 
end script
293
 
""")
294
 
 
295
 
    ### Switch back to upstart instead of systemd
296
 
    with open(os.path.join(rootfs_path, "etc", "X11", "default-display-manager"), "w+") as fd:
297
 
        fd.write("")
298
 
 
299
 
    os.chdir(container_path)
300
 
 
301
 
    files = glob.glob("%s/upstart-sysv_*.deb" % container_path)
302
 
 
303
 
    upstart_file = ""
304
 
 
305
 
    if files:
306
 
       upstart_file = files[0]
307
 
 
308
 
    if args.redownload or not os.path.exists(upstart_file):
309
 
        if os.path.exists(upstart_file):
310
 
            os.remove(upstart_file)
311
 
 
312
 
        rmadison_cmd = "rmadison -s %s upstart-sysv | cut -d '|' -f 2 | tr -d '[[:space:]]'" % current_release
313
 
        upstart_sysv_ver = subprocess.getoutput(rmadison_cmd)
314
 
        upstart_file = "upstart-sysv_%s_%s.deb" % (upstart_sysv_ver, architecture)
315
 
 
316
 
        dget_url = "http://archive.ubuntu.com/ubuntu/pool/main/u/upstart/%s" % upstart_file
317
 
        subprocess.call(["dget", dget_url])
318
 
 
319
 
    if args.rebuild_rootfs:
320
 
        rootfs_archive_path = os.path.join(rootfs_path, "var", "cache", "apt", "archives")
321
 
        shutil.copy(upstart_file, rootfs_archive_path)
322
 
 
323
 
    start_container(False)
324
 
 
325
 
    print("Generating locale...")
326
 
    r = os.popen("locale", "r").read().split("\n")
327
 
    locale = ["locale-gen"]
328
 
 
329
 
    for i in r:
330
 
        if i == "":
331
 
          break
332
 
        i = i.split('=')[1].replace("\"", "")
333
 
        if not i in locale and i != "":
334
 
            locale.append(i)
335
 
 
336
 
    container.attach_wait(lxc.attach_run_command, locale)
337
 
 
338
 
    print("Switching to upstart...")
339
 
    container.attach_wait(lxc.attach_run_command,
340
 
                          ["apt-get", "install", "upstart-sysv", "-y", "--no-download", "--ignore-missing"])
341
 
 
342
 
    container.stop()
 
195
    with open(os.path.join(container_path, 'config'), 'a') as fd:
 
196
        fd.write("lxc.include = " + os.path.join('/', 'usr', 'share', 'unity8-lxc', 'unity8-lxc.conf'))
 
197
        fd.close()
343
198
 
344
199
if args.test:
345
200
    # Start a test session