Merge lp:~townsend/libertine/release-1.2.2 into lp:libertine/trunk
- release-1.2.2
- Merge into trunk
Status: | Merged | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Approved by: | Larry Price | ||||||||||||||||||||||||||||
Approved revision: | 139 | ||||||||||||||||||||||||||||
Merged at revision: | 138 | ||||||||||||||||||||||||||||
Proposed branch: | lp:~townsend/libertine/release-1.2.2 | ||||||||||||||||||||||||||||
Merge into: | lp:libertine/trunk | ||||||||||||||||||||||||||||
Diff against target: |
2202 lines (+838/-865) 17 files modified
debian/changelog (+30/-0) debian/python3-libertine.install (+2/-0) debian/rules (+3/-0) libertine/qml/ExtraArchivesView.qml (+3/-6) libertine/qml/GenericErrorDialog.qml (+17/-3) libertine/qml/HomeView.qml (+51/-43) po/CMakeLists.txt (+0/-3) python/libertine/ChrootContainer.py (+26/-72) python/libertine/ContainersConfig.py (+309/-0) python/libertine/HostInfo.py (+71/-0) python/libertine/Libertine.py (+17/-44) python/libertine/LxcContainer.py (+19/-16) python/libertine/utils.py (+1/-29) tests/unit/CMakeLists.txt (+11/-7) tools/libertine-container-manager (+269/-634) tools/libertine-launch (+8/-8) tools/libertine-session-bridge (+1/-0) |
||||||||||||||||||||||||||||
To merge this branch: | bzr merge lp:~townsend/libertine/release-1.2.2 | ||||||||||||||||||||||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Larry Price | Approve | ||
Review via email: mp+299464@code.launchpad.net |
Commit message
* If we fail to find the host path remove the session path since it would have been created with the socket.
* Only set the LXC log file when the container is about to start. (LP: #1596020)
* Add a new ContainersConfig Python class for managing all things ContainersConfi
* Refactor host information into a new HostInfo class.
* Check if the LXC container is defined before trying to start it.
* Remove dependency on DISPLAY variable and use random string to generate session socket path.
* Prevent showing multiple error dialogs when adding archive fails. (LP: #1594957)
* Only generate translations when manually running `make translations`.
* Ensure /usr/games is in PATH before launching applications. (LP: #1598227)
* Make error details selectable and add clipboard button. (LP: #1598786)
* Use dialog instead of ActionSelection
* Verify host kernel has lxc support. (LP: #1599193)
* Set DEBIAN_FRONTEND directly in environment for chroot containers. (LP: #1599246)
Description of the change
- 139. By Christopher Townsend
-
Merged hot fix from lp:libertine.
Fix typo in debian/changelog.
Larry Price (larryprice) wrote : | # |
Alright - lgtm!
Preview Diff
1 | === modified file 'debian/changelog' |
2 | --- debian/changelog 2016-06-23 14:01:47 +0000 |
3 | +++ debian/changelog 2016-07-07 20:14:07 +0000 |
4 | @@ -1,3 +1,33 @@ |
5 | +libertine (1.2.2-0ubuntu1) UNRELEASED; urgency=medium |
6 | + |
7 | + [ Brandon Schaefer ] |
8 | + * If we fail to find the host path remove the session path since it would |
9 | + have been created with the socket. |
10 | + |
11 | + [ Chris Townsend ] |
12 | + * Only set the LXC log file when the container is about to start. |
13 | + (LP: #1596020) |
14 | + * Add a new ContainersConfig Python class for managing all things |
15 | + ContainersConfig.json. |
16 | + * Refactor host information into a new HostInfo class. |
17 | + * Check if the LXC container is defined before trying to start it. |
18 | + |
19 | + [ Larry Price ] |
20 | + * Remove dependency on DISPLAY variable and use random string to generate |
21 | + session socket path. |
22 | + * Prevent showing multiple error dialogs when adding archive fails. |
23 | + (LP: #1594957) |
24 | + * Only generate translations when manually running `make translations`. |
25 | + * Ensure /usr/games is in PATH before launching applications. (LP: #1598227) |
26 | + * Make error details selectable and add clipboard button. (LP: #1598786) |
27 | + * Use dialog instead of ActionSelectionPopover to address focusing issues |
28 | + when installing packages. |
29 | + * Verify host kernel has lxc support. (LP: #1599193) |
30 | + * Set DEBIAN_FRONTEND directly in environment for chroot containers. |
31 | + (LP: #1599246) |
32 | + |
33 | + -- Chris Townsend <christopher.townsend@canonical.com> Thu, 07 Jul 2016 15:08:59 -0400 |
34 | + |
35 | libertine (1.2.1+16.10.20160623-0ubuntu1) yakkety; urgency=medium |
36 | |
37 | [ Chris Townsend ] |
38 | |
39 | === modified file 'debian/python3-libertine.install' |
40 | --- debian/python3-libertine.install 2015-12-15 15:46:58 +0000 |
41 | +++ debian/python3-libertine.install 2016-07-07 20:14:07 +0000 |
42 | @@ -1,4 +1,6 @@ |
43 | usr/lib/python*/*/libertine/AppDiscovery.py |
44 | usr/lib/python*/*/libertine/Libertine.py |
45 | +usr/lib/python*/*/libertine/ContainersConfig.py |
46 | +usr/lib/python*/*/libertine/HostInfo.py |
47 | usr/lib/python*/*/libertine/utils.py |
48 | usr/lib/python*/*/libertine/__init__.py |
49 | |
50 | === modified file 'debian/rules' |
51 | --- debian/rules 2016-06-22 19:52:24 +0000 |
52 | +++ debian/rules 2016-07-07 20:14:07 +0000 |
53 | @@ -2,3 +2,6 @@ |
54 | |
55 | %: |
56 | dh $@ --with python3,gir,click |
57 | + |
58 | +override_dh_auto_build: |
59 | + dh_auto_build -- all translations |
60 | |
61 | === modified file 'libertine/qml/ExtraArchivesView.qml' |
62 | --- libertine/qml/ExtraArchivesView.qml 2016-05-19 17:56:43 +0000 |
63 | +++ libertine/qml/ExtraArchivesView.qml 2016-07-07 20:14:07 +0000 |
64 | @@ -126,25 +126,22 @@ |
65 | } |
66 | |
67 | function addArchive(archive) { |
68 | - var comp = Qt.createComponent("ContainerManager.qml") |
69 | - worker = comp.createObject(mainView) |
70 | + var worker = Qt.createComponent("ContainerManager.qml").createObject(mainView) |
71 | worker.finishedConfigure.connect(finishedConfigure) |
72 | worker.error.connect(sendAddError) |
73 | - error.connect(mainView.error) |
74 | worker.configureContainer(mainView.currentContainer, containerConfigList.getContainerName(mainView.currentContainer), ["--add-archive", archive]) |
75 | } |
76 | |
77 | function deleteArchive(archive) { |
78 | - var comp = Qt.createComponent("ContainerManager.qml") |
79 | - worker = comp.createObject(mainView) |
80 | + var worker = Qt.createComponent("ContainerManager.qml").createObject(mainView) |
81 | worker.finishedConfigure.connect(finishedConfigure) |
82 | worker.error.connect(sendDeleteError) |
83 | - error.connect(mainView.error) |
84 | worker.configureContainer(mainView.currentContainer, containerConfigList.getContainerName(mainView.currentContainer), ["--delete-archive", archive]) |
85 | } |
86 | |
87 | Component.onCompleted: { |
88 | containerConfigList.configChanged.connect(reloadArchives) |
89 | + error.connect(mainView.error) |
90 | } |
91 | |
92 | Component.onDestruction: { |
93 | |
94 | === modified file 'libertine/qml/GenericErrorDialog.qml' |
95 | --- libertine/qml/GenericErrorDialog.qml 2016-05-10 14:55:15 +0000 |
96 | +++ libertine/qml/GenericErrorDialog.qml 2016-07-07 20:14:07 +0000 |
97 | @@ -22,11 +22,25 @@ |
98 | |
99 | Dialog { |
100 | id: genericErrorDialog |
101 | - property var short_description: null |
102 | - property var details: null |
103 | + property string short_description: "" |
104 | + property string details: "" |
105 | |
106 | title: short_description |
107 | - text: details |
108 | + |
109 | + TextEdit { |
110 | + color: UbuntuColors.darkGrey |
111 | + text: details |
112 | + readOnly: true |
113 | + selectByMouse: true |
114 | + wrapMode: TextEdit.WordWrap |
115 | + } |
116 | + |
117 | + Button { |
118 | + text: i18n.tr("Copy to Clipboard") |
119 | + onClicked: { |
120 | + Clipboard.push(details) |
121 | + } |
122 | + } |
123 | |
124 | Button { |
125 | text: i18n.tr("Dismiss") |
126 | |
127 | === modified file 'libertine/qml/HomeView.qml' |
128 | --- libertine/qml/HomeView.qml 2016-06-08 21:36:14 +0000 |
129 | +++ libertine/qml/HomeView.qml 2016-07-07 20:14:07 +0000 |
130 | @@ -31,11 +31,11 @@ |
131 | Action { |
132 | id: settingsButton |
133 | iconName: "settings" |
134 | - onTriggered: PopupUtils.open(settingsMenu, homeView) |
135 | + onTriggered: PopupUtils.open(settingsMenu) |
136 | }, |
137 | Action { |
138 | iconName: "add" |
139 | - onTriggered: PopupUtils.open(addAppsMenu, homeView) |
140 | + onTriggered: PopupUtils.open(addAppsMenu) |
141 | } |
142 | ] |
143 | } |
144 | @@ -98,26 +98,28 @@ |
145 | |
146 | Component { |
147 | id: settingsMenu |
148 | - ActionSelectionPopover { |
149 | - actions: ActionList { |
150 | - Action { |
151 | - text: i18n.tr("Manage Container") |
152 | - onTriggered: { |
153 | - pageStack.push(Qt.resolvedUrl("ManageContainer.qml"), {currentContainer: currentContainer}) |
154 | - } |
155 | - } |
156 | - Action { |
157 | - text: i18n.tr("Container Information") |
158 | - onTriggered: { |
159 | - pageStack.push(Qt.resolvedUrl("ContainerInfoView.qml"), {currentContainer: currentContainer}) |
160 | - } |
161 | - } |
162 | - Action { |
163 | - text: i18n.tr("Switch Container") |
164 | - onTriggered: { |
165 | - pageStack.pop() |
166 | - pageStack.push(Qt.resolvedUrl("ContainersView.qml")) |
167 | - } |
168 | + Dialog { |
169 | + id: settingsDialog |
170 | + __closeOnDismissAreaPress: true |
171 | + Button { |
172 | + text: i18n.tr("Manage Container") |
173 | + onTriggered: { |
174 | + PopupUtils.close(settingsDialog) |
175 | + pageStack.push(Qt.resolvedUrl("ManageContainer.qml"), {currentContainer: currentContainer}) |
176 | + } |
177 | + } |
178 | + Button { |
179 | + text: i18n.tr("Container Information") |
180 | + onTriggered: { |
181 | + PopupUtils.close(settingsDialog) |
182 | + pageStack.push(Qt.resolvedUrl("ContainerInfoView.qml"), {currentContainer: currentContainer}) |
183 | + } |
184 | + } |
185 | + Button { |
186 | + text: i18n.tr("Switch Container") |
187 | + onTriggered: { |
188 | + PopupUtils.close(settingsDialog) |
189 | + pageStack.pop() |
190 | } |
191 | } |
192 | } |
193 | @@ -125,27 +127,33 @@ |
194 | |
195 | Component { |
196 | id: addAppsMenu |
197 | - ActionSelectionPopover { |
198 | - id: addAppsActions |
199 | - actions: ActionList { |
200 | - Action { |
201 | - text: i18n.tr("Enter package name or Debian file") |
202 | - onTriggered: { |
203 | - PopupUtils.open(enterPackagePopup) |
204 | - } |
205 | - } |
206 | - Action { |
207 | - text: i18n.tr("Choose Debian package to install") |
208 | - onTriggered: { |
209 | - var packages = containerConfigList.getDebianPackageFiles() |
210 | - pageStack.push(Qt.resolvedUrl("DebianPackagePicker.qml"), {packageList: packages}) |
211 | - } |
212 | - } |
213 | - Action { |
214 | - text: i18n.tr("Search archives for a package") |
215 | - onTriggered: { |
216 | - PopupUtils.open(Qt.resolvedUrl("SearchPackagesDialog.qml")) |
217 | - } |
218 | + Dialog { |
219 | + id: addAppsDialog |
220 | + __closeOnDismissAreaPress: true |
221 | + |
222 | + Button { |
223 | + text: i18n.tr("Enter package name or Debian file") |
224 | + width: parent.width |
225 | + onClicked: { |
226 | + PopupUtils.close(addAppsDialog) |
227 | + PopupUtils.open(enterPackagePopup) |
228 | + } |
229 | + } |
230 | + Button { |
231 | + text: i18n.tr("Choose Debian package to install") |
232 | + width: parent.width |
233 | + onClicked: { |
234 | + PopupUtils.close(addAppsDialog) |
235 | + var packages = containerConfigList.getDebianPackageFiles() |
236 | + pageStack.push(Qt.resolvedUrl("DebianPackagePicker.qml"), {packageList: packages}) |
237 | + } |
238 | + } |
239 | + Button { |
240 | + text: i18n.tr("Search archives for a package") |
241 | + width: parent.width |
242 | + onClicked: { |
243 | + PopupUtils.close(addAppsDialog) |
244 | + PopupUtils.open(Qt.resolvedUrl("SearchPackagesDialog.qml")) |
245 | } |
246 | } |
247 | } |
248 | |
249 | === modified file 'po/CMakeLists.txt' |
250 | --- po/CMakeLists.txt 2016-02-29 18:07:02 +0000 |
251 | +++ po/CMakeLists.txt 2016-07-07 20:14:07 +0000 |
252 | @@ -1,7 +1,6 @@ |
253 | set (GETTEXT_PACKAGE "libertine") |
254 | |
255 | intltool_update_potfile( |
256 | - ALL |
257 | UBUNTU_SDK_DEFAULTS |
258 | POTFILES_TEMPLATE POTFILES.in.in |
259 | COPYRIGHT_HOLDER "Canonical Ltd." |
260 | @@ -9,14 +8,12 @@ |
261 | ) |
262 | |
263 | intltool_install_translations( |
264 | - ALL |
265 | GETTEXT_PACKAGE ${GETTEXT_PACKAGE} |
266 | ) |
267 | |
268 | file(GLOB_RECURSE ALL_POFILES "*.po") |
269 | |
270 | add_custom_target(potfiles |
271 | - ALL |
272 | SOURCES |
273 | POTFILES.in.in |
274 | ${GETTEXT_PACKAGE}.pot |
275 | |
276 | === modified file 'python/libertine/ChrootContainer.py' |
277 | --- python/libertine/ChrootContainer.py 2016-06-15 18:10:05 +0000 |
278 | +++ python/libertine/ChrootContainer.py 2016-07-07 20:14:07 +0000 |
279 | @@ -17,6 +17,7 @@ |
280 | import shlex |
281 | import shutil |
282 | import subprocess |
283 | + |
284 | from .Libertine import BaseContainer |
285 | from . import utils |
286 | |
287 | @@ -53,10 +54,7 @@ |
288 | |
289 | def run_in_container(self, command_string): |
290 | cmd_args = shlex.split(command_string) |
291 | - if self.get_container_distro(self.container_id) == "trusty": |
292 | - command_prefix = self._build_privileged_proot_cmd() |
293 | - else: |
294 | - command_prefix = "fakechroot fakeroot chroot " + self.root_path |
295 | + command_prefix = "fakechroot fakeroot chroot " + self.root_path |
296 | args = shlex.split(command_prefix + ' ' + command_string) |
297 | cmd = subprocess.Popen(args) |
298 | return cmd.wait() |
299 | @@ -66,15 +64,9 @@ |
300 | shutil.rmtree(container_root) |
301 | |
302 | def create_libertine_container(self, password=None, multiarch=False, verbosity=1): |
303 | - installed_release = self.get_container_distro(self.container_id) |
304 | - architecture = utils.get_host_architecture() |
305 | - |
306 | # Create the actual chroot |
307 | - if installed_release == "trusty": |
308 | - command_line = "debootstrap --verbose " + installed_release + " " + self.root_path |
309 | - else: |
310 | - command_line = "fakechroot fakeroot debootstrap --verbose --variant=fakechroot {} {}".format( |
311 | - installed_release, self.root_path) |
312 | + command_line = "fakechroot fakeroot debootstrap --verbose --variant=fakechroot {} {}".format( |
313 | + self.installed_release, self.root_path) |
314 | args = shlex.split(command_line) |
315 | cmd = subprocess.Popen(args) |
316 | cmd.wait() |
317 | @@ -84,25 +76,24 @@ |
318 | self.destroy_libertine_container() |
319 | return False |
320 | |
321 | - # Remove symlinks as they can ill-behaved recursive behavior in the chroot |
322 | - if installed_release != "trusty": |
323 | - print("Fixing chroot symlinks...") |
324 | - os.remove(os.path.join(self.root_path, 'dev')) |
325 | - os.remove(os.path.join(self.root_path, 'proc')) |
326 | + # Remove symlinks as they can cause ill-behaved recursive behavior in the chroot |
327 | + print("Fixing chroot symlinks...") |
328 | + os.remove(os.path.join(self.root_path, 'dev')) |
329 | + os.remove(os.path.join(self.root_path, 'proc')) |
330 | |
331 | - with open(os.path.join(self.root_path, 'usr', 'sbin', 'policy-rc.d'), 'w+') as fd: |
332 | - fd.write("#!/bin/sh\n\n") |
333 | - fd.write("while true; do\n") |
334 | - fd.write("case \"$1\" in\n") |
335 | - fd.write(" -*) shift ;;\n") |
336 | - fd.write(" makedev) exit 0;;\n") |
337 | - fd.write(" *) exit 101;;\n") |
338 | - fd.write("esac\n") |
339 | - fd.write("done\n") |
340 | - os.fchmod(fd.fileno(), 0o755) |
341 | + with open(os.path.join(self.root_path, 'usr', 'sbin', 'policy-rc.d'), 'w+') as fd: |
342 | + fd.write("#!/bin/sh\n\n") |
343 | + fd.write("while true; do\n") |
344 | + fd.write("case \"$1\" in\n") |
345 | + fd.write(" -*) shift ;;\n") |
346 | + fd.write(" makedev) exit 0;;\n") |
347 | + fd.write(" *) exit 101;;\n") |
348 | + fd.write("esac\n") |
349 | + fd.write("done\n") |
350 | + os.fchmod(fd.fileno(), 0o755) |
351 | |
352 | # Add universe, multiverse, and -updates to the chroot's sources.list |
353 | - if (utils.get_host_architecture() == 'armhf'): |
354 | + if (self.architecture == 'armhf'): |
355 | archive = "deb http://ports.ubuntu.com/ubuntu-ports " |
356 | else: |
357 | archive = "deb http://archive.ubuntu.com/ubuntu " |
358 | @@ -110,52 +101,15 @@ |
359 | if verbosity == 1: |
360 | print("Updating chroot's sources.list entries...") |
361 | with open(os.path.join(self.root_path, 'etc', 'apt', 'sources.list'), 'a') as fd: |
362 | - fd.write(archive + installed_release + "-updates main\n") |
363 | - fd.write(archive + installed_release + " universe\n") |
364 | - fd.write(archive + installed_release + "-updates universe\n") |
365 | - fd.write(archive + installed_release + " multiverse\n") |
366 | - fd.write(archive + installed_release + "-updates multiverse\n") |
367 | + fd.write(archive + self.installed_release + "-updates main\n") |
368 | + fd.write(archive + self.installed_release + " universe\n") |
369 | + fd.write(archive + self.installed_release + "-updates universe\n") |
370 | + fd.write(archive + self.installed_release + " multiverse\n") |
371 | + fd.write(archive + self.installed_release + "-updates multiverse\n") |
372 | |
373 | utils.create_libertine_user_data_dir(self.container_id) |
374 | |
375 | - if installed_release == "trusty": |
376 | - print("Additional configuration for Trusty chroot...") |
377 | - |
378 | - cmd_line_prefix = self._build_privileged_proot_cmd() |
379 | - |
380 | - command_line = cmd_line_prefix + " dpkg-divert --local --rename --add /etc/init.d/systemd-logind" |
381 | - args = shlex.split(command_line) |
382 | - cmd = subprocess.Popen(args).wait() |
383 | - |
384 | - command_line = cmd_line_prefix + " dpkg-divert --local --rename --add /sbin/initctl" |
385 | - args = shlex.split(command_line) |
386 | - cmd = subprocess.Popen(args).wait() |
387 | - |
388 | - command_line = cmd_line_prefix + " dpkg-divert --local --rename --add /sbin/udevd" |
389 | - args = shlex.split(command_line) |
390 | - cmd = subprocess.Popen(args).wait() |
391 | - |
392 | - command_line = cmd_line_prefix + " dpkg-divert --local --rename --add /usr/sbin/rsyslogd" |
393 | - args = shlex.split(command_line) |
394 | - cmd = subprocess.Popen(args).wait() |
395 | - |
396 | - command_line = cmd_line_prefix + " ln -s /bin/true /etc/init.d/systemd-logind" |
397 | - args = shlex.split(command_line) |
398 | - cmd = subprocess.Popen(args).wait() |
399 | - |
400 | - command_line = cmd_line_prefix + " ln -s /bin/true /sbin/initctl" |
401 | - args = shlex.split(command_line) |
402 | - cmd = subprocess.Popen(args).wait() |
403 | - |
404 | - command_line = cmd_line_prefix + " ln -s /bin/true /sbin/udevd" |
405 | - args = shlex.split(command_line) |
406 | - cmd = subprocess.Popen(args).wait() |
407 | - |
408 | - command_line = cmd_line_prefix + " ln -s /bin/true /usr/sbin/rsyslogd" |
409 | - args = shlex.split(command_line) |
410 | - cmd = subprocess.Popen(args).wait() |
411 | - |
412 | - if multiarch and architecture == 'amd64': |
413 | + if multiarch and self.architecture == 'amd64': |
414 | if verbosity == 1: |
415 | print("Adding i386 multiarch support...") |
416 | self.run_in_container("dpkg --add-architecture i386") |
417 | @@ -170,7 +124,7 @@ |
418 | self.destroy_libertine_container() |
419 | return False |
420 | |
421 | - if installed_release == "vivid": |
422 | + if self.installed_release == "vivid": |
423 | if verbosity == 1: |
424 | print("Installing the Vivid Stable Overlay PPA...") |
425 | self.run_in_container("add-apt-repository ppa:ci-train-ppa-service/stable-phone-overlay -y") |
426 | |
427 | === added file 'python/libertine/ContainersConfig.py' |
428 | --- python/libertine/ContainersConfig.py 1970-01-01 00:00:00 +0000 |
429 | +++ python/libertine/ContainersConfig.py 2016-07-07 20:14:07 +0000 |
430 | @@ -0,0 +1,309 @@ |
431 | +# Copyright 2016 Canonical Ltd. |
432 | +# |
433 | +# This program is free software: you can redistribute it and/or modify it |
434 | +# under the terms of the GNU General Public License version 3, as published |
435 | +# by the Free Software Foundation. |
436 | +# |
437 | +# This program is distributed in the hope that it will be useful, but |
438 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
439 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
440 | +# PURPOSE. See the GNU General Public License for more details. |
441 | +# |
442 | +# You should have received a copy of the GNU General Public License along |
443 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
444 | + |
445 | +import fcntl |
446 | +import json |
447 | +import libertine.utils |
448 | +import os |
449 | +import sys |
450 | + |
451 | + |
452 | +def read_container_config_file(): |
453 | + container_list = {} |
454 | + container_config_file = libertine.utils.get_libertine_database_file_path() |
455 | + |
456 | + if (os.path.exists(container_config_file) and |
457 | + os.path.getsize(container_config_file) != 0): |
458 | + with open(container_config_file, 'r') as fd: |
459 | + container_list = json.load(fd) |
460 | + |
461 | + return container_list |
462 | + |
463 | + |
464 | +def write_container_config_file(container_list): |
465 | + container_config_file = libertine.utils.get_libertine_database_file_path() |
466 | + |
467 | + with open(container_config_file, 'w') as fd: |
468 | + fcntl.lockf(fd, fcntl.LOCK_EX) |
469 | + json.dump(container_list, fd, sort_keys=True, indent=4) |
470 | + fd.write('\n') |
471 | + fcntl.lockf(fd, fcntl.LOCK_UN) |
472 | + |
473 | + |
474 | +class ContainersConfig(object): |
475 | + |
476 | + def __init__(self): |
477 | + self.container_list = read_container_config_file() |
478 | + |
479 | + if "defaultContainer" in self.container_list: |
480 | + self.default_container_id = self.container_list['defaultContainer'] |
481 | + else: |
482 | + self.default_container_id = None |
483 | + |
484 | + """ |
485 | + Private helper methods |
486 | + """ |
487 | + def _get_container_entry(self, container_id): |
488 | + if not self.container_list: |
489 | + return None |
490 | + |
491 | + for container in self.container_list['containerList']: |
492 | + if container['id'] == container_id: |
493 | + return container |
494 | + |
495 | + return None |
496 | + |
497 | + def _get_value_by_key(self, container_id, key): |
498 | + container = self._get_container_entry(container_id) |
499 | + |
500 | + if not container or key not in container: |
501 | + return None |
502 | + else: |
503 | + return container[key] |
504 | + |
505 | + def _get_array_object_value_by_key(self, container_id, array_key, object_key, matcher, key): |
506 | + for item in self._get_value_by_key(container_id, array_key): |
507 | + if item[object_key] == matcher: |
508 | + return item[key] |
509 | + |
510 | + def _set_value_by_key(self, container_id, key, value): |
511 | + container = self._get_container_entry(container_id) |
512 | + |
513 | + if not container: |
514 | + return |
515 | + |
516 | + if type(value) is str: |
517 | + container[key] = value |
518 | + elif type(value) is dict: |
519 | + if key not in container: |
520 | + container[key] = [value] |
521 | + else: |
522 | + container[key].append(value) |
523 | + |
524 | + write_container_config_file(self.container_list) |
525 | + |
526 | + def _set_array_object_value_by_key(self, container_id, array_key, object_key, matcher, key, value): |
527 | + container = self._get_container_entry(container_id) |
528 | + |
529 | + if not container: |
530 | + return |
531 | + |
532 | + for item in container[array_key]: |
533 | + if item[object_key] == matcher: |
534 | + item[key] = value |
535 | + write_container_config_file(self.container_list) |
536 | + return |
537 | + |
538 | + def _delete_array_object_by_key_value(self, container_id, array_key, object_key, value): |
539 | + container = self._get_container_entry(container_id) |
540 | + |
541 | + if not container: |
542 | + return |
543 | + |
544 | + for item in container[array_key]: |
545 | + if item[object_key] == value: |
546 | + container[array_key].remove(item) |
547 | + write_container_config_file(self.container_list) |
548 | + return |
549 | + |
550 | + def _test_key_value_exists(self, container_id, key, value=None): |
551 | + key_value = self._get_value_by_key(container_id, key) |
552 | + |
553 | + if not key_value: |
554 | + return False |
555 | + elif key == 'id': |
556 | + return True |
557 | + elif key_value == value: |
558 | + return True |
559 | + else: |
560 | + return False |
561 | + |
562 | + def _test_array_object_key_value_exists(self, container_id, array_key, object_key, value): |
563 | + array = self._get_value_by_key(container_id, array_key) |
564 | + |
565 | + if not array: |
566 | + return False |
567 | + |
568 | + for item in array: |
569 | + if item[object_key] == value: |
570 | + return True |
571 | + |
572 | + return False |
573 | + |
574 | + """ |
575 | + Miscellaneous ContainersConfig.json operations |
576 | + """ |
577 | + def _find_duplicate_container_entry(self, container_list, container_id): |
578 | + for container in container_list['containerList']: |
579 | + if container['id'] == container_id: |
580 | + return container |
581 | + |
582 | + return None |
583 | + |
584 | + def merge_container_config_files(self, filepath): |
585 | + merged_json = [] |
586 | + |
587 | + with open(filepath, 'r') as fd: |
588 | + merge_source = json.load(fd) |
589 | + |
590 | + if "containerList" in self.container_list: |
591 | + # Finds any duplicate entries and assumes we want to update the main config |
592 | + # with entries from the merge source. |
593 | + for i, container in enumerate(self.container_list['containerList']): |
594 | + merge_container = self._find_duplicate_container_entry(merge_source, container['id']) |
595 | + if merge_container: |
596 | + self.container_list['containerList'][i] = merge_container |
597 | + merge_source['containerList'].remove(merge_container) |
598 | + |
599 | + # Merges in any remaining non-duplicate entries. |
600 | + for container in merge_source['containerList']: |
601 | + self.container_list['containerList'].append(container) |
602 | + |
603 | + else: |
604 | + self.container_list = merge_source |
605 | + |
606 | + write_container_config_file(self.container_list) |
607 | + |
608 | + def check_container_id(self, container_id): |
609 | + if container_id and not self.container_exists(container_id): |
610 | + print("Container id \'%s\' does not exist." % container_id, file=sys.stderr) |
611 | + sys.exit(1) |
612 | + elif not container_id: |
613 | + return self.get_default_container_id() |
614 | + |
615 | + return container_id |
616 | + |
617 | + def get_default_container_id(self): |
618 | + return self.default_container_id |
619 | + |
620 | + def set_default_container_id(self, container_id, write_json=False): |
621 | + self.default_container_id = container_id |
622 | + self.container_list['defaultContainer'] = container_id |
623 | + |
624 | + if write_json: |
625 | + write_container_config_file(self.container_list) |
626 | + |
627 | + def clear_default_container_id(self, write_json=False): |
628 | + self.default_container_id = None |
629 | + self.container_list.pop('defaultContainer', None) |
630 | + |
631 | + if write_json: |
632 | + write_container_config_file(self.container_list) |
633 | + |
634 | + """ |
635 | + Operations for the container itself. |
636 | + """ |
637 | + def add_new_container(self, container_id, container_name, container_type, container_distro): |
638 | + container_obj = {'id': container_id, 'installStatus': 'new', 'type': container_type, |
639 | + 'distro': container_distro, 'name': container_name, 'installedApps': []} |
640 | + |
641 | + if "defaultContainer" not in self.container_list: |
642 | + self.set_default_container_id(container_id) |
643 | + |
644 | + if "containerList" not in self.container_list: |
645 | + self.container_list['containerList'] = [container_obj] |
646 | + else: |
647 | + self.container_list['containerList'].append(container_obj) |
648 | + |
649 | + write_container_config_file(self.container_list) |
650 | + |
651 | + def delete_container(self, container_id): |
652 | + if not self.container_list: |
653 | + print("Unable to delete container. No containers defined.") |
654 | + sys.exit(1) |
655 | + |
656 | + container = self._get_container_entry(container_id) |
657 | + |
658 | + self.container_list['containerList'].remove(container) |
659 | + |
660 | + # Set a new defaultContainer if the current default is being deleted. |
661 | + if self.container_list['defaultContainer'] == container_id and self.container_list['containerList']: |
662 | + self.set_default_container_id(self.container_list['containerList'][0]['id']) |
663 | + # Remove the defaultContainer if there are no more containers left. |
664 | + elif not self.container_list['containerList']: |
665 | + self.clear_default_container_id() |
666 | + |
667 | + write_container_config_file(self.container_list) |
668 | + |
669 | + def update_container_install_status(self, container_id, new_status): |
670 | + self._set_value_by_key(container_id, 'installStatus', new_status) |
671 | + |
672 | + def container_exists(self, container_id): |
673 | + return self._test_key_value_exists(container_id, 'id') |
674 | + |
675 | + def update_container_multiarch_support(self, container_id, multiarch): |
676 | + if multiarch == 'enabled' and libertine.utils.get_host_architecture() == 'i386': |
677 | + multiarch = 'disabled' |
678 | + |
679 | + self._set_value_by_key(container_id, 'multiarch', multiarch) |
680 | + |
681 | + def get_container_multiarch_support(self, container_id): |
682 | + multiarch_support = self._get_value_by_key(container_id, 'multiarch') |
683 | + |
684 | + if multiarch_support == None: |
685 | + return 'disabled' |
686 | + else: |
687 | + return multiarch_support |
688 | + |
689 | + """ |
690 | + Operations for archive (PPA) maintenance in a Libertine container. |
691 | + """ |
692 | + def add_container_archive(self, container_id, archive_name): |
693 | + archive_obj = {'archiveName': archive_name, 'archiveStatus': 'new'} |
694 | + self._set_value_by_key(container_id, 'extraArchives', archive_obj) |
695 | + |
696 | + def delete_container_archive(self, container_id, archive_name): |
697 | + self._delete_array_object_by_key_value(container_id, 'extraArchives', |
698 | + 'archiveName', archive_name) |
699 | + |
700 | + def update_archive_install_status(self, container_id, archive_name, new_status): |
701 | + self._set_array_object_value_by_key(container_id, 'extraArchives', 'archiveName', |
702 | + archive_name, 'archiveStatus', new_status) |
703 | + |
704 | + def archive_exists(self, container_id, archive_name): |
705 | + return self._test_array_object_key_value_exists(container_id, 'extraArchives', 'archiveName', |
706 | + archive_name) |
707 | + |
708 | + """ |
709 | + Operations for package maintenance in a Libertine container. |
710 | + """ |
711 | + def add_new_package(self, container_id, package_name): |
712 | + package_obj = {'packageName': package_name, 'appStatus': 'new'} |
713 | + self._set_value_by_key(container_id, 'installedApps', package_obj) |
714 | + |
715 | + def delete_package(self, container_id, package_name): |
716 | + self._delete_array_object_by_key_value(container_id, 'installedApps', |
717 | + 'packageName', package_name) |
718 | + |
719 | + def update_package_install_status(self, container_id, package_name, new_status): |
720 | + self._set_array_object_value_by_key(container_id, 'installedApps', 'packageName', |
721 | + package_name, 'appStatus', new_status) |
722 | + |
723 | + def get_package_install_status(self, container_id, package_name): |
724 | + return self._get_array_object_value_by_key(container_id, 'installedApps', 'packageName', |
725 | + package_name, 'appStatus') |
726 | + |
727 | + def package_exists(self, container_id, package_name): |
728 | + return self._test_array_object_key_value_exists(container_id, 'installedApps', 'packageName', |
729 | + package_name) |
730 | + |
731 | + """ |
732 | + Fetcher functions for various configuration information. |
733 | + """ |
734 | + def get_container_distro(self, container_id): |
735 | + return self._get_value_by_key(container_id, 'distro') |
736 | + |
737 | + def get_container_type(self, container_id): |
738 | + return self._get_value_by_key(container_id, 'type') |
739 | + |
740 | |
741 | === added file 'python/libertine/HostInfo.py' |
742 | --- python/libertine/HostInfo.py 1970-01-01 00:00:00 +0000 |
743 | +++ python/libertine/HostInfo.py 2016-07-07 20:14:07 +0000 |
744 | @@ -0,0 +1,71 @@ |
745 | +# Copyright 2016 Canonical Ltd. |
746 | +# |
747 | +# This program is free software: you can redistribute it and/or modify it |
748 | +# under the terms of the GNU General Public License version 3, as published |
749 | +# by the Free Software Foundation. |
750 | +# |
751 | +# This program is distributed in the hope that it will be useful, but |
752 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
753 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
754 | +# PURPOSE. See the GNU General Public License for more details. |
755 | +# |
756 | +# You should have received a copy of the GNU General Public License along |
757 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
758 | + |
759 | +import lsb_release |
760 | +import platform |
761 | +import subprocess |
762 | + |
763 | +from distro_info import UbuntuDistroInfo |
764 | + |
765 | + |
766 | +class HostInfo(object): |
767 | + |
768 | + def select_container_type_by_kernel(self): |
769 | + if self.has_lxc_support(): |
770 | + return "lxc" |
771 | + else: |
772 | + return "chroot" |
773 | + |
774 | + def has_lxc_support(self): |
775 | + kernel_release = platform.release().split('.') |
776 | + return int(kernel_release[0]) >= 4 or (int(kernel_release[0]) == 3 and int(kernel_release[1]) >= 13) |
777 | + |
778 | + def get_host_distro_release(self): |
779 | + distinfo = lsb_release.get_distro_information() |
780 | + |
781 | + return distinfo.get('CODENAME', 'n/a') |
782 | + |
783 | + def is_distro_valid(self, distro, force): |
784 | + if force: |
785 | + return UbuntuDistroInfo().valid(distro) |
786 | + |
787 | + if distro == self.get_host_distro_release(): |
788 | + return True |
789 | + |
790 | + supported_distros = UbuntuDistroInfo().supported() |
791 | + |
792 | + try: |
793 | + supported_distros.index(distro) |
794 | + except ValueError: |
795 | + return False |
796 | + |
797 | + return True |
798 | + |
799 | + def get_distro_codename(self, distro): |
800 | + ubuntu_distro_info = UbuntuDistroInfo() |
801 | + |
802 | + for row in ubuntu_distro_info._rows: |
803 | + if row['series'] == distro: |
804 | + return row['codename'] |
805 | + |
806 | + return None |
807 | + |
808 | + def get_host_architecture(self): |
809 | + dpkg = subprocess.Popen(['dpkg', '--print-architecture'], |
810 | + stdout=subprocess.PIPE, |
811 | + universal_newlines=True) |
812 | + if dpkg.wait() != 0: |
813 | + parser.error("Failed to determine the local architecture.") |
814 | + |
815 | + return dpkg.stdout.read().strip() |
816 | |
817 | === modified file 'python/libertine/Libertine.py' |
818 | --- python/libertine/Libertine.py 2016-06-14 20:05:24 +0000 |
819 | +++ python/libertine/Libertine.py 2016-07-07 20:14:07 +0000 |
820 | @@ -16,30 +16,12 @@ |
821 | from gi.repository import Libertine |
822 | import abc |
823 | import contextlib |
824 | -import json |
825 | import libertine.utils |
826 | import os |
827 | import shutil |
828 | |
829 | - |
830 | -def get_container_type(container_id): |
831 | - """ |
832 | - Retrieves the type of container for a given container ID. |
833 | - :param container_id: The Container ID to search for. |
834 | - """ |
835 | - try: |
836 | - with open(libertine.utils.get_libertine_database_file_path()) as fd: |
837 | - container_list = json.load(fd) |
838 | - |
839 | - for container in container_list["containerList"]: |
840 | - if container["id"] == container_id: |
841 | - return container["type"] |
842 | - |
843 | - except FileNotFoundError: |
844 | - pass |
845 | - |
846 | - # Return lxc as the default container type |
847 | - return "lxc" |
848 | +from libertine.ContainersConfig import ContainersConfig |
849 | +from libertine.HostInfo import HostInfo |
850 | |
851 | |
852 | def apt_args_for_verbosity_level(verbosity): |
853 | @@ -175,10 +157,9 @@ |
854 | |
855 | return ret |
856 | else: |
857 | - cmd = apt_command_prefix(verbosity) + " install '" + package_name + "'" |
858 | if readline: |
859 | - cmd = "env DEBIAN_FRONTEND=readline " + cmd |
860 | - return self.run_in_container(cmd) == 0 |
861 | + os.environ['DEBIAN_FRONTEND'] = 'readline' |
862 | + return self.run_in_container(apt_command_prefix(verbosity) + " install '" + package_name + "'") == 0 |
863 | |
864 | def configure_command(self, command, *args, verbosity=1): |
865 | """ |
866 | @@ -207,21 +188,6 @@ |
867 | elif command == 'delete-archive': |
868 | return self.run_in_container("add-apt-repository -y -r " + args[0]) |
869 | |
870 | - def get_container_distro(self, container_id): |
871 | - """ |
872 | - Retrieves the distro code name for a given container ID. |
873 | - |
874 | - :param container_id: The Container ID to search for. |
875 | - """ |
876 | - with open(libertine.utils.get_libertine_database_file_path()) as fd: |
877 | - container_list = json.load(fd) |
878 | - |
879 | - for container in container_list["containerList"]: |
880 | - if container["id"] == container_id: |
881 | - return container["distro"] |
882 | - |
883 | - return "" |
884 | - |
885 | @property |
886 | def name(self): |
887 | """ |
888 | @@ -281,9 +247,9 @@ |
889 | """ |
890 | super().__init__() |
891 | |
892 | - container_type = get_container_type(container_id) |
893 | + container_type = ContainersConfig().get_container_type(container_id) |
894 | |
895 | - if container_type == "lxc": |
896 | + if container_type == None or container_type == "lxc": |
897 | from libertine.LxcContainer import LibertineLXC |
898 | self.container = LibertineLXC(container_id) |
899 | elif container_type == "chroot": |
900 | @@ -320,6 +286,9 @@ |
901 | """ |
902 | Creates the container. |
903 | """ |
904 | + self.container.architecture = HostInfo().get_host_architecture() |
905 | + self.container.installed_release = ContainersConfig().get_container_distro(self.container_id) |
906 | + |
907 | return self.container.create_libertine_container(password, multiarch, verbosity) |
908 | |
909 | def update_libertine_container(self, verbosity=1): |
910 | @@ -351,10 +320,10 @@ |
911 | """ |
912 | try: |
913 | with ContainerRunning(self.container): |
914 | - cmd = apt_command_prefix(verbosity) + " purge '" + package_name + "'" |
915 | if readline: |
916 | - cmd = "env DEBIAN_FRONTEND=readline " + cmd |
917 | - if self.container.run_in_container(cmd) != 0: |
918 | + os.environ['DEBIAN_FRONTEND'] = 'readline' |
919 | + |
920 | + if self.container.run_in_container(apt_command_prefix(verbosity) + " purge '" + package_name + "'") != 0: |
921 | return False |
922 | return self.container.run_in_container(apt_command_prefix(verbosity) + "autoremove --purge") == 0 |
923 | except RuntimeError as e: |
924 | @@ -380,7 +349,11 @@ |
925 | :param app_exec_line: the application exec line as passed in by |
926 | ubuntu-app-launch |
927 | """ |
928 | - if libertine.utils.container_exists(self.container.container_id): |
929 | + if ContainersConfig().container_exists(self.container.container_id): |
930 | + # Update $PATH as necessary |
931 | + if '/usr/games' not in os.environ['PATH']: |
932 | + os.environ['PATH'] = os.environ['PATH'] + ":/usr/games" |
933 | + |
934 | self.container.launch_application(app_exec_line) |
935 | else: |
936 | raise RuntimeError("Container with id %s does not exist." % self.container.container_id) |
937 | |
938 | === modified file 'python/libertine/LxcContainer.py' |
939 | --- python/libertine/LxcContainer.py 2016-06-22 16:26:15 +0000 |
940 | +++ python/libertine/LxcContainer.py 2016-07-07 20:14:07 +0000 |
941 | @@ -18,6 +18,7 @@ |
942 | import psutil |
943 | import shlex |
944 | import subprocess |
945 | +import sys |
946 | import tempfile |
947 | |
948 | from .Libertine import BaseContainer |
949 | @@ -85,11 +86,6 @@ |
950 | self.container_type = "lxc" |
951 | self.container = lxc_container(container_id) |
952 | |
953 | - if self.container.defined: |
954 | - self.lxc_log_file = os.path.join(tempfile.mkdtemp(), 'lxc-start.log') |
955 | - self.container.append_config_item("lxc.logfile", self.lxc_log_file) |
956 | - self.container.append_config_item("lxc.logpriority", "3") |
957 | - |
958 | def is_running(self): |
959 | return self.container.running |
960 | |
961 | @@ -105,7 +101,11 @@ |
962 | return True |
963 | |
964 | def start_container(self): |
965 | + if not self.container.defined: |
966 | + raise RuntimeError("Container %s is not valid" % self.container_id) |
967 | + |
968 | if not self.container.running: |
969 | + self._set_lxc_log() |
970 | if not self.container.start(): |
971 | self._dump_lxc_log() |
972 | raise RuntimeError("Container failed to start") |
973 | @@ -147,8 +147,6 @@ |
974 | if password is None: |
975 | return False |
976 | |
977 | - installed_release = self.get_container_distro(self.container_id) |
978 | - |
979 | username = os.environ['USER'] |
980 | user_id = os.getuid() |
981 | group_id = os.getgid() |
982 | @@ -177,13 +175,10 @@ |
983 | |
984 | utils.create_libertine_user_data_dir(self.container_id) |
985 | |
986 | - # Figure out the host architecture |
987 | - architecture = utils.get_host_architecture() |
988 | - |
989 | if not self.container.create("download", 0, |
990 | {"dist": "ubuntu", |
991 | - "release": installed_release, |
992 | - "arch": architecture}): |
993 | + "release": self.installed_release, |
994 | + "arch": self.architecture}): |
995 | print("Failed to create container") |
996 | return False |
997 | |
998 | @@ -202,7 +197,7 @@ |
999 | self.run_in_container("useradd -u {} -p {} -G sudo {}".format( |
1000 | str(user_id), crypt.crypt(password), str(username))) |
1001 | |
1002 | - if multiarch and architecture == 'amd64': |
1003 | + if multiarch and self.architecture == 'amd64': |
1004 | if verbosity == 1: |
1005 | print("Adding i386 multiarch support...") |
1006 | self.run_in_container("dpkg --add-architecture i386") |
1007 | @@ -308,7 +303,15 @@ |
1008 | data = libertine_lxc_mgr_sock.recv(1024) |
1009 | libertine_lxc_mgr_sock.close() |
1010 | |
1011 | + def _set_lxc_log(self): |
1012 | + self.lxc_log_file = os.path.join(tempfile.mkdtemp(), 'lxc-start.log') |
1013 | + self.container.append_config_item("lxc.logfile", self.lxc_log_file) |
1014 | + self.container.append_config_item("lxc.logpriority", "3") |
1015 | + |
1016 | def _dump_lxc_log(self): |
1017 | - with open(self.lxc_log_file, 'r') as fd: |
1018 | - for line in fd: |
1019 | - print(line.lstrip()) |
1020 | + try: |
1021 | + with open(self.lxc_log_file, 'r') as fd: |
1022 | + for line in fd: |
1023 | + print(line.lstrip()) |
1024 | + except Exception as ex: |
1025 | + print("Could not open LXC log file: %s" % ex, file=sys.stderr) |
1026 | |
1027 | === modified file 'python/libertine/utils.py' |
1028 | --- python/libertine/utils.py 2016-06-23 13:27:26 +0000 |
1029 | +++ python/libertine/utils.py 2016-07-07 20:14:07 +0000 |
1030 | @@ -16,9 +16,7 @@ |
1031 | # You should have received a copy of the GNU General Public License |
1032 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
1033 | |
1034 | -import json |
1035 | import os |
1036 | -import psutil |
1037 | import shlex |
1038 | import subprocess |
1039 | import xdg.BaseDirectory as basedir |
1040 | @@ -28,22 +26,6 @@ |
1041 | from gi.repository import Libertine |
1042 | |
1043 | |
1044 | -def container_exists(container_id): |
1045 | - container_config_file_path = get_libertine_database_file_path() |
1046 | - |
1047 | - if (os.path.exists(container_config_file_path) and |
1048 | - os.path.getsize(container_config_file_path) != 0): |
1049 | - with open(get_libertine_database_file_path()) as fd: |
1050 | - container_list = json.load(fd) |
1051 | - |
1052 | - if container_list: |
1053 | - for container in container_list['containerList']: |
1054 | - if container['id'] == container_id: |
1055 | - return True |
1056 | - |
1057 | - return False |
1058 | - |
1059 | - |
1060 | def get_libertine_container_rootfs_path(container_id): |
1061 | path = Libertine.container_path(container_id) |
1062 | |
1063 | @@ -97,16 +79,6 @@ |
1064 | return os.path.join(get_user_runtime_dir(), 'libertine') |
1065 | |
1066 | |
1067 | -def get_host_architecture(): |
1068 | - dpkg = subprocess.Popen(['dpkg', '--print-architecture'], |
1069 | - stdout=subprocess.PIPE, |
1070 | - universal_newlines=True) |
1071 | - if dpkg.wait() != 0: |
1072 | - parser.error("Failed to determine the local architecture.") |
1073 | - |
1074 | - return dpkg.stdout.read().strip() |
1075 | - |
1076 | - |
1077 | def get_common_xdg_directories(): |
1078 | return ['Documents', 'Music', 'Pictures', 'Videos', 'Downloads'] |
1079 | |
1080 | @@ -167,4 +139,4 @@ |
1081 | gdbus_cmd = ("gdbus emit --session --object-path %s --signal %s %s" % |
1082 | (scopes_object_path, invalidate_signal, libertine_scope_id)) |
1083 | |
1084 | - subprocess.Popen(shlex.split(gdbus_cmd)) |
1085 | + subprocess.Popen(shlex.split(gdbus_cmd), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) |
1086 | |
1087 | === modified file 'tests/unit/CMakeLists.txt' |
1088 | --- tests/unit/CMakeLists.txt 2015-12-21 21:02:33 +0000 |
1089 | +++ tests/unit/CMakeLists.txt 2016-07-07 20:14:07 +0000 |
1090 | @@ -44,10 +44,14 @@ |
1091 | ENVIRONMENT |
1092 | "GI_TYPELIB_PATH=${CMAKE_BINARY_DIR}/liblibertine;LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/liblibertine:${LD_LIBRARY_PATH};CMAKE_BINARY_DIR=${CMAKE_BINARY_DIR};PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}:${CMAKE_SOURCE_DIR}/python;CMAKE_SOURCE_DIR=${CMAKE_SOURCE_DIR}") |
1093 | |
1094 | -add_test(test_libertine_launch |
1095 | - "/usr/bin/python3" "-m" "testtools.run" "libertine_launch_tests" |
1096 | -) |
1097 | -set_tests_properties(test_libertine_launch |
1098 | - PROPERTIES |
1099 | - ENVIRONMENT |
1100 | - "GI_TYPELIB_PATH=${CMAKE_BINARY_DIR}/liblibertine;LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/liblibertine:${LD_LIBRARY_PATH};CMAKE_BINARY_DIR=${CMAKE_BINARY_DIR};PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}:${CMAKE_SOURCE_DIR}/python;CMAKE_SOURCE_DIR=${CMAKE_SOURCE_DIR}") |
1101 | +set(DISABLED 1) |
1102 | + |
1103 | +if (NOT DISABLED) |
1104 | + add_test(test_libertine_launch |
1105 | + "/usr/bin/python3" "-m" "testtools.run" "libertine_launch_tests" |
1106 | + ) |
1107 | + set_tests_properties(test_libertine_launch |
1108 | + PROPERTIES |
1109 | + ENVIRONMENT |
1110 | + "GI_TYPELIB_PATH=${CMAKE_BINARY_DIR}/liblibertine;LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/liblibertine:${LD_LIBRARY_PATH};CMAKE_BINARY_DIR=${CMAKE_BINARY_DIR};PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}:${CMAKE_SOURCE_DIR}/python;CMAKE_SOURCE_DIR=${CMAKE_SOURCE_DIR}") |
1111 | +endif() |
1112 | |
1113 | === modified file 'tools/libertine-container-manager' |
1114 | --- tools/libertine-container-manager 2016-06-20 15:15:38 +0000 |
1115 | +++ tools/libertine-container-manager 2016-07-07 20:14:07 +0000 |
1116 | @@ -17,637 +17,270 @@ |
1117 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
1118 | |
1119 | import argparse |
1120 | -import fcntl |
1121 | -import json |
1122 | import libertine.utils |
1123 | -import lsb_release |
1124 | import getpass |
1125 | import os |
1126 | -import platform |
1127 | import sys |
1128 | |
1129 | from apt.debfile import DebPackage |
1130 | -from distro_info import UbuntuDistroInfo, DistroDataOutdated |
1131 | from libertine import LibertineContainer |
1132 | - |
1133 | - |
1134 | -def find_duplicate_container_entry(container_list, container_id): |
1135 | - for container in container_list['containerList']: |
1136 | - if container['id'] == container_id: |
1137 | - return container |
1138 | - |
1139 | - return None |
1140 | - |
1141 | - |
1142 | -def merge_container_config_files(filepath): |
1143 | - merged_json = [] |
1144 | - main_config = read_container_config_file() |
1145 | - |
1146 | - with open(filepath, 'r') as fd: |
1147 | - merge_source = json.load(fd) |
1148 | - |
1149 | - if "containerList" in main_config: |
1150 | - # Finds any duplicate entries and assumes we want to update the main config |
1151 | - # with entries from the merge source. |
1152 | - for i, container in enumerate(main_config['containerList']): |
1153 | - merge_container = find_duplicate_container_entry(merge_source, container['id']) |
1154 | - if merge_container: |
1155 | - main_config['containerList'][i] = merge_container |
1156 | - merge_source['containerList'].remove(merge_container) |
1157 | - |
1158 | - # Merges in any remaining non-duplicate entries. |
1159 | - for container in merge_source['containerList']: |
1160 | - main_config['containerList'].append(container) |
1161 | - |
1162 | - else: |
1163 | - main_config = merge_source |
1164 | - |
1165 | - write_container_config_file(main_config) |
1166 | - |
1167 | - |
1168 | -def read_container_config_file(): |
1169 | - container_list = {} |
1170 | - container_config_file = libertine.utils.get_libertine_database_file_path() |
1171 | - |
1172 | - if (os.path.exists(container_config_file) and |
1173 | - os.path.getsize(container_config_file) != 0): |
1174 | - with open(container_config_file, 'r') as fd: |
1175 | - container_list = json.load(fd) |
1176 | - |
1177 | - return container_list |
1178 | - |
1179 | - |
1180 | -def write_container_config_file(container_list): |
1181 | - container_config_file = libertine.utils.get_libertine_database_file_path() |
1182 | - |
1183 | - with open(container_config_file, 'w') as fd: |
1184 | - fcntl.lockf(fd, fcntl.LOCK_EX) |
1185 | - json.dump(container_list, fd, sort_keys=True, indent=4) |
1186 | - fd.write('\n') |
1187 | - fcntl.lockf(fd, fcntl.LOCK_UN) |
1188 | - |
1189 | - |
1190 | -def get_default_container_id(): |
1191 | - default_container_id = None |
1192 | - |
1193 | - container_list = read_container_config_file() |
1194 | - |
1195 | - if "defaultContainer" in container_list: |
1196 | - default_container_id = container_list['defaultContainer'] |
1197 | - |
1198 | - return default_container_id |
1199 | - |
1200 | - |
1201 | -def select_container_type(): |
1202 | - kernel_release = platform.release().split('.') |
1203 | - |
1204 | - if int(kernel_release[0]) >= 4: |
1205 | - return "lxc" |
1206 | - elif int(kernel_release[0]) == 3 and int(kernel_release[1]) >= 13: |
1207 | - return "lxc" |
1208 | - else: |
1209 | - return "chroot" |
1210 | - |
1211 | - |
1212 | -def get_host_distro_release(): |
1213 | - distinfo = lsb_release.get_distro_information() |
1214 | - |
1215 | - return distinfo.get('CODENAME', 'n/a') |
1216 | - |
1217 | - |
1218 | -def is_distro_valid(distro, force): |
1219 | - if force: |
1220 | - return UbuntuDistroInfo().valid(distro) |
1221 | - |
1222 | - if distro == get_host_distro_release(): |
1223 | - return True |
1224 | - |
1225 | - supported_distros = UbuntuDistroInfo().supported() |
1226 | - |
1227 | - try: |
1228 | - supported_distros.index(distro) |
1229 | - except ValueError: |
1230 | - return False |
1231 | - |
1232 | - return True |
1233 | - |
1234 | - |
1235 | -def get_distro_codename(distro): |
1236 | - ubuntu_distro_info = UbuntuDistroInfo() |
1237 | - |
1238 | - for row in ubuntu_distro_info._rows: |
1239 | - if row['series'] == distro: |
1240 | - return row['codename'] |
1241 | - |
1242 | - return None |
1243 | - |
1244 | - |
1245 | -def update_container_install_status(container_id, new_status): |
1246 | - container_list = read_container_config_file() |
1247 | - |
1248 | - for container in container_list['containerList']: |
1249 | - if container['id'] == container_id: |
1250 | - container['installStatus'] = new_status |
1251 | - |
1252 | - write_container_config_file(container_list) |
1253 | - break |
1254 | - |
1255 | - |
1256 | -def update_container_multiarch_support(container_id, multiarch): |
1257 | - container_list = read_container_config_file() |
1258 | - |
1259 | - if multiarch == 'enabled' and libertine.utils.get_host_architecture() == 'i386': |
1260 | - multiarch = 'disabled' |
1261 | - |
1262 | - for container in container_list['containerList']: |
1263 | - if container['id'] == container_id: |
1264 | - container['multiarch'] = multiarch |
1265 | - |
1266 | - write_container_config_file(container_list) |
1267 | - break |
1268 | - |
1269 | - |
1270 | -def get_container_multiarch_support(container_id): |
1271 | - container_list = read_container_config_file() |
1272 | - |
1273 | - for container in container_list['containerList']: |
1274 | - if container['id'] == container_id: |
1275 | - if not 'multiarch' in container: |
1276 | - return 'disabled' |
1277 | - else: |
1278 | - return container['multiarch'] |
1279 | - |
1280 | - |
1281 | -def update_archive_install_status(container_id, archive_name, new_status): |
1282 | - container_list = read_container_config_file() |
1283 | - |
1284 | - for container in container_list['containerList']: |
1285 | - if container['id'] == container_id: |
1286 | - for archive in container['extraArchives']: |
1287 | - if archive['archiveName'] == archive_name: |
1288 | - archive['archiveStatus'] = new_status |
1289 | - write_container_config_file(container_list) |
1290 | - return |
1291 | - |
1292 | - |
1293 | -def add_container_archive(container_id, archive_name): |
1294 | - container_list = read_container_config_file() |
1295 | - |
1296 | - for container in container_list['containerList']: |
1297 | - if container['id'] == container_id: |
1298 | - archive_obj = {'archiveName': archive_name, 'archiveStatus': 'new'} |
1299 | - |
1300 | - if 'extraArchives' not in container: |
1301 | - container['extraArchives'] = [archive_obj] |
1302 | - else: |
1303 | - container['extraArchives'].append(archive_obj) |
1304 | - |
1305 | - write_container_config_file(container_list) |
1306 | - break |
1307 | - |
1308 | - |
1309 | -def delete_container_archive(container_id, archive_name): |
1310 | - container_list = read_container_config_file() |
1311 | - |
1312 | - for container in container_list['containerList']: |
1313 | - if container['id'] == container_id: |
1314 | - for archive in container['extraArchives']: |
1315 | - if archive['archiveName'] == archive_name: |
1316 | - container['extraArchives'].remove(archive) |
1317 | - write_container_config_file(container_list) |
1318 | - return |
1319 | - |
1320 | - print("%s does not exist." % archive_name) |
1321 | - sys.exit(1) |
1322 | - |
1323 | - |
1324 | -def archive_exists(container_id, archive_name): |
1325 | - container_list = read_container_config_file() |
1326 | - |
1327 | - for container in container_list['containerList']: |
1328 | - if container['id'] == container_id: |
1329 | - if 'extraArchives' not in container: |
1330 | - return False |
1331 | - else: |
1332 | - for archive in container['extraArchives']: |
1333 | - if archive['archiveName'] == archive_name: |
1334 | - return True |
1335 | - |
1336 | - return False |
1337 | - |
1338 | - |
1339 | -def add_new_container(id, name, type, distro): |
1340 | - if not os.path.exists(libertine.utils.get_libertine_database_dir_path()): |
1341 | - os.makedirs(libertine.utils.get_libertine_database_dir_path()) |
1342 | - |
1343 | - container_list = read_container_config_file() |
1344 | - |
1345 | - container_obj = {'id': id, 'installStatus': 'new', 'type': type, |
1346 | - 'distro': distro, 'name': name, 'installedApps': []} |
1347 | - |
1348 | - if "defaultContainer" not in container_list: |
1349 | - container_list['defaultContainer'] = id |
1350 | - |
1351 | - if "containerList" not in container_list: |
1352 | - container_list['containerList'] = [container_obj] |
1353 | - else: |
1354 | - container_list['containerList'].append(container_obj) |
1355 | - |
1356 | - write_container_config_file(container_list) |
1357 | - |
1358 | - |
1359 | -def delete_container(container_id): |
1360 | - container_list = read_container_config_file() |
1361 | - |
1362 | - if not container_list: |
1363 | - print("Unable to delete container. No containers defined.") |
1364 | - sys.exit(1) |
1365 | - |
1366 | - for container in container_list['containerList']: |
1367 | - if container['id'] == container_id: |
1368 | - container_list['containerList'].remove(container) |
1369 | - |
1370 | - # Set a new defaultContainer if the current default is being deleted. |
1371 | - if container_list['defaultContainer'] == container_id and container_list['containerList']: |
1372 | - container_list['defaultContainer'] = container_list['containerList'][0]['id'] |
1373 | - # Remove the defaultContainer if there are no more containers left. |
1374 | - elif not container_list['containerList']: |
1375 | - del container_list['defaultContainer'] |
1376 | - |
1377 | - write_container_config_file(container_list) |
1378 | - break |
1379 | - |
1380 | - |
1381 | -def package_exists(container_id, package_name): |
1382 | - container_list = read_container_config_file() |
1383 | - |
1384 | - if not container_list: |
1385 | - return False |
1386 | - |
1387 | - for container in container_list['containerList']: |
1388 | - if container['id'] == container_id: |
1389 | - for package in container['installedApps']: |
1390 | - if package['packageName'] == package_name: |
1391 | - return True |
1392 | - |
1393 | - return False |
1394 | - |
1395 | - |
1396 | -def update_package_install_status(container_id, package_name, new_status): |
1397 | - container_list = read_container_config_file() |
1398 | - |
1399 | - for container in container_list['containerList']: |
1400 | - if container['id'] == container_id: |
1401 | - for package in container['installedApps']: |
1402 | - if package['packageName'] == package_name: |
1403 | - package['appStatus'] = new_status |
1404 | - write_container_config_file(container_list) |
1405 | - return |
1406 | - |
1407 | - |
1408 | -def get_package_install_status(container_id, package_name): |
1409 | - container_list = read_container_config_file() |
1410 | - |
1411 | - for container in container_list['containerList']: |
1412 | - if container['id'] == container_id: |
1413 | - for package in container['installedApps']: |
1414 | - if package['packageName'] == package_name: |
1415 | - return package['appStatus'] |
1416 | - |
1417 | - |
1418 | -def add_new_package(container_id, package_name): |
1419 | - container_list = read_container_config_file() |
1420 | - |
1421 | - if not container_list: |
1422 | - print("No containers defined. Please create a new container before installing a package.") |
1423 | - sys.exit(1) |
1424 | - |
1425 | - for container in container_list['containerList']: |
1426 | - if container['id'] == container_id: |
1427 | - package_obj = {'packageName': package_name, 'appStatus': 'new'} |
1428 | - |
1429 | - if not container['installedApps']: |
1430 | - container['installedApps'] = [package_obj] |
1431 | - else: |
1432 | - container['installedApps'].append(package_obj) |
1433 | - |
1434 | - write_container_config_file(container_list) |
1435 | - break |
1436 | - |
1437 | - |
1438 | -def delete_package(container_id, package_name): |
1439 | - container_list = read_container_config_file() |
1440 | - |
1441 | - if not container_list: |
1442 | - print("No containers defined. Please create a new container before installing a package.") |
1443 | - sys.exit(1) |
1444 | - |
1445 | - for container in container_list['containerList']: |
1446 | - if container['id'] == container_id: |
1447 | - for package in container['installedApps']: |
1448 | - if package['packageName'] == package_name: |
1449 | - container['installedApps'].remove(package) |
1450 | - write_container_config_file(container_list) |
1451 | - return |
1452 | - |
1453 | - print("Package \'%s\' does not exist." % package_name) |
1454 | - sys.exit(1) |
1455 | - |
1456 | - |
1457 | -def create(args): |
1458 | - password = None |
1459 | - |
1460 | - if args.distro and not is_distro_valid(args.distro, args.force): |
1461 | - print("Invalid distro %s" % args.distro, file=sys.stderr) |
1462 | - sys.exit(1) |
1463 | - |
1464 | - if args.id and libertine.utils.container_exists(args.id): |
1465 | - print("Container id \'%s\' is already used." % args.id, file=sys.stderr) |
1466 | - sys.exit(1) |
1467 | - elif not args.id: |
1468 | - args.id = get_unique_container_id(distro) |
1469 | - |
1470 | - if not args.type: |
1471 | - container_type = select_container_type() |
1472 | - else: |
1473 | - container_type = args.type |
1474 | - |
1475 | - if not args.distro: |
1476 | - args.distro = get_host_distro_release() |
1477 | - elif container_type == "chroot": |
1478 | - host_distro = get_host_distro_release() |
1479 | - |
1480 | - if args.distro != host_distro: |
1481 | - print("The container distribution needs to match the host ditribution for chroot" |
1482 | - " based containers. Please either use \'%s\' or omit the -d/--distro option." |
1483 | - % host_distro) |
1484 | - sys.exit(1) |
1485 | - |
1486 | - if not args.name: |
1487 | - args.name = "Ubuntu \'" + get_distro_codename(args.distro) + "\'" |
1488 | - |
1489 | - if container_type == "lxc": |
1490 | - if args.password: |
1491 | - password = args.password |
1492 | - elif sys.stdin.isatty(): |
1493 | - print("Your user password is required for creating a Libertine container.") |
1494 | - password = getpass.getpass() |
1495 | - else: |
1496 | - password = sys.stdin.readline().rstrip() |
1497 | - |
1498 | - add_new_container(args.id, args.name, container_type, args.distro) |
1499 | - |
1500 | - multiarch = 'disabled' |
1501 | - if args.multiarch: |
1502 | - multiarch = 'enabled' |
1503 | - update_container_multiarch_support(args.id, multiarch) |
1504 | - |
1505 | - container = LibertineContainer(args.id) |
1506 | - update_container_install_status(args.id, "installing") |
1507 | - if not container.create_libertine_container(password, args.multiarch, args.verbosity): |
1508 | - delete_container(args.id) |
1509 | - sys.exit(1) |
1510 | - update_container_install_status(args.id, "ready") |
1511 | - |
1512 | - libertine.utils.refresh_libertine_scope() |
1513 | - |
1514 | - |
1515 | -def destroy_container_by_id(id): |
1516 | - container = LibertineContainer(id) |
1517 | - update_container_install_status(id, "removing") |
1518 | - container.destroy_libertine_container() |
1519 | - update_container_install_status(id, "removed") |
1520 | - delete_container(id) |
1521 | - |
1522 | - |
1523 | -def destroy(args): |
1524 | - if args.id and not libertine.utils.container_exists(args.id): |
1525 | - print("Container id \'%s\' does not exist." % args.id, file=sys.stderr) |
1526 | - sys.exit(1) |
1527 | - elif not args.id: |
1528 | - args.id = get_default_container_id() |
1529 | - |
1530 | - destroy_container_by_id(args.id) |
1531 | - |
1532 | - libertine.utils.refresh_libertine_scope() |
1533 | - |
1534 | - |
1535 | -def install_package(args): |
1536 | - if args.id and not libertine.utils.container_exists(args.id): |
1537 | - print("Container id \'%s\' does not exist." % args.id, file=sys.stderr) |
1538 | - sys.exit(1) |
1539 | - elif not args.id: |
1540 | - args.id = get_default_container_id() |
1541 | - |
1542 | - is_debian_package = args.package.endswith('.deb') |
1543 | - |
1544 | - if is_debian_package: |
1545 | - if os.path.exists(args.package): |
1546 | - package = DebPackage(args.package).pkgname |
1547 | - else: |
1548 | - print("%s does not exist." % args.package) |
1549 | - sys.exit(1) |
1550 | - else: |
1551 | - package = args.package |
1552 | - |
1553 | - if package_exists(args.id, package): |
1554 | - if not is_debian_package: |
1555 | - print("Package \'%s\' is already installed." % package) |
1556 | - sys.exit(1) |
1557 | - else: |
1558 | - add_new_package(args.id, package) |
1559 | - |
1560 | - container = LibertineContainer(args.id) |
1561 | - |
1562 | - update_package_install_status(args.id, package, "installing") |
1563 | - if not container.install_package(args.package, args.verbosity, args.readline): |
1564 | - delete_package(args.id, package) |
1565 | - sys.exit(1) |
1566 | - |
1567 | - update_package_install_status(args.id, package, "installed") |
1568 | - |
1569 | - libertine.utils.refresh_libertine_scope() |
1570 | - |
1571 | - |
1572 | -def remove_package_by_name(container_id, package_name, verbosity=1, readline=False): |
1573 | - fallback_status = get_package_install_status(container_id, package_name) |
1574 | - update_package_install_status(container_id, package_name, "removing") |
1575 | - |
1576 | - container = LibertineContainer(container_id) |
1577 | - if not container.remove_package(package_name, verbosity, readline): |
1578 | - update_package_install_status(container_id, package_name, fallback_status) |
1579 | - return False |
1580 | - |
1581 | - update_package_install_status(container_id, package_name, "removed") |
1582 | - delete_package(container_id, package_name) |
1583 | - |
1584 | - return True |
1585 | - |
1586 | - |
1587 | -def remove_package(args): |
1588 | - if args.id and not libertine.utils.container_exists(args.id): |
1589 | - print("Container id \'%s\' does not exist." % args.id, file=sys.stderr) |
1590 | - sys.exit(1) |
1591 | - elif not args.id: |
1592 | - args.id = get_default_container_id() |
1593 | - |
1594 | - if get_package_install_status(args.id, args.package) != 'installed': |
1595 | - print("Package \'%s\' is not installed." % args.package) |
1596 | - sys.exit(1) |
1597 | - |
1598 | - if not remove_package_by_name(args.id, args.package, args.verbosity, args.readline): |
1599 | - sys.exit(1) |
1600 | - |
1601 | - libertine.utils.refresh_libertine_scope() |
1602 | - |
1603 | - |
1604 | -def search_cache(args): |
1605 | - if args.id and not libertine.utils.container_exists(args.id): |
1606 | - print("Container id \'%s\' does not exist." % args.id, file=sys.stderr) |
1607 | - sys.exit(1) |
1608 | - elif not args.id: |
1609 | - args.id = get_default_container_id() |
1610 | - |
1611 | - container = LibertineContainer(args.id) |
1612 | - if container.search_package_cache(args.search_string) is not 0: |
1613 | - sys.exit(1) |
1614 | - |
1615 | - |
1616 | -def update(args): |
1617 | - if args.id and not libertine.utils.container_exists(args.id): |
1618 | - print("Container id \'%s\' does not exist." % args.id, file=sys.stderr) |
1619 | - sys.exit(1) |
1620 | - elif not args.id: |
1621 | - args.id = get_default_container_id() |
1622 | - |
1623 | - container = LibertineContainer(args.id) |
1624 | - |
1625 | - update_container_install_status(args.id, "updating") |
1626 | - if container.update_libertine_container(args.verbosity) is not 0: |
1627 | - update_container_install_status(args.id, "ready") |
1628 | - sys.exit(1) |
1629 | - |
1630 | - update_container_install_status(args.id, "ready") |
1631 | - |
1632 | - |
1633 | -def list(args): |
1634 | - containers = libertine.utils.Libertine.list_containers() |
1635 | - for container in containers: |
1636 | - print("%s" % container) |
1637 | - |
1638 | - |
1639 | -def list_apps(args): |
1640 | - if args.id and not libertine.utils.container_exists(args.id): |
1641 | - print("Container id \'%s\' does not exist." % args.id, file=sys.stderr) |
1642 | - sys.exit(1) |
1643 | - elif not args.id: |
1644 | - args.id = get_default_container_id() |
1645 | - |
1646 | - container = LibertineContainer(args.id) |
1647 | - print(container.list_app_launchers(use_json=args.json)) |
1648 | - |
1649 | - |
1650 | -def exec(args): |
1651 | - if args.id and not libertine.utils.container_exists(args.id): |
1652 | - print("Container id \'%s\' does not exist." % args.id, file=sys.stderr) |
1653 | - sys.exit(1) |
1654 | - elif not args.id: |
1655 | - args.id = get_default_container_id() |
1656 | - |
1657 | - container = LibertineContainer(args.id) |
1658 | - |
1659 | - if not container.exec_command(args.command): |
1660 | - sys.exit(1) |
1661 | - |
1662 | - |
1663 | -def delete_archive_by_name(container_id, archive_name): |
1664 | - update_archive_install_status(container_id, archive_name, 'removing') |
1665 | - if LibertineContainer(container_id).configure_command('delete-archive', archive_name) is not 0: |
1666 | - return False |
1667 | - delete_container_archive(container_id, archive_name) |
1668 | - return True |
1669 | - |
1670 | - |
1671 | -def configure(args): |
1672 | - if args.id and not libertine.utils.container_exists(args.id): |
1673 | - print("Container id \'%s\' does not exist." % args.id, file=sys.stderr) |
1674 | - sys.exit(1) |
1675 | - elif not args.id: |
1676 | - args.id = get_default_container_id() |
1677 | - |
1678 | - container = LibertineContainer(args.id) |
1679 | - |
1680 | - if args.multiarch and libertine.utils.get_host_architecture() == 'amd64': |
1681 | +from libertine.ContainersConfig import ContainersConfig |
1682 | +from libertine.HostInfo import HostInfo |
1683 | + |
1684 | + |
1685 | +class LibertineContainerManager(object): |
1686 | + |
1687 | + def __init__(self): |
1688 | + self.containers_config = ContainersConfig() |
1689 | + self.host_info = HostInfo() |
1690 | + |
1691 | + def create(self, args): |
1692 | + password = None |
1693 | + |
1694 | + if args.distro and not self.host_info.is_distro_valid(args.distro, args.force): |
1695 | + print("Invalid distro %s" % args.distro, file=sys.stderr) |
1696 | + sys.exit(1) |
1697 | + |
1698 | + if args.id and self.containers_config.container_exists(args.id): |
1699 | + print("Container id \'%s\' is already used." % args.id, file=sys.stderr) |
1700 | + sys.exit(1) |
1701 | + elif not args.id: |
1702 | + args.id = get_unique_container_id(distro) |
1703 | + |
1704 | + if not args.type: |
1705 | + container_type = self.host_info.select_container_type_by_kernel() |
1706 | + else: |
1707 | + if args.type == 'lxc' and not self.host_info.has_lxc_support(): |
1708 | + print("System kernel does not support lxc type containers. " |
1709 | + "Please either use chroot or omit the -t option.") |
1710 | + sys.exit(1) |
1711 | + container_type = args.type |
1712 | + |
1713 | + if not args.distro: |
1714 | + args.distro = self.host_info.get_host_distro_release() |
1715 | + elif container_type == "chroot": |
1716 | + host_distro = self.host_info.get_host_distro_release() |
1717 | + |
1718 | + if args.distro != host_distro: |
1719 | + print("The container distribution needs to match the host ditribution for chroot" |
1720 | + " based containers. Please either use \'%s\' or omit the -d/--distro option." |
1721 | + % host_distro) |
1722 | + sys.exit(1) |
1723 | + |
1724 | + if not args.name: |
1725 | + args.name = "Ubuntu \'" + self.host_info.get_distro_codename(args.distro) + "\'" |
1726 | + |
1727 | + if container_type == "lxc": |
1728 | + if args.password: |
1729 | + password = args.password |
1730 | + elif sys.stdin.isatty(): |
1731 | + print("Your user password is required for creating a Libertine container.") |
1732 | + password = getpass.getpass() |
1733 | + else: |
1734 | + password = sys.stdin.readline().rstrip() |
1735 | + |
1736 | + self.containers_config.add_new_container(args.id, args.name, container_type, args.distro) |
1737 | + |
1738 | multiarch = 'disabled' |
1739 | if args.multiarch == 'enable': |
1740 | multiarch = 'enabled' |
1741 | - |
1742 | - current_multiarch = get_container_multiarch_support(args.id) |
1743 | - if current_multiarch == multiarch: |
1744 | - print("i386 multiarch support is already %s" % multiarch) |
1745 | - sys.exit(1) |
1746 | - |
1747 | - if container.configure_command('multiarch', args.multiarch) is not 0: |
1748 | - sys.exit(1) |
1749 | - |
1750 | - update_container_multiarch_support(args.id, multiarch) |
1751 | - |
1752 | - elif args.add_archive: |
1753 | - if archive_exists(args.id, args.add_archive): |
1754 | - print("%s already added in container." % args.add_archive) |
1755 | - sys.exit(1) |
1756 | - |
1757 | - add_container_archive(args.id, args.add_archive) |
1758 | - update_archive_install_status(args.id, args.add_archive, 'installing') |
1759 | - if container.configure_command('add-archive', args.add_archive) is not 0: |
1760 | - delete_container_archive(args.id, args.add_archive) |
1761 | - sys.exit(1) |
1762 | - |
1763 | - update_archive_install_status(args.id, args.add_archive, 'installed') |
1764 | - |
1765 | - elif args.delete_archive: |
1766 | - if not archive_exists(args.id, args.delete_archive): |
1767 | - print("%s is not added in container." % args.delete_archive) |
1768 | - sys.exit(1) |
1769 | - |
1770 | - if not delete_archive_by_name(args.id, args.delete_archive): |
1771 | - sys.exit(1) |
1772 | - |
1773 | - |
1774 | -def merge(args): |
1775 | - merge_container_config_files(args.file) |
1776 | - |
1777 | - |
1778 | -def fix_integrity(args): |
1779 | - for container in read_container_config_file()['containerList']: |
1780 | - if container['installStatus'] != 'ready': |
1781 | - destroy_container_by_id(container['id']) |
1782 | - continue |
1783 | - LibertineContainer(container['id']).exec_command('dpkg --configure -a') |
1784 | - |
1785 | - for package in container['installedApps']: |
1786 | - if package['appStatus'] != 'installed': |
1787 | - remove_package_by_name(container['id'], package['packageName']) |
1788 | - if 'extraArchives' in container: |
1789 | - for archive in container['extraArchives']: |
1790 | - if archive['archiveStatus'] != 'installed': |
1791 | - delete_archive_by_name(container['id'], archive['archiveName']) |
1792 | - |
1793 | - |
1794 | -def set_default(args): |
1795 | - if args.clear: |
1796 | - container_list = read_container_config_file() |
1797 | - container_list.pop('defaultContainer', None) |
1798 | - write_container_config_file(container_list) |
1799 | - sys.exit(0) |
1800 | - |
1801 | - if not libertine.utils.container_exists(args.id): |
1802 | - print("Container id \'%s\' does not exist." % args.id, file=sys.stderr) |
1803 | - sys.exit(1) |
1804 | - |
1805 | - container_list = read_container_config_file() |
1806 | - container_list['defaultContainer'] = args.id |
1807 | - write_container_config_file(container_list) |
1808 | + self.containers_config.update_container_multiarch_support(args.id, multiarch) |
1809 | + |
1810 | + container = LibertineContainer(args.id) |
1811 | + self.containers_config.update_container_install_status(args.id, "installing") |
1812 | + if not container.create_libertine_container(password, args.multiarch, args.verbosity): |
1813 | + self.containers_config.delete_container(args.id) |
1814 | + sys.exit(1) |
1815 | + self.containers_config.update_container_install_status(args.id, "ready") |
1816 | + |
1817 | + libertine.utils.refresh_libertine_scope() |
1818 | + |
1819 | + def destroy_container_by_id(self, id): |
1820 | + container = LibertineContainer(id) |
1821 | + |
1822 | + self.containers_config.update_container_install_status(id, "removing") |
1823 | + container.destroy_libertine_container() |
1824 | + self.containers_config.update_container_install_status(id, "removed") |
1825 | + self.containers_config.delete_container(id) |
1826 | + |
1827 | + def destroy(self, args): |
1828 | + args.id = self.containers_config.check_container_id(args.id) |
1829 | + |
1830 | + self.destroy_container_by_id(args.id) |
1831 | + |
1832 | + libertine.utils.refresh_libertine_scope() |
1833 | + |
1834 | + def install_package(self, args): |
1835 | + container_id = self.containers_config.check_container_id(args.id) |
1836 | + |
1837 | + is_debian_package = args.package.endswith('.deb') |
1838 | + |
1839 | + if is_debian_package: |
1840 | + if os.path.exists(args.package): |
1841 | + package = DebPackage(args.package).pkgname |
1842 | + else: |
1843 | + print("%s does not exist." % args.package) |
1844 | + sys.exit(1) |
1845 | + else: |
1846 | + package = args.package |
1847 | + |
1848 | + if self.containers_config.package_exists(container_id, package): |
1849 | + if not is_debian_package: |
1850 | + print("Package \'%s\' is already installed." % package) |
1851 | + sys.exit(1) |
1852 | + else: |
1853 | + self.containers_config.add_new_package(container_id, package) |
1854 | + |
1855 | + container = LibertineContainer(container_id) |
1856 | + |
1857 | + self.containers_config.update_package_install_status(container_id, package, "installing") |
1858 | + if not container.install_package(args.package, args.verbosity, args.readline): |
1859 | + self.containers_config.delete_package(container_id, package) |
1860 | + sys.exit(1) |
1861 | + |
1862 | + self.containers_config.update_package_install_status(container_id, package, "installed") |
1863 | + |
1864 | + libertine.utils.refresh_libertine_scope() |
1865 | + |
1866 | + def remove_package_by_name(self, container_id, package_name, verbosity=1, readline=False): |
1867 | + fallback_status = self.containers_config.get_package_install_status(container_id, package_name) |
1868 | + self.containers_config.update_package_install_status(container_id, package_name, "removing") |
1869 | + |
1870 | + container = LibertineContainer(container_id) |
1871 | + if not container.remove_package(package_name, verbosity, readline) and fallback_status == 'installed': |
1872 | + self.containers_config.update_package_install_status(container_id, package_name, fallback_status) |
1873 | + return False |
1874 | + |
1875 | + self.containers_config.update_package_install_status(container_id, package_name, "removed") |
1876 | + self.containers_config.delete_package(container_id, package_name) |
1877 | + |
1878 | + return True |
1879 | + |
1880 | + def remove_package(self, args): |
1881 | + container_id = self.containers_config.check_container_id(args.id) |
1882 | + |
1883 | + if self.containers_config.get_package_install_status(container_id, args.package) != 'installed': |
1884 | + print("Package \'%s\' is not installed." % args.package) |
1885 | + sys.exit(1) |
1886 | + |
1887 | + if not self.remove_package_by_name(container_id, args.package, args.verbosity, args.readline): |
1888 | + sys.exit(1) |
1889 | + |
1890 | + libertine.utils.refresh_libertine_scope() |
1891 | + |
1892 | + def search_cache(self, args): |
1893 | + container_id = self.containers_config.check_container_id(args.id) |
1894 | + |
1895 | + container = LibertineContainer(container_id) |
1896 | + if container.search_package_cache(args.search_string) is not 0: |
1897 | + sys.exit(1) |
1898 | + |
1899 | + def update(self, args): |
1900 | + container_id = self.containers_config.check_container_id(args.id) |
1901 | + |
1902 | + container = LibertineContainer(container_id) |
1903 | + |
1904 | + self.containers_config.update_container_install_status(container_id, "updating") |
1905 | + if container.update_libertine_container(args.verbosity) is not 0: |
1906 | + self.containers_config.update_container_install_status(container_id, "ready") |
1907 | + sys.exit(1) |
1908 | + |
1909 | + self.containers_config.update_container_install_status(container_id, "ready") |
1910 | + |
1911 | + def list(self, args): |
1912 | + containers = libertine.utils.Libertine.list_containers() |
1913 | + for container in containers: |
1914 | + print("%s" % container) |
1915 | + |
1916 | + def list_apps(self, args): |
1917 | + container_id = self.containers_config.check_container_id(args.id) |
1918 | + |
1919 | + container = LibertineContainer(container_id) |
1920 | + print(container.list_app_launchers(use_json=args.json)) |
1921 | + |
1922 | + def exec(self, args): |
1923 | + container_id = self.containers_config.check_container_id(args.id) |
1924 | + |
1925 | + container = LibertineContainer(container_id) |
1926 | + |
1927 | + if not container.exec_command(args.command): |
1928 | + sys.exit(1) |
1929 | + |
1930 | + def delete_archive_by_name(self, container_id, archive_name): |
1931 | + self.containers_config.update_archive_install_status(container_id, archive_name, 'removing') |
1932 | + if LibertineContainer(container_id).configure_command('delete-archive', archive_name) is not 0: |
1933 | + return False |
1934 | + self.containers_config.delete_container_archive(container_id, archive_name) |
1935 | + return True |
1936 | + |
1937 | + def configure(self, args): |
1938 | + container_id = self.containers_config.check_container_id(args.id) |
1939 | + |
1940 | + container = LibertineContainer(container_id) |
1941 | + |
1942 | + if args.multiarch and self.host_info.get_host_architecture() == 'amd64': |
1943 | + multiarch = 'disabled' |
1944 | + if args.multiarch == 'enable': |
1945 | + multiarch = 'enabled' |
1946 | + |
1947 | + current_multiarch = self.containers_config.get_container_multiarch_support(container_id) |
1948 | + if current_multiarch == multiarch: |
1949 | + print("i386 multiarch support is already %s" % multiarch) |
1950 | + sys.exit(1) |
1951 | + |
1952 | + if container.configure_command('multiarch', args.multiarch) is not 0: |
1953 | + sys.exit(1) |
1954 | + |
1955 | + self.containers_config.update_container_multiarch_support(container_id, multiarch) |
1956 | + |
1957 | + elif args.add_archive: |
1958 | + if self.containers_config.archive_exists(container_id, args.add_archive): |
1959 | + print("%s already added in container." % args.add_archive) |
1960 | + sys.exit(1) |
1961 | + |
1962 | + self.containers_config.add_container_archive(container_id, args.add_archive) |
1963 | + self.containers_config.update_archive_install_status(container_id, args.add_archive, 'installing') |
1964 | + if container.configure_command('add-archive', args.add_archive) is not 0: |
1965 | + self.containers_config.delete_container_archive(container_id, args.add_archive) |
1966 | + sys.exit(1) |
1967 | + |
1968 | + self.containers_config.update_archive_install_status(container_id, args.add_archive, 'installed') |
1969 | + |
1970 | + elif args.delete_archive: |
1971 | + if not self.containers_config.archive_exists(container_id, args.delete_archive): |
1972 | + print("%s is not added in container." % args.delete_archive) |
1973 | + sys.exit(1) |
1974 | + |
1975 | + if not self.delete_archive_by_name(container_id, args.delete_archive): |
1976 | + sys.exit(1) |
1977 | + |
1978 | + def merge(self, args): |
1979 | + self.containers_config.merge_container_config_files(args.file) |
1980 | + |
1981 | + def fix_integrity(self, args): |
1982 | + for container in self.containers_config.container_list['containerList']: |
1983 | + if 'installStatus' not in container or container['installStatus'] != 'ready': |
1984 | + self.destroy_container_by_id(container['id']) |
1985 | + continue |
1986 | + LibertineContainer(container['id']).exec_command('dpkg --configure -a') |
1987 | + |
1988 | + for package in container['installedApps']: |
1989 | + if package['appStatus'] != 'installed': |
1990 | + self.remove_package_by_name(container['id'], package['packageName']) |
1991 | + |
1992 | + if 'extraArchives' in container: |
1993 | + for archive in container['extraArchives']: |
1994 | + if archive['archiveStatus'] != 'installed': |
1995 | + self.delete_archive_by_name(container['id'], archive['archiveName']) |
1996 | + |
1997 | + def set_default(self, args): |
1998 | + if args.clear: |
1999 | + self.containers_config.clear_default_container_id(True) |
2000 | + sys.exit(0) |
2001 | + |
2002 | + container_id = self.containers_config.check_container_id(args.id) |
2003 | + |
2004 | + self.containers_config.set_default_container_id(container_id, True) |
2005 | |
2006 | |
2007 | if __name__ == '__main__': |
2008 | @@ -657,6 +290,8 @@ |
2009 | print("Please do not run %s using sudo" % parser.prog) |
2010 | sys.exit(1) |
2011 | |
2012 | + container_manager = LibertineContainerManager() |
2013 | + |
2014 | parser.add_argument('-q', '--quiet', |
2015 | action='store_const', dest='verbosity', const=0, |
2016 | help=('do not print status updates on stdout')) |
2017 | @@ -696,7 +331,7 @@ |
2018 | '--password', |
2019 | help=("Pass in the user's password when creating an LXC container. This " |
2020 | "is intended for testing only and is very insecure.")) |
2021 | - parser_create.set_defaults(func=create) |
2022 | + parser_create.set_defaults(func=container_manager.create) |
2023 | |
2024 | # Handle the destroy command and its options |
2025 | parser_destroy = subparsers.add_parser( |
2026 | @@ -705,7 +340,7 @@ |
2027 | parser_destroy.add_argument( |
2028 | '-i', '--id', |
2029 | help=("Container identifier. Default container is used if omitted.")) |
2030 | - parser_destroy.set_defaults(func=destroy) |
2031 | + parser_destroy.set_defaults(func=container_manager.destroy) |
2032 | |
2033 | # Handle the install-package command and its options |
2034 | parser_install = subparsers.add_parser( |
2035 | @@ -721,7 +356,7 @@ |
2036 | parser_install.add_argument( |
2037 | '-r', '--readline', action='store_true', |
2038 | help=("Readline mode. Use text-based frontend during debconf interactions.")) |
2039 | - parser_install.set_defaults(func=install_package) |
2040 | + parser_install.set_defaults(func=container_manager.install_package) |
2041 | |
2042 | # Handle the remove-package command and its options |
2043 | parser_remove = subparsers.add_parser( |
2044 | @@ -737,7 +372,7 @@ |
2045 | parser_remove.add_argument( |
2046 | '-r', '--readline', action='store_true', |
2047 | help=("Readline mode. Use text-based frontend during debconf interactions.")) |
2048 | - parser_remove.set_defaults(func=remove_package) |
2049 | + parser_remove.set_defaults(func=container_manager.remove_package) |
2050 | |
2051 | # Handle the search-cache command and its options |
2052 | parser_search = subparsers.add_parser( |
2053 | @@ -750,7 +385,7 @@ |
2054 | parser_search.add_argument( |
2055 | '-i', '--id', |
2056 | help=("Container identifier. Default container is used if omitted.")) |
2057 | - parser_search.set_defaults(func=search_cache) |
2058 | + parser_search.set_defaults(func=container_manager.search_cache) |
2059 | |
2060 | # Handle the update command and its options |
2061 | parser_update = subparsers.add_parser( |
2062 | @@ -759,13 +394,13 @@ |
2063 | parser_update.add_argument( |
2064 | '-i', '--id', |
2065 | help=("Container identifier. Default container is used if omitted.")) |
2066 | - parser_update.set_defaults(func=update) |
2067 | + parser_update.set_defaults(func=container_manager.update) |
2068 | |
2069 | # Handle the list command |
2070 | parser_list = subparsers.add_parser( |
2071 | "list", |
2072 | help=("List all Libertine containers.")) |
2073 | - parser_list.set_defaults(func=list) |
2074 | + parser_list.set_defaults(func=container_manager.list) |
2075 | |
2076 | # Handle the list-apps command and its options |
2077 | parser_list_apps = subparsers.add_parser( |
2078 | @@ -778,7 +413,7 @@ |
2079 | '-j', '--json', |
2080 | action='store_true', |
2081 | help=("use JSON output format.")) |
2082 | - parser_list_apps.set_defaults(func=list_apps) |
2083 | + parser_list_apps.set_defaults(func=container_manager.list_apps) |
2084 | |
2085 | # Handle the execute command and it's options |
2086 | parser_exec = subparsers.add_parser( |
2087 | @@ -791,7 +426,7 @@ |
2088 | parser_exec.add_argument( |
2089 | '-c', '--command', |
2090 | help=("The command to run in the specified container.")) |
2091 | - parser_exec.set_defaults(func=exec) |
2092 | + parser_exec.set_defaults(func=container_manager.exec) |
2093 | |
2094 | # Handle the configure command and it's options |
2095 | parser_configure = subparsers.add_parser( |
2096 | @@ -816,7 +451,7 @@ |
2097 | metavar='Archive name', |
2098 | help=("Deletes an existing archive (PPA) in the specified Libertine container. " |
2099 | "Needs to be in the form of \"ppa:user/ppa-name\".")) |
2100 | - parser_configure.set_defaults(func=configure) |
2101 | + parser_configure.set_defaults(func=container_manager.configure) |
2102 | |
2103 | # Handle merging another ContainersConfig.json file into the main ContainersConfig.json file |
2104 | parser_merge = subparsers.add_parser( |
2105 | @@ -825,13 +460,13 @@ |
2106 | parser_merge.add_argument( |
2107 | '-f', '--file', |
2108 | required=True) |
2109 | - parser_merge.set_defaults(func=merge) |
2110 | + parser_merge.set_defaults(func=container_manager.merge) |
2111 | |
2112 | # Indiscriminately destroy containers, packages, and archives which are not fully installed |
2113 | parser_integrity = subparsers.add_parser( |
2114 | 'fix-integrity', |
2115 | add_help=False) |
2116 | - parser_integrity.set_defaults(func=fix_integrity) |
2117 | + parser_integrity.set_defaults(func=container_manager.fix_integrity) |
2118 | |
2119 | # Set the default container in ContainersConfig |
2120 | parser_default = subparsers.add_parser( |
2121 | @@ -844,7 +479,7 @@ |
2122 | parser_default.add_argument( |
2123 | '-c', '--clear', action='store_true', |
2124 | help=("Clear the default container.")) |
2125 | - parser_default.set_defaults(func=set_default) |
2126 | + parser_default.set_defaults(func=container_manager.set_default) |
2127 | |
2128 | # Actually parse the args |
2129 | args = parser.parse_args() |
2130 | |
2131 | === modified file 'tools/libertine-launch' |
2132 | --- tools/libertine-launch 2016-05-10 20:17:44 +0000 |
2133 | +++ tools/libertine-launch 2016-07-07 20:14:07 +0000 |
2134 | @@ -18,21 +18,24 @@ |
2135 | |
2136 | import argparse |
2137 | import os |
2138 | +import random |
2139 | +import string |
2140 | import libertine.utils |
2141 | import psutil |
2142 | import shlex |
2143 | import time |
2144 | + |
2145 | from libertine import LibertineContainer |
2146 | |
2147 | |
2148 | def get_session_socket_path(session_socket_name): |
2149 | - unique_id = os.environ['DISPLAY'].strip(':') |
2150 | + unique_id = ''.join(random.choice(string.ascii_lowercase + string.digits) for i in range(8)) |
2151 | |
2152 | if not os.path.exists(libertine.utils.get_libertine_runtime_dir()): |
2153 | os.makedirs(libertine.utils.get_libertine_runtime_dir()) |
2154 | |
2155 | session_socket_path = ( |
2156 | - os.path.join(libertine.utils.get_libertine_runtime_dir(), session_socket_name + unique_id)) |
2157 | + os.path.join(libertine.utils.get_libertine_runtime_dir(), session_socket_name + '-' + unique_id)) |
2158 | |
2159 | return session_socket_path |
2160 | |
2161 | @@ -69,8 +72,8 @@ |
2162 | |
2163 | while not os.path.exists(session_socket_path): |
2164 | if retries >= 10: |
2165 | - raise RuntimeError("Timeout waiting for Libertine Dbus session bridge socket") |
2166 | - |
2167 | + raise RuntimeError("Timeout waiting for Libertine Dbus session bridge socket") |
2168 | + |
2169 | print("Libertine Dbus session bridge socket is not ready. Waiting...") |
2170 | retries += 1 |
2171 | time.sleep(.5) |
2172 | @@ -84,9 +87,6 @@ |
2173 | help='exec line') |
2174 | args = arg_parser.parse_args() |
2175 | |
2176 | - if not libertine.utils.container_exists(args.container_id): |
2177 | - raise RuntimeError("Container ID %s does not exist." % args.container_id) |
2178 | - |
2179 | # remove problematic environment variables |
2180 | for e in ['QT_QPA_PLATFORM', 'LD_LIBRARY_PATH', 'FAKECHROOT_BASE', 'FAKECHROOT_CMD_SUBST']: |
2181 | if e in os.environ: |
2182 | @@ -104,7 +104,7 @@ |
2183 | # should detect the maliit socket, but dont know if its around or not here. |
2184 | detect_session_bridge_socket(dbus_socket_path) |
2185 | |
2186 | - container = LibertineContainer(args.container_id) |
2187 | + container = LibertineContainer(args.container_id) |
2188 | |
2189 | try: |
2190 | container.launch_application(args.app_exec_line) |
2191 | |
2192 | === modified file 'tools/libertine-session-bridge' |
2193 | --- tools/libertine-session-bridge 2016-05-19 20:00:22 +0000 |
2194 | +++ tools/libertine-session-bridge 2016-07-07 20:14:07 +0000 |
2195 | @@ -170,6 +170,7 @@ |
2196 | except: |
2197 | container_session_sock.close() |
2198 | container_session_sock = None |
2199 | + os.remove(session_socket_path) |
2200 | raise |
2201 | else: |
2202 | host_session_socket_path_map.update({container_session_sock:host_session_path}) |
When creating a container, I get the following error:
``` libertine- container- manager" , line 492, in <module> libertine- container- manager" , line 51, in create info.select_ container_ type_by_ kernel( ) lrp/Projects/ 2016/libertine- 1.2.2/python/ libertine/ HostInfo. py", line 25, in select_ container_ type_by_ kernel
Traceback (most recent call last):
File "tools/
args.func(args)
File "tools/
container_type = self.host_
File "/home/
if has_lxc_support():
NameError: name 'has_lxc_support' is not defined
```
Probably missing a `self`? See minor diff comments.