~unity8-desktop-session-team/unity8-preview-lxc/stable

« back to all changes in this revision

Viewing changes to bin/unity8-lxc-setup

  • Committer: CI Train Bot
  • Author(s): Chris Townsend
  • Date: 2015-10-27 18:50:10 UTC
  • mfrom: (61.2.1 unity8-preview-lxc.merge-devel)
  • Revision ID: ci-train-bot@canonical.com-20151027185010-5elr9c3ez1w4zif7
* Bump version to 1.0.5.
* Rework unity8-lxc-setup to download the latest Ubuntu release LXC and
  install a Unity 8 desktop session metapackage insteaf using the now
  deprecated Ubuntu Desktop Next ISO image. (LP: #1477720)
* Many fixes for systemd based containers.
* Changed some command line options to only have '--rebuild'.
* Bind mount the hosts locale inside the container. (LP: #1466035)
Approved by: Stephen M. Webb

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
import subprocess
26
26
import sys
27
27
import time
28
 
import platform
29
28
 
30
 
from distro_info import UbuntuDistroInfo, DistroDataOutdated
31
29
 
32
30
CONTAINER_NAME = "unity8-lxc"
33
 
ISO_URL = "http://cdimage.ubuntu.com/ubuntu-desktop-next/daily-live/" \
34
 
          "current/RELEASE-desktop-ARCH.iso.zsync"
35
31
SKIP_JOB = ["bluetooth", "lightdm", "plymouth", "plymouth-upstart-bridge",
36
32
            "plymouth-shutdown", "ubiquity"]
37
33
 
39
35
# Argument parsing
40
36
parser = argparse.ArgumentParser(description="Unity8 inside LXC")
41
37
parser.add_argument(
42
 
    "--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."))
47
 
parser.add_argument(
48
 
    "--rebuild-rootfs", action="store_true",
 
38
    "--rebuild", action="store_true",
49
39
    help=("Wipe and replace the container rootfs."))
50
40
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
41
    "--test", type=int, metavar="SECONDS", default=None,
55
42
    help=("Test mode, argument is time in seconds for the session."))
56
43
parser.add_argument(
68
55
container = lxc.Container(CONTAINER_NAME)
69
56
container_path = "%s/%s" % (lxc.default_config_path, CONTAINER_NAME)
70
57
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
 
 
75
 
(distro, version, codename) = platform.linux_distribution()
76
 
 
77
 
if not os.path.exists(iso_filename):
78
 
    args.redownload = True
79
58
 
80
59
# Actions
81
 
generate_config = True
82
60
generate_rootfs = True
83
61
 
 
62
 
84
63
def start_container(wait_for_network=True):
85
64
    if not container.running:
86
65
        print("Starting the container")
92
71
        print("Not able to connect to the network.")
93
72
        sys.exit(0)
94
73
 
 
74
 
 
75
def get_latest_lxc_image(arch):
 
76
    cmd = ['/usr/share/lxc/templates/lxc-download', '-d', 'ubuntu', '-a', arch, '-l']
 
77
 
 
78
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
 
79
    output = proc.communicate()[0]
 
80
 
 
81
    lxc_output = output.split(b'\n')
 
82
 
 
83
    for line in lxc_output:
 
84
        if line.decode().startswith('ubuntu'):
 
85
            last_line = line
 
86
 
 
87
    release = last_line.decode().split('\t')
 
88
 
 
89
    return release[1]
 
90
 
 
91
 
95
92
# Destroy a container entirely
96
93
if args.destroy and container.defined:
97
94
    container.stop()
100
97
 
101
98
# Deal with existing containers
102
99
if container.defined:
103
 
    generate_config = False
104
100
    generate_rootfs = False
105
101
 
106
102
    if not args.test and container.running:
107
103
        print("Stopping the existing container.")
108
104
        container.stop()
109
105
 
110
 
    if args.rebuild_all:
111
 
        args.rebuild_config = True
112
 
        args.rebuild_rootfs = True
113
 
 
114
 
    if args.rebuild_config:
115
 
        generate_config = True
116
 
        container.clear_config()
117
 
 
118
 
    if args.rebuild_rootfs:
 
106
    if args.rebuild:
 
107
        print("Deleting current unity8-lxc container...")
119
108
        generate_rootfs = True
120
 
        if os.path.exists(rootfs_path):
121
 
            shutil.rmtree(rootfs_path)
 
109
        container.destroy()
 
110
        del container
 
111
        container = lxc.Container(CONTAINER_NAME)
122
112
 
123
 
if not generate_config and not generate_rootfs and not args.test and not args.update_lxc:
 
113
if not generate_rootfs and not args.test and not args.update_lxc:
124
114
    parser.error("The container already exists.")
125
115
 
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
116
if generate_rootfs:
200
117
    # Setup the container rootfs
201
118
 
208
125
 
209
126
    architecture = dpkg.stdout.read().strip()
210
127
 
211
 
    try:
212
 
        current_release = UbuntuDistroInfo().devel()
213
 
    except DistroDataOutdated:
214
 
        current_release = UbuntuDistroInfo().stable()
215
 
 
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)
 
128
    print("Determining latest LXC image release...")
 
129
    current_release = get_latest_lxc_image(architecture)
 
130
 
 
131
    print("Creating the new unity8-lxc container...")
 
132
    container.create("download", 0,
 
133
                     {"dist": "ubuntu",
 
134
                      "release": current_release,
 
135
                      "arch": architecture})
254
136
 
255
137
    ## Configure
256
138
    print("Configuring the Unity8 LXC...")
262
144
    ### Generate /etc/hosts
263
145
    with open(os.path.join(rootfs_path, "etc", "hosts"), "w+") as fd:
264
146
        fd.write("""127.0.0.1   localhost
265
 
127.0.1.1   unity8-mir
 
147
127.0.1.1   unity8-lxc
266
148
 
267
149
# The following lines are desirable for IPv6 capable hosts
268
150
::1     localhost ip6-localhost ip6-loopback
270
152
ff02::2 ip6-allrouters
271
153
""")
272
154
 
273
 
    ### Disable some upstart jobs
 
155
    # Generate the package install policy override
 
156
    with open(os.path.join(rootfs_path, 'usr', 'sbin', 'policy-rc.d'), 'w+') as fd:
 
157
        fd.write("""#!/bin/sh
 
158
while true; do
 
159
case \"$1\" in
 
160
  -*) shift ;;
 
161
  makedev) exit 0;;
 
162
  *)  exit 101;;
 
163
esac
 
164
done
 
165
""")
 
166
        fd.close()
 
167
 
 
168
    os.chmod(os.path.join(rootfs_path, 'usr', 'sbin', 'policy-rc.d'), 0o755)
 
169
 
 
170
    start_container()
 
171
 
 
172
    # Remove the default ubuntu user in the container
 
173
    container.attach_wait(lxc.attach_run_command,
 
174
                          ["userdel", "-r", "ubuntu"])
 
175
 
 
176
    # Copy the ubuntu-desktop-next metapackage to the rootfs
 
177
    lxc_archive = os.path.join(rootfs_path, 'tmp', 'ubuntu-desktop-next.deb')
 
178
    glob_name = "ubuntu-desktop-next_*_%s.deb" % architecture
 
179
    metapackage = glob.glob(os.path.join('/', 'usr', 'share', 'unity8-lxc', glob_name))
 
180
    shutil.copy(metapackage[0], lxc_archive)
 
181
   
 
182
    container.attach_wait(lxc.attach_run_command,
 
183
                          ["apt-get", "update"])
 
184
 
 
185
    container.attach_wait(lxc.attach_run_command,
 
186
                          ["dpkg", "-i", os.path.join('/', 'tmp', 'ubuntu-desktop-next.deb')])
 
187
    container.attach_wait(lxc.attach_run_command,
 
188
                          ["apt-get", "install", "-f", "-y"])
 
189
 
 
190
    container.attach_wait(lxc.attach_run_command,
 
191
                          ["systemctl", "disable", "lightdm.service"]) 
 
192
    container.stop()
 
193
 
 
194
    # Disable some upstart jobs
274
195
    for job in glob.glob("%s/etc/init/*.conf" % rootfs_path):
275
196
        if os.path.basename(job).rsplit(".conf", 1)[0] in SKIP_JOB:
276
197
            with open("%s.override" % job.rsplit(".conf", 1)[0], "w+") as fd:
277
198
                fd.write("manual")
278
199
 
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("Switching to upstart...")
326
 
    container.attach_wait(lxc.attach_run_command,
327
 
                          ["apt-get", "install", "upstart-sysv", "-y", "--no-download"])
328
 
 
329
 
    container.stop()
 
200
    with open(os.path.join(container_path, 'config'), 'a') as fd:
 
201
        fd.write("lxc.include = " + os.path.join('/', 'usr', 'share', 'unity8-lxc', 'unity8-lxc.conf'))
 
202
        fd.close()
330
203
 
331
204
if args.test:
332
205
    # Start a test session