Merge lp:~townsend/libertine/release-1.5 into lp:libertine/trunk
- release-1.5
- Merge into trunk
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Approved by: | Larry Price | ||||||||
Approved revision: | 182 | ||||||||
Merged at revision: | 179 | ||||||||
Proposed branch: | lp:~townsend/libertine/release-1.5 | ||||||||
Merge into: | lp:libertine/trunk | ||||||||
Diff against target: |
3383 lines (+1572/-672) 45 files modified
.bzrignore (+8/-0) CMakeLists.txt (+1/-1) common/ContainerConfigList.h (+1/-2) data/CMakeLists.txt (+4/-6) data/com.canonical.libertine.LxdManager.service (+3/-0) data/libertine-lxd-sudo (+1/-0) data/python3-libertine-chroot.click-hook.in (+0/-4) data/snap-runner.wrapper (+11/-0) debian/changelog (+20/-0) debian/control (+17/-4) debian/python3-libertine-chroot.install (+0/-1) debian/python3-libertine-lxd.install (+5/-0) debian/python3-libertine.install (+1/-0) debian/rules (+1/-1) liblibertine/libertine.cpp (+41/-26) parts/plugins/utils.py (+63/-0) parts/plugins/x-libertine-deps.py (+155/-0) parts/plugins/x-libertine.py (+45/-0) python/CMakeLists.txt (+1/-1) python/libertine/AppDiscovery.py (+1/-3) python/libertine/ChrootContainer.py (+30/-31) python/libertine/ContainersConfig.py (+3/-3) python/libertine/HostInfo.py (+5/-0) python/libertine/Libertine.py (+113/-125) python/libertine/LxcContainer.py (+67/-77) python/libertine/LxdContainer.py (+455/-0) python/libertine/launcher/session.py (+10/-13) python/libertine/launcher/task.py (+3/-3) python/libertine/lifecycle/ContainerLifecycleService.py (+108/-0) python/libertine/lifecycle/ContainerLifecycleServiceRunner.py (+46/-0) python/libertine/lifecycle/LifecycleResult.py (+37/-0) python/libertine/lifecycle/__init__.py (+23/-0) python/libertine/utils.py (+24/-3) snapcraft.yaml (+32/-0) tests/unit/test_libertine_gir.py (+2/-4) tests/unit/test_logger.py (+5/-1) tools/CMakeLists.txt (+4/-5) tools/libertine-container-manager (+55/-44) tools/libertine-container-manager.1 (+8/-2) tools/libertine-lxc-manager (+46/-123) tools/libertine-lxd-manager (+57/-0) tools/libertine-lxd-manager.1 (+9/-0) tools/libertine-lxd-setup (+40/-0) tools/libertined (+11/-6) tools/update-puritine-containers (+0/-183) |
||||||||
To merge this branch: | bzr merge lp:~townsend/libertine/release-1.5 | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Larry Price | Approve | ||
Review via email: mp+314179@code.launchpad.net |
Commit message
* Drop support for the Puritine click package as that is Vivid only and a Vivid Libertine branch exists for any future fixes.
* Only set the lxc log when a container is defined during class init. (LP: #1653973)
* Bump version to 1.5 for new upstream release.
* Logic for bundling libertine as a snap built from source.
* Catch exceptions raised during container creation.
* Initial implementation of lxd backend. (LP: #1580612)
* Use the libertine logger and LIBERTINE_DEBUG variable everywhere.
* Update configure bind-mount logic given new lxd backend.
* Use dpkg to find package name when installing local deb.
* Create d-bus service for lxd container management.
Description of the change
- 181. By Christopher Townsend
-
Update the version for the snap package as well.
- 182. By Larry Price
-
Merge from lp:libertine for fixes during testing and review.
Preview Diff
1 | === modified file '.bzrignore' |
2 | --- .bzrignore 2016-10-25 17:27:50 +0000 |
3 | +++ .bzrignore 2017-01-05 20:19:05 +0000 |
4 | @@ -2,3 +2,11 @@ |
5 | po/Makefile.in.in |
6 | __pycache__ |
7 | tests/unit/.cache |
8 | +*.click-hook |
9 | + |
10 | +prime/ |
11 | +stage/ |
12 | +*.snap |
13 | +parts/* |
14 | +!parts/plugins |
15 | +parts/plugins/__pycache__ |
16 | |
17 | === modified file 'CMakeLists.txt' |
18 | --- CMakeLists.txt 2016-11-03 18:28:54 +0000 |
19 | +++ CMakeLists.txt 2017-01-05 20:19:05 +0000 |
20 | @@ -2,7 +2,7 @@ |
21 | cmake_policy(SET CMP0048 NEW) |
22 | |
23 | project(libertine |
24 | - VERSION 1.4.3) |
25 | + VERSION 1.5) |
26 | |
27 | set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" "${CMAKE_MODULE_PATH}") |
28 | |
29 | |
30 | === modified file 'common/ContainerConfigList.h' |
31 | --- common/ContainerConfigList.h 2016-09-26 18:17:07 +0000 |
32 | +++ common/ContainerConfigList.h 2017-01-05 20:19:05 +0000 |
33 | @@ -55,11 +55,10 @@ |
34 | * Display roles for a container config. |
35 | */ |
36 | enum class DataRole |
37 | - : int |
38 | { |
39 | ContainerId = Qt::UserRole + 1, /**< The container ID */ |
40 | ContainerName, /**< The container name */ |
41 | - ContainerType, /**< The type of container - lxc or chroot */ |
42 | + ContainerType, /**< The type of container */ |
43 | DistroSeries, /**< The distro from which the container was built */ |
44 | InstallStatus, /**< Current container install status */ |
45 | Error /**< last role (error) */ |
46 | |
47 | === modified file 'data/CMakeLists.txt' |
48 | --- data/CMakeLists.txt 2016-11-01 20:10:58 +0000 |
49 | +++ data/CMakeLists.txt 2017-01-05 20:19:05 +0000 |
50 | @@ -6,11 +6,9 @@ |
51 | DESTINATION ${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME}) |
52 | install(FILES libertine-xmir.conf |
53 | DESTINATION ${CMAKE_INSTALL_DATADIR}/upstart/sessions) |
54 | -install(FILES libertine-lxc-sudo |
55 | +install(FILES libertine-lxc-sudo libertine-lxd-sudo |
56 | DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/sudoers.d) |
57 | -install(FILES com.canonical.libertine.LxcManager.service com.canonical.libertine.ContainerManager.service |
58 | +install(FILES com.canonical.libertine.ContainerManager.service |
59 | + com.canonical.libertine.LxcManager.service |
60 | + com.canonical.libertine.LxdManager.service |
61 | DESTINATION ${CMAKE_INSTALL_DATADIR}/dbus-1/services) |
62 | - |
63 | -configure_file("python3-libertine-chroot.click-hook.in" |
64 | - "${CMAKE_SOURCE_DIR}/debian/python3-libertine-chroot.click-hook" |
65 | - @ONLY) |
66 | |
67 | === added file 'data/com.canonical.libertine.LxdManager.service' |
68 | --- data/com.canonical.libertine.LxdManager.service 1970-01-01 00:00:00 +0000 |
69 | +++ data/com.canonical.libertine.LxdManager.service 2017-01-05 20:19:05 +0000 |
70 | @@ -0,0 +1,3 @@ |
71 | +[D-BUS Service] |
72 | +Name=com.canonical.libertine.LxdManager |
73 | +Exec=/usr/bin/libertine-lxd-manager |
74 | |
75 | === added file 'data/libertine-lxd-sudo' |
76 | --- data/libertine-lxd-sudo 1970-01-01 00:00:00 +0000 |
77 | +++ data/libertine-lxd-sudo 2017-01-05 20:19:05 +0000 |
78 | @@ -0,0 +1,1 @@ |
79 | +ALL ALL=(ALL) NOPASSWD:/usr/bin/libertine-lxd-setup |
80 | |
81 | === removed file 'data/python3-libertine-chroot.click-hook.in' |
82 | --- data/python3-libertine-chroot.click-hook.in 2016-09-06 12:36:47 +0000 |
83 | +++ data/python3-libertine-chroot.click-hook.in 1970-01-01 00:00:00 +0000 |
84 | @@ -1,4 +0,0 @@ |
85 | -Pattern: ${home}/.cache/libertine/puritine/${id} |
86 | -Exec: @CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libertine/update-puritine-containers |
87 | -User-Level: yes |
88 | -Hook-Name: puritine |
89 | |
90 | === added file 'data/snap-runner.wrapper' |
91 | --- data/snap-runner.wrapper 1970-01-01 00:00:00 +0000 |
92 | +++ data/snap-runner.wrapper 2017-01-05 20:19:05 +0000 |
93 | @@ -0,0 +1,11 @@ |
94 | +#!/bin/bash |
95 | +# additional env modifications on top of desktop-launch |
96 | + |
97 | +export LD_LIBRARY_PATH=$SNAP/usr/lib/$ARCH/fakechroot:$LD_LIBRARY_PATH |
98 | +export DEBOOTSTRAP_DIR=$SNAP/usr/share/debootstrap |
99 | + |
100 | +# Useful debug variables |
101 | +# export FAKECHROOT_DEBUG=1 |
102 | +# export LIBERTINE_DEBUG=1 |
103 | + |
104 | +exec $SNAP/bin/desktop-launch $@ |
105 | |
106 | === modified file 'debian/changelog' |
107 | --- debian/changelog 2016-12-05 21:10:03 +0000 |
108 | +++ debian/changelog 2017-01-05 20:19:05 +0000 |
109 | @@ -1,3 +1,23 @@ |
110 | +libertine (1.5-0ubuntu1) UNRELEASED; urgency=medium |
111 | + |
112 | + [ Chris Townsend ] |
113 | + * Drop support for the Puritine click package as that is Vivid only |
114 | + and a Vivid Libertine branch exists for any future fixes. |
115 | + * Only set the lxc log when a container is defined during class init. |
116 | + (LP: #1653973) |
117 | + * Bump version to 1.5 for new upstream release. |
118 | + |
119 | + [ Larry Price ] |
120 | + * Logic for bundling libertine as a snap built from source. |
121 | + * Catch exceptions raised during container creation. |
122 | + * Initial implementation of lxd backend. (LP: #1580612) |
123 | + * Use the libertine logger and LIBERTINE_DEBUG variable everywhere. |
124 | + * Update configure bind-mount logic given new lxd backend. |
125 | + * Use dpkg to find package name when installing local deb. |
126 | + * Create d-bus service for lxd container management. |
127 | + |
128 | + -- Chris Townsend <christopher.townsend@canonical.com> Thu, 05 Jan 2017 11:46:42 -0500 |
129 | + |
130 | libertine (1.4.4+17.04.20161205-0ubuntu1) zesty; urgency=medium |
131 | |
132 | [ Chris Townsend ] |
133 | |
134 | === modified file 'debian/control' |
135 | --- debian/control 2016-11-22 17:47:05 +0000 |
136 | +++ debian/control 2017-01-05 20:19:05 +0000 |
137 | @@ -2,8 +2,7 @@ |
138 | Section: utils |
139 | Priority: extra |
140 | Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> |
141 | -Build-Depends: click-dev, |
142 | - cmake, |
143 | +Build-Depends: cmake, |
144 | cmake-extras, |
145 | debhelper (>= 9), |
146 | dh-translations, |
147 | @@ -61,8 +60,7 @@ |
148 | Depends: libertine-qt-common, |
149 | libertine-tools, |
150 | python3-libertine-lxc, |
151 | - qml-module-qtquick2, |
152 | - qtdeclarative5-ubuntu-ui-toolkit-plugin, |
153 | + libsystemsettings1, |
154 | ${misc:Depends}, |
155 | ${shlibs:Depends} |
156 | Enhances: ubuntu-system-settings |
157 | @@ -184,6 +182,21 @@ |
158 | Libertine sandbox. It requires support for unprivileged LXC containers in the |
159 | Linux kernel. |
160 | |
161 | +Package: python3-libertine-lxd |
162 | +Architecture: any |
163 | +Section: python |
164 | +Multi-Arch: allowed |
165 | +Depends: lxd, |
166 | + python3-libertine, |
167 | + python3-pexpect, |
168 | + python3-pylxd, |
169 | + ${misc:Depends}, |
170 | + ${python3:Depends} |
171 | +Description: Python3 scripts for the Libertine application sandbox |
172 | + This package provides the LXD-based container back end module for the |
173 | + Libertine sandbox. It requires support for unprivileged LXD containers in the |
174 | + Linux kernel. |
175 | + |
176 | Package: python3-libertine-chroot |
177 | Architecture: any |
178 | Section: python |
179 | |
180 | === modified file 'debian/python3-libertine-chroot.install' |
181 | --- debian/python3-libertine-chroot.install 2016-10-07 21:09:41 +0000 |
182 | +++ debian/python3-libertine-chroot.install 2017-01-05 20:19:05 +0000 |
183 | @@ -1,2 +1,1 @@ |
184 | -usr/lib/*/libertine/update-puritine-containers |
185 | usr/lib/python*/*/libertine/ChrootContainer.py |
186 | |
187 | === added file 'debian/python3-libertine-lxd.install' |
188 | --- debian/python3-libertine-lxd.install 1970-01-01 00:00:00 +0000 |
189 | +++ debian/python3-libertine-lxd.install 2017-01-05 20:19:05 +0000 |
190 | @@ -0,0 +1,5 @@ |
191 | +etc/sudoers.d/libertine-lxd-sudo |
192 | +usr/bin/libertine-lxd-setup |
193 | +usr/bin/libertine-lxd-manager |
194 | +usr/lib/python*/*/libertine/LxdContainer.py |
195 | +usr/share/dbus-1/services/com.canonical.libertine.LxdManager.service |
196 | |
197 | === modified file 'debian/python3-libertine.install' |
198 | --- debian/python3-libertine.install 2016-10-28 19:37:32 +0000 |
199 | +++ debian/python3-libertine.install 2017-01-05 20:19:05 +0000 |
200 | @@ -4,5 +4,6 @@ |
201 | usr/lib/python*/*/libertine/Libertine.py |
202 | usr/lib/python*/*/libertine/__init__.py |
203 | usr/lib/python*/*/libertine/launcher |
204 | +usr/lib/python*/*/libertine/lifecycle |
205 | usr/lib/python*/*/libertine/service |
206 | usr/lib/python*/*/libertine/utils.py |
207 | |
208 | === modified file 'debian/rules' |
209 | --- debian/rules 2016-12-05 15:32:46 +0000 |
210 | +++ debian/rules 2017-01-05 20:19:05 +0000 |
211 | @@ -1,7 +1,7 @@ |
212 | #!/usr/bin/make -f |
213 | |
214 | %: |
215 | - dh $@ --with python3,gir,click |
216 | + dh $@ --with python3,gir |
217 | |
218 | override_dh_auto_test: |
219 | dbus-run-session -- dh_auto_test |
220 | |
221 | === modified file 'liblibertine/libertine.cpp' |
222 | --- liblibertine/libertine.cpp 2016-10-26 13:58:19 +0000 |
223 | +++ liblibertine/libertine.cpp 2017-01-05 20:19:05 +0000 |
224 | @@ -67,8 +67,16 @@ |
225 | g_dir_close(dir); |
226 | return nullptr; |
227 | } |
228 | -} |
229 | - |
230 | + |
231 | + |
232 | +gchar* |
233 | +id_from_list_index(const ContainerConfigList& container_list, guint index) |
234 | +{ |
235 | + return (gchar*)container_list.data(container_list.index(index, 0), |
236 | + (int)ContainerConfigList::DataRole::ContainerId) |
237 | + .toString().toStdString().c_str(); |
238 | +} |
239 | +} |
240 | |
241 | gchar** |
242 | libertine_list_apps_for_container(const gchar* container_id) |
243 | @@ -137,61 +145,68 @@ |
244 | gchar * |
245 | libertine_container_path(const gchar * container_id) |
246 | { |
247 | + g_return_val_if_fail(container_id != nullptr, nullptr); |
248 | + LibertineConfig config; |
249 | + ContainerConfigList container_list(&config); |
250 | gchar * path = nullptr; |
251 | - g_return_val_if_fail(container_id != nullptr, nullptr); |
252 | |
253 | - path = g_build_filename(g_get_user_cache_dir(), "libertine-container", container_id, "rootfs", nullptr); |
254 | + if (g_strcmp0((gchar*)container_list.getContainerType(container_id).toStdString().c_str(), "lxd") == 0) |
255 | + { |
256 | + path = g_build_filename("/", "var", "lib", "lxd", "containers", container_id, "rootfs", nullptr); |
257 | + } |
258 | + else |
259 | + { |
260 | + path = g_build_filename(g_get_user_cache_dir(), "libertine-container", container_id, "rootfs", nullptr); |
261 | + } |
262 | |
263 | if (g_file_test(path, G_FILE_TEST_EXISTS)) |
264 | { |
265 | return path; |
266 | } |
267 | - else |
268 | - { |
269 | - g_free(path); |
270 | - return nullptr; |
271 | - } |
272 | + |
273 | + g_free(path); |
274 | + return nullptr; |
275 | } |
276 | |
277 | |
278 | gchar * |
279 | libertine_container_home_path(const gchar * container_id) |
280 | { |
281 | + g_return_val_if_fail(container_id != nullptr, nullptr); |
282 | + LibertineConfig config; |
283 | + ContainerConfigList container_list(&config); |
284 | gchar * path = nullptr; |
285 | - g_return_val_if_fail(container_id != nullptr, nullptr); |
286 | |
287 | - path = g_build_filename(g_get_user_data_dir(), "libertine-container", "user-data", container_id, nullptr); |
288 | + if (g_strcmp0((gchar*)container_list.getContainerType(container_id).toStdString().c_str(), "lxd") == 0) |
289 | + { |
290 | + path = g_build_filename("/", "var", "lib", "lxd", "containers", container_id, "rootfs", "home", g_get_user_name(), nullptr); |
291 | + } |
292 | + else |
293 | + { |
294 | + path = g_build_filename(g_get_user_data_dir(), "libertine-container", "user-data", container_id, nullptr); |
295 | + } |
296 | |
297 | if (g_file_test(path, G_FILE_TEST_EXISTS)) |
298 | { |
299 | return path; |
300 | } |
301 | - else |
302 | - { |
303 | - g_free(path); |
304 | - return nullptr; |
305 | - } |
306 | |
307 | + g_free(path); |
308 | + return nullptr; |
309 | } |
310 | |
311 | |
312 | gchar * |
313 | libertine_container_name(const gchar * container_id) |
314 | { |
315 | - guint container_count; |
316 | - guint i; |
317 | gchar * container_name = nullptr; |
318 | LibertineConfig config; |
319 | ContainerConfigList container_list(&config); |
320 | - QVariant id; |
321 | - |
322 | - container_count = (guint)container_list.size(); |
323 | - |
324 | - for (i = 0; i < container_count; ++i) |
325 | + guint container_count = (guint)container_list.size(); |
326 | + |
327 | + for (guint i = 0; i < container_count; ++i) |
328 | { |
329 | - id = container_list.data(container_list.index(i, 0), (int)ContainerConfigList::DataRole::ContainerId); |
330 | - |
331 | - if (g_strcmp0((gchar *)id.toString().toStdString().c_str(), container_id) == 0) |
332 | + if (g_strcmp0(id_from_list_index(container_list, i), container_id) == 0) |
333 | { |
334 | QVariant name = container_list.data(container_list.index(i, 0), (int)ContainerConfigList::DataRole::ContainerName); |
335 | container_name = g_strdup(name.toString().toStdString().c_str()); |
336 | |
337 | === added directory 'parts' |
338 | === added directory 'parts/plugins' |
339 | === added file 'parts/plugins/utils.py' |
340 | --- parts/plugins/utils.py 1970-01-01 00:00:00 +0000 |
341 | +++ parts/plugins/utils.py 2017-01-05 20:19:05 +0000 |
342 | @@ -0,0 +1,63 @@ |
343 | +# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- |
344 | +# |
345 | +# Copyright (C) 2016 Canonical Ltd |
346 | +# |
347 | +# This program is free software: you can redistribute it and/or modify |
348 | +# it under the terms of the GNU General Public License version 3 as |
349 | +# published by the Free Software Foundation. |
350 | +# |
351 | +# This program is distributed in the hope that it will be useful, |
352 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
353 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
354 | +# GNU General Public License for more details. |
355 | +# |
356 | +# You should have received a copy of the GNU General Public License |
357 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
358 | + |
359 | +import re |
360 | +from snapcraft import file_utils |
361 | + |
362 | + |
363 | +def fix_shebangs(path): |
364 | + file_utils.replace_in_file(path, re.compile(r''), |
365 | + re.compile(r'^#!.*python'), |
366 | + r'#!/usr/bin/env python') |
367 | + |
368 | + |
369 | +def _sanitize(dep): |
370 | + return re.sub(r"\(.*\)", "", re.sub(r"\[.*\]", "", dep)).replace(',', '').strip() |
371 | + |
372 | + |
373 | +class DependsParser(object): |
374 | + def __init__(self): |
375 | + self.keyword = 'Depends:' |
376 | + self._parsing = False |
377 | + self._deps = [] |
378 | + self._packages = [] |
379 | + |
380 | + @property |
381 | + def deps(self): |
382 | + return [dep for dep in self._deps if dep not in self._packages] |
383 | + |
384 | + def parse(self, line): |
385 | + if self._parsing: |
386 | + if ':' in line: |
387 | + if not line.strip().startswith('${'): |
388 | + self._parsing = False |
389 | + else: |
390 | + self._deps.append(_sanitize(line)) |
391 | + |
392 | + if line.startswith(self.keyword): |
393 | + self._parsing = True |
394 | + possible_dep = line.lstrip(self.keyword) |
395 | + if not possible_dep.isspace() and not possible_dep.strip().startswith('${'): |
396 | + self._deps.append(_sanitize(possible_dep)) |
397 | + |
398 | + if line.startswith('Package:'): |
399 | + self._packages.append(line.lstrip('Package:').strip()) |
400 | + |
401 | + |
402 | +class BuildDependsParser(DependsParser): |
403 | + def __init__(self): |
404 | + super().__init__() |
405 | + self.keyword = 'Build-Depends:' |
406 | |
407 | === added file 'parts/plugins/x-libertine-deps.py' |
408 | --- parts/plugins/x-libertine-deps.py 1970-01-01 00:00:00 +0000 |
409 | +++ parts/plugins/x-libertine-deps.py 2017-01-05 20:19:05 +0000 |
410 | @@ -0,0 +1,155 @@ |
411 | +# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- |
412 | +# |
413 | +# Copyright (C) 2016 Canonical Ltd |
414 | +# |
415 | +# This program is free software: you can redistribute it and/or modify |
416 | +# it under the terms of the GNU General Public License version 3 as |
417 | +# published by the Free Software Foundation. |
418 | +# |
419 | +# This program is distributed in the hope that it will be useful, |
420 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
421 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
422 | +# GNU General Public License for more details. |
423 | +# |
424 | +# You should have received a copy of the GNU General Public License |
425 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
426 | + |
427 | + |
428 | +import fileinput |
429 | +import os |
430 | +import re |
431 | +import shlex |
432 | +import snapcraft.plugins.nil |
433 | +import sys |
434 | +import subprocess |
435 | +import utils # local |
436 | + |
437 | + |
438 | +def _arch(): |
439 | + cmd = subprocess.Popen(shlex.split('dpkg-architecture -qDEB_HOST_ARCH_CPU'), stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
440 | + out, err = cmd.communicate() |
441 | + arch = str(out, 'utf-8').strip() |
442 | + if arch == 'amd64': |
443 | + return 'x86_64-linux-gnu' |
444 | + elif arch == 'armhf': |
445 | + return 'arm-linux-gnueabihf' |
446 | + elif arch == 'arm64': |
447 | + return 'aarch64-linux-gnu' |
448 | + |
449 | + return '{}-linux-gnu'.format(arch) |
450 | + |
451 | + |
452 | +class LibertineDependenciesPlugin(snapcraft.plugins.nil.NilPlugin): |
453 | + def __init__(self, name, options, project): |
454 | + super().__init__(name, options, project) |
455 | + self._arch = _arch() |
456 | + |
457 | + deps_parser = utils.DependsParser() |
458 | + with open('debian/control') as control: |
459 | + for line in control.readlines(): |
460 | + deps_parser.parse(line) |
461 | + self.stage_packages.extend(deps_parser.deps) |
462 | + |
463 | + self._ignore_duplicate_files() |
464 | + |
465 | + @classmethod |
466 | + def schema(cls): |
467 | + return super().schema() |
468 | + |
469 | + def enable_cross_compilation(self): |
470 | + return super().enable_cross_compilation() |
471 | + |
472 | + def env(self, root): |
473 | + return super().env(root) + ['ARCH={}'.format(self._arch)] |
474 | + |
475 | + # By design, snapcraft ignores all pre- and post-inst scripts which |
476 | + # result in a bunch of packages that just don't work. |
477 | + def _run_preinst_postinst(self): |
478 | + # We need to create the lxc-usernet |
479 | + if not os.path.exists(self.installdir + '/etc/lxc/lxc-usernet'): |
480 | + with open(self.installdir + '/etc/lxc/lxc-usernet', 'w') as f: |
481 | + f.write('# USERNAME TYPE BRIDGE COUNT') |
482 | + |
483 | + # We need to run update-alternatives to create /usr/bin/fakeroot |
484 | + admindir = self.installdir + '/var/lib/dpkg/alternatives' |
485 | + altdir = self.installdir + '/etc/alternatives' |
486 | + os.makedirs(admindir, exist_ok=True) |
487 | + os.makedirs(altdir, exist_ok=True) |
488 | + subprocess.Popen(shlex.split('\ |
489 | + update-alternatives --admindir {0} --altdir {1} --install \ |
490 | + {2}/usr/bin/fakeroot fakeroot {2}/usr/bin/fakeroot-sysv 50 \ |
491 | + --slave {2}/usr/share/man/man1/fakeroot.1.gz \ |
492 | + fakeroot.1.gz {2}/usr/share/man/man1/fakeroot-sysv.1.gz \ |
493 | + --slave {2}/usr/share/man/man1/faked.1.gz \ |
494 | + faked.1.gz {2}/usr/share/man/man1/faked-sysv.1.gz \ |
495 | + --slave {2}/usr/share/man/es/man1/fakeroot.1.gz \ |
496 | + fakeroot.es.1.gz {2}/usr/share/man/es/man1/fakeroot-sysv.1.gz \ |
497 | + --slave {2}/usr/share/man/es/man1/faked.1.gz \ |
498 | + faked.es.1.gz {2}/usr/share/man/es/man1/faked-sysv.1.gz \ |
499 | + --slave {2}/usr/share/man/fr/man1/fakeroot.1.gz \ |
500 | + fakeroot.fr.1.gz {2}/usr/share/man/fr/man1/fakeroot-sysv.1.gz \ |
501 | + --slave {2}/usr/share/man/fr/man1/faked.1.gz \ |
502 | + faked.fr.1.gz {2}/usr/share/man/fr/man1/faked-sysv.1.gz \ |
503 | + --slave {2}/usr/share/man/sv/man1/fakeroot.1.gz \ |
504 | + fakeroot.sv.1.gz {2}/usr/share/man/sv/man1/fakeroot-sysv.1.gz \ |
505 | + --slave {2}/usr/share/man/sv/man1/faked.1.gz \ |
506 | + faked.sv.1.gz {2}/usr/share/man/sv/man1/faked-sysv.1.gz\ |
507 | + '.format(admindir, altdir, self.installdir))).wait() |
508 | + subprocess.Popen(shlex.split('\ |
509 | + update-alternatives --admindir {0} --altdir {1} --install \ |
510 | + {2}/usr/bin/fakeroot fakeroot {2}/usr/bin/fakeroot-tcp 30 \ |
511 | + --slave {2}/usr/share/man/man1/fakeroot.1.gz \ |
512 | + fakeroot.1.gz {2}/usr/share/man/man1/fakeroot-tcp.1.gz \ |
513 | + --slave {2}/usr/share/man/man1/faked.1.gz \ |
514 | + faked.1.gz {2}/usr/share/man/man1/faked-tcp.1.gz \ |
515 | + --slave {2}/usr/share/man/es/man1/fakeroot.1.gz \ |
516 | + fakeroot.es.1.gz {2}/usr/share/man/es/man1/fakeroot-tcp.1.gz \ |
517 | + --slave {2}/usr/share/man/es/man1/faked.1.gz \ |
518 | + faked.es.1.gz {2}/usr/share/man/es/man1/faked-tcp.1.gz \ |
519 | + --slave {2}/usr/share/man/fr/man1/fakeroot.1.gz \ |
520 | + fakeroot.fr.1.gz {2}/usr/share/man/fr/man1/fakeroot-tcp.1.gz \ |
521 | + --slave {2}/usr/share/man/fr/man1/faked.1.gz \ |
522 | + faked.fr.1.gz {2}/usr/share/man/fr/man1/faked-tcp.1.gz \ |
523 | + --slave {2}/usr/share/man/sv/man1/fakeroot.1.gz \ |
524 | + fakeroot.sv.1.gz {2}/usr/share/man/sv/man1/fakeroot-tcp.1.gz \ |
525 | + --slave {2}/usr/share/man/sv/man1/faked.1.gz \ |
526 | + faked.sv.1.gz {2}/usr/share/man/sv/man1/faked-tcp.1.gz\ |
527 | + '.format(admindir, altdir, self.installdir))).wait() |
528 | + |
529 | + def _fix_symlinks(self): |
530 | + for root, dirs, files in os.walk(self.installdir): |
531 | + for f in files: |
532 | + path = '{}/{}'.format(root, f) |
533 | + if os.path.islink(path): |
534 | + link = os.readlink(path) |
535 | + if link.startswith('/'): # needs fixing |
536 | + os.remove(path) |
537 | + os.symlink(os.path.relpath(link, root), path) |
538 | + |
539 | + def _fix_fakeroot(self): |
540 | + for line in fileinput.FileInput('{}/usr/bin/fakeroot'.format(self.installdir), inplace=True): |
541 | + if line.startswith('FAKEROOT_PREFIX='): |
542 | + sys.stdout.write('FAKEROOT_PREFIX=${SNAP}/usr # Updated by x-libertine.py\n') |
543 | + elif line.startswith('FAKEROOT_BINDIR='): |
544 | + sys.stdout.write('FAKEROOT_BINDIR=${SNAP}/usr/bin # Updated by x-libertine.py\n') |
545 | + elif line.startswith('PATHS='): |
546 | + sys.stdout.write('PATHS=${FAKEROOT_PREFIX}/lib/%s/libfakeroot:' \ |
547 | + '${FAKEROOT_PREFIX}/lib64/libfakeroot:' \ |
548 | + '${FAKEROOT_PREFIX}/lib32/libfakeroot' \ |
549 | + ' # Updated by x-libertine.py\n' % self._arch) |
550 | + else: |
551 | + sys.stdout.write(line) |
552 | + |
553 | + def _ignore_duplicate_files(self): |
554 | + self.options.stage.extend([ |
555 | + '-usr/lib/{}/liblibertine.so*'.format(self._arch) |
556 | + ]) |
557 | + |
558 | + def build(self): |
559 | + super().build() |
560 | + |
561 | + utils.fix_shebangs(self.installdir + '/usr/bin') |
562 | + |
563 | + self._run_preinst_postinst() |
564 | + self._fix_symlinks() |
565 | + self._fix_fakeroot() |
566 | |
567 | === added file 'parts/plugins/x-libertine.py' |
568 | --- parts/plugins/x-libertine.py 1970-01-01 00:00:00 +0000 |
569 | +++ parts/plugins/x-libertine.py 2017-01-05 20:19:05 +0000 |
570 | @@ -0,0 +1,45 @@ |
571 | +# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- |
572 | +# |
573 | +# Copyright (C) 2016 Canonical Ltd |
574 | +# |
575 | +# This program is free software: you can redistribute it and/or modify |
576 | +# it under the terms of the GNU General Public License version 3 as |
577 | +# published by the Free Software Foundation. |
578 | +# |
579 | +# This program is distributed in the hope that it will be useful, |
580 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
581 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
582 | +# GNU General Public License for more details. |
583 | +# |
584 | +# You should have received a copy of the GNU General Public License |
585 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
586 | + |
587 | + |
588 | +import snapcraft.plugins.cmake |
589 | +import utils # local |
590 | + |
591 | +class LibertinePlugin(snapcraft.plugins.cmake.CMakePlugin): |
592 | + def __init__(self, name, options, project): |
593 | + super().__init__(name, options, project) |
594 | + |
595 | + deps = utils.BuildDependsParser() |
596 | + |
597 | + with open('debian/control') as control: |
598 | + for line in control.readlines(): |
599 | + deps.parse(line) |
600 | + |
601 | + self.build_packages.extend(deps.deps) |
602 | + self.options.configflags.extend(['-DCMAKE_INSTALL_PREFIX=/usr']) |
603 | + |
604 | + @classmethod |
605 | + def schema(cls): |
606 | + return super().schema() |
607 | + |
608 | + def enable_cross_compilation(self): |
609 | + return super().enable_cross_compilation() |
610 | + |
611 | + def build(self): |
612 | + super().build() |
613 | + |
614 | + # Fix all shebangs to use the in-snap python. |
615 | + utils.fix_shebangs(self.installdir + '/usr/bin') |
616 | |
617 | === modified file 'python/CMakeLists.txt' |
618 | --- python/CMakeLists.txt 2015-07-20 15:00:27 +0000 |
619 | +++ python/CMakeLists.txt 2017-01-05 20:19:05 +0000 |
620 | @@ -1,4 +1,4 @@ |
621 | -execute_process(COMMAND python3 -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())" |
622 | +execute_process(COMMAND /usr/bin/python3 -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())" |
623 | OUTPUT_VARIABLE python_site_packages |
624 | OUTPUT_STRIP_TRAILING_WHITESPACE) |
625 | |
626 | |
627 | === modified file 'python/libertine/AppDiscovery.py' |
628 | --- python/libertine/AppDiscovery.py 2016-01-04 17:41:08 +0000 |
629 | +++ python/libertine/AppDiscovery.py 2017-01-05 20:19:05 +0000 |
630 | @@ -188,7 +188,7 @@ |
631 | if desktop_file_is_showable(desktop_entry): |
632 | yield AppInfo(desktop_file_name, desktop_entry, icon_cache) |
633 | except Exception as ex: |
634 | - print("error processing {}: {}".format(desktop_file_name, ex), file=sys.stderr) |
635 | + utils.get_logger().error("error processing {}: {}".format(desktop_file_name, ex)) |
636 | |
637 | |
638 | class AppLauncherCache(object): |
639 | @@ -218,5 +218,3 @@ |
640 | default=lambda o: o.__dict__, |
641 | sort_keys=True, |
642 | indent=4) |
643 | - |
644 | - |
645 | |
646 | === modified file 'python/libertine/ChrootContainer.py' |
647 | --- python/libertine/ChrootContainer.py 2016-12-02 17:46:47 +0000 |
648 | +++ python/libertine/ChrootContainer.py 2017-01-05 20:19:05 +0000 |
649 | @@ -71,10 +71,10 @@ |
650 | shutil.rmtree(container_root) |
651 | return True |
652 | except Exception as e: |
653 | - print("%s" % e) |
654 | + utils.get_logger().error("%s" % e) |
655 | return False |
656 | |
657 | - def create_libertine_container(self, password=None, multiarch=False, verbosity=1): |
658 | + def create_libertine_container(self, password=None, multiarch=False): |
659 | # Create the actual chroot |
660 | command_line = "{} fakeroot debootstrap --verbose --variant=fakechroot {} {}".format( |
661 | self._build_fakechroot_command(), self.installed_release, self.root_path) |
662 | @@ -83,12 +83,12 @@ |
663 | cmd.wait() |
664 | |
665 | if cmd.returncode != 0: |
666 | - print("Failed to create container") |
667 | + utils.get_logger().error("Failed to create container") |
668 | self.destroy_libertine_container() |
669 | return False |
670 | |
671 | # Remove symlinks as they can cause ill-behaved recursive behavior in the chroot |
672 | - print("Fixing chroot symlinks...") |
673 | + utils.get_logger().info("Fixing chroot symlinks...") |
674 | os.remove(os.path.join(self.root_path, 'dev')) |
675 | os.remove(os.path.join(self.root_path, 'proc')) |
676 | |
677 | @@ -109,8 +109,8 @@ |
678 | else: |
679 | archive = "deb http://archive.ubuntu.com/ubuntu " |
680 | |
681 | - if verbosity == 1: |
682 | - print("Updating chroot's sources.list entries...") |
683 | + utils.get_logger().info("Updating chroot's sources.list entries...") |
684 | + |
685 | with open(os.path.join(self.root_path, 'etc', 'apt', 'sources.list'), 'a') as fd: |
686 | fd.write(archive + self.installed_release + "-updates main\n") |
687 | fd.write(archive + self.installed_release + " universe\n") |
688 | @@ -121,46 +121,43 @@ |
689 | utils.create_libertine_user_data_dir(self.container_id) |
690 | |
691 | if multiarch and self.architecture == 'amd64': |
692 | - if verbosity == 1: |
693 | - print("Adding i386 multiarch support...") |
694 | + utils.get_logger().info("Adding i386 multiarch support...") |
695 | self.run_in_container("dpkg --add-architecture i386") |
696 | |
697 | - if verbosity == 1: |
698 | - print("Updating the contents of the container after creation...") |
699 | - self.update_packages(verbosity) |
700 | + utils.get_logger().info("Updating the contents of the container after creation...") |
701 | + self.update_packages() |
702 | |
703 | for package in self.default_packages: |
704 | - if not self.install_package(package, verbosity, update_cache=False): |
705 | - print("Failure installing %s during container creation" % package) |
706 | + if not self.install_package(package, update_cache=False): |
707 | + utils.get_logger().error("Failure installing %s during container creation" % package) |
708 | self.destroy_libertine_container() |
709 | return False |
710 | |
711 | if self.installed_release == "vivid" or self.installed_release == "xenial": |
712 | - if verbosity == 1: |
713 | - print("Installing the Stable Overlay PPA...") |
714 | - if not self.install_package("software-properties-common", verbosity, update_cache=False): |
715 | - print("Failure installing software-properties-common during container creation") |
716 | + utils.get_logger().info("Installing the Stable Overlay PPA...") |
717 | + if not self.install_package("software-properties-common", update_cache=False): |
718 | + utils.get_logger().error("Failure installing software-properties-common during container creation") |
719 | self.destroy_libertine_container() |
720 | return False |
721 | |
722 | self.run_in_container("add-apt-repository ppa:ci-train-ppa-service/stable-phone-overlay -y") |
723 | - self.update_packages(verbosity) |
724 | + self.update_packages() |
725 | |
726 | # Check if the container was created as root and chown the user directories as necessary |
727 | chown_recursive_dirs(utils.get_libertine_container_userdata_dir_path(self.container_id)) |
728 | |
729 | return True |
730 | |
731 | - def update_packages(self, verbosity=1): |
732 | - retcode = super().update_packages(verbosity) |
733 | - self._run_ldconfig(verbosity) |
734 | + def update_packages(self): |
735 | + retcode = super().update_packages() |
736 | + self._run_ldconfig() |
737 | return retcode == 0 |
738 | |
739 | - def install_package(self, package_name, verbosity=1, no_dialog=False, update_cache=True): |
740 | - returncode = super().install_package(package_name, verbosity, no_dialog, update_cache) |
741 | + def install_package(self, package_name, no_dialog=False, update_cache=True): |
742 | + returncode = super().install_package(package_name, no_dialog, update_cache) |
743 | |
744 | if returncode: |
745 | - self._run_ldconfig(verbosity) |
746 | + self._run_ldconfig() |
747 | |
748 | return returncode |
749 | |
750 | @@ -197,8 +194,12 @@ |
751 | mounts = self._sanitize_bind_mounts(utils.get_common_xdg_user_directories() + \ |
752 | ContainersConfig().get_container_bind_mounts(self.container_id)) |
753 | for user_dir in utils.generate_binding_directories(mounts, home_path): |
754 | - user_dir_path = os.path.join(home_path, user_dir[1]) |
755 | - bind_mounts += " -b \"%s:%s\"" % (user_dir[0], user_dir_path) |
756 | + if os.path.isabs(user_dir[1]): |
757 | + path = user_dir[1] |
758 | + else: |
759 | + path = os.path.join(home_path, user_dir[1]) |
760 | + |
761 | + bind_mounts += " -b \"%s:%s\"" % (user_dir[0], path) |
762 | |
763 | proot_cmd += bind_mounts |
764 | |
765 | @@ -233,16 +234,14 @@ |
766 | |
767 | args = shlex.split(proot_cmd) |
768 | args.extend(app_exec_line) |
769 | - app = psutil.Popen(args, env=environ) |
770 | - return app |
771 | + return psutil.Popen(args, env=environ) |
772 | |
773 | def finish_application(self, app): |
774 | utils.terminate_window_manager(self._window_manager) |
775 | app.wait() |
776 | |
777 | - def _run_ldconfig(self, verbosity=1): |
778 | - if verbosity == 1: |
779 | - print("Refreshing the container's dynamic linker run-time bindings...") |
780 | + def _run_ldconfig(self): |
781 | + utils.get_logger().info("Refreshing the container's dynamic linker run-time bindings...") |
782 | |
783 | command_line = self._build_privileged_proot_cmd() + " ldconfig.REAL" |
784 | |
785 | |
786 | === modified file 'python/libertine/ContainersConfig.py' |
787 | --- python/libertine/ContainersConfig.py 2016-11-16 17:58:38 +0000 |
788 | +++ python/libertine/ContainersConfig.py 2017-01-05 20:19:05 +0000 |
789 | @@ -214,12 +214,12 @@ |
790 | |
791 | def check_container_id(self, container_id): |
792 | if container_id and not self.container_exists(container_id): |
793 | - print("Container id \'%s\' does not exist." % container_id, file=sys.stderr) |
794 | + libertine.utils.get_logger().error("Container id \'%s\' does not exist." % container_id) |
795 | sys.exit(1) |
796 | elif not container_id: |
797 | container_id = self.get_default_container_id() |
798 | if container_id is None: |
799 | - print("No default container available.") |
800 | + libertine.utils.get_logger().error("No default container available.") |
801 | sys.exit(1) |
802 | |
803 | return container_id |
804 | @@ -260,7 +260,7 @@ |
805 | |
806 | def delete_container(self, container_id): |
807 | if not self.container_list: |
808 | - print("Unable to delete container. No containers defined.") |
809 | + libertine.utils.get_logger().error("Unable to delete container. No containers defined.") |
810 | sys.exit(1) |
811 | |
812 | container = self._get_container_entry(container_id) |
813 | |
814 | === modified file 'python/libertine/HostInfo.py' |
815 | --- python/libertine/HostInfo.py 2016-10-28 18:11:12 +0000 |
816 | +++ python/libertine/HostInfo.py 2017-01-05 20:19:05 +0000 |
817 | @@ -13,6 +13,7 @@ |
818 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
819 | |
820 | import lsb_release |
821 | +import os |
822 | import platform |
823 | import subprocess |
824 | |
825 | @@ -69,3 +70,7 @@ |
826 | parser.error("Failed to determine the local architecture.") |
827 | |
828 | return dpkg.stdout.read().strip() |
829 | + |
830 | + def get_host_timezone(self): |
831 | + with open(os.path.join('/', 'etc', 'timezone'), 'r') as fd: |
832 | + return fd.read().strip('\n') |
833 | |
834 | === modified file 'python/libertine/Libertine.py' |
835 | --- python/libertine/Libertine.py 2016-11-16 17:58:38 +0000 |
836 | +++ python/libertine/Libertine.py 2017-01-05 20:19:05 +0000 |
837 | @@ -1,4 +1,4 @@ |
838 | -# Copyright 2015-2106 Canonical Ltd. |
839 | +# Copyright 2015-2016 Canonical Ltd. |
840 | # |
841 | # This program is free software: you can redistribute it and/or modify it |
842 | # under the terms of the GNU General Public License version 3, as published |
843 | @@ -20,31 +20,32 @@ |
844 | import os |
845 | import shutil |
846 | |
847 | +from . import utils |
848 | from libertine.ContainersConfig import ContainersConfig |
849 | from libertine.HostInfo import HostInfo |
850 | |
851 | |
852 | -def apt_args_for_verbosity_level(verbosity): |
853 | - """ |
854 | - Maps numeric verbosity levels onto APT command-line arguments. |
855 | - |
856 | - :param verbosity: 0 is quiet, 1 is normal, 2 is incontinent |
857 | - """ |
858 | - return { |
859 | - 0: '--quiet=2', |
860 | - 1: '--assume-yes', |
861 | - 2: '--quiet=1 --assume-yes --option APT::Status-Fd=1' |
862 | - }.get(verbosity, '') |
863 | - |
864 | - |
865 | -def apt_command_prefix(verbosity): |
866 | - return '/usr/bin/apt-get ' + apt_args_for_verbosity_level(verbosity) + \ |
867 | +def _apt_args_for_verbosity_level(): |
868 | + """ |
869 | + Maps debug levels to APT command-line arguments. |
870 | + """ |
871 | + if 'LIBERTINE_DEBUG' not in os.environ or os.environ['LIBERTINE_DEBUG'] == '0': |
872 | + return '--quiet=2' |
873 | + |
874 | + if os.environ['LIBERTINE_DEBUG'] == '1': |
875 | + return '--quiet=1 --assume-yes' |
876 | + |
877 | + return '--assume-yes --option APT::Status-Fd=1' |
878 | + |
879 | + |
880 | +def _apt_command_prefix(): |
881 | + return '/usr/bin/apt-get ' + _apt_args_for_verbosity_level() + \ |
882 | ' --option Apt::Cmd::Disable-Script-Warning=true --option Dpkg::Progress-Fancy=1' + \ |
883 | ' --option Apt::Color=1 ' |
884 | |
885 | |
886 | def handle_runtime_error(error): |
887 | - print("%s" % error) |
888 | + utils.get_logger().error("%s" % error) |
889 | return False |
890 | |
891 | |
892 | @@ -92,41 +93,33 @@ |
893 | 'maliit-inputcontext-gtk3', |
894 | 'maliit-framework'] |
895 | |
896 | - def create_libertine_container(self, password=None, multiarch=False, verbosity=1): |
897 | - pass |
898 | - |
899 | - def destroy_libertine_container(self, verbosity=1): |
900 | - pass |
901 | - |
902 | - def copy_package_to_container(self, package_path): |
903 | - """ |
904 | - Copies a Debian package from the host to a pre-determined place |
905 | - in a container. |
906 | - |
907 | - :param package_path: The full path to the Debian package located |
908 | - on the host. |
909 | - """ |
910 | - if os.path.exists(os.path.join(self.root_path, 'tmp', package_path.split('/')[-1])): |
911 | + def create_libertine_container(self, password=None, multiarch=False): |
912 | + pass |
913 | + |
914 | + def destroy_libertine_container(self): |
915 | + pass |
916 | + |
917 | + def copy_file_to_container(self, source, dest): |
918 | + """ |
919 | + Copies a file from the host to the given path in the container. |
920 | + |
921 | + :param source: The full path to the file on the host. |
922 | + :param dest: The relative path to the file in the container without |
923 | + the root path. |
924 | + """ |
925 | + if os.path.exists(os.path.join(self.root_path, dest)): |
926 | return False |
927 | |
928 | - shutil.copy2(package_path, os.path.join(self.root_path, 'tmp')) |
929 | + shutil.copy2(source, os.path.join(self.root_path, dest.lstrip('/'))) |
930 | return True |
931 | |
932 | - def delete_package_in_container(self, package_path): |
933 | - """ |
934 | - Deletes a previously copied in Debian package in a container. |
935 | - |
936 | - :param package_path: The full path to the Debian package located |
937 | - on the host. |
938 | - """ |
939 | - os.remove(os.path.join(self.root_path, 'tmp', package_path.split('/')[-1])) |
940 | - |
941 | - def is_running(self): |
942 | - """ |
943 | - Indicates if the container is 'running'. The definition of 'running' |
944 | - depends on the type of the container. |
945 | - """ |
946 | - return False |
947 | + def delete_file_in_container(self, path): |
948 | + """ |
949 | + Deletes a file within the container. |
950 | + |
951 | + :param path: The path to the file without the container root path. |
952 | + """ |
953 | + os.remove(os.path.join(self.root_path, path.lstrip('/'))) |
954 | |
955 | def start_container(self): |
956 | """ |
957 | @@ -151,91 +144,85 @@ |
958 | """ |
959 | pass |
960 | |
961 | - def update_apt_cache(self, verbosity=1): |
962 | + def update_apt_cache(self): |
963 | """ |
964 | Updates the apt cache in the container. |
965 | - |
966 | - :param verbosity: the chattiness of the output on a range from 0 to 2 |
967 | """ |
968 | - return self.run_in_container(apt_command_prefix(verbosity) + 'update') |
969 | + return self.run_in_container(_apt_command_prefix() + 'update') |
970 | |
971 | - def update_packages(self, verbosity=1): |
972 | + def update_packages(self): |
973 | """ |
974 | Updates all packages installed in the container. |
975 | - |
976 | - :param verbosity: the chattiness of the output on a range from 0 to 2 |
977 | """ |
978 | - self.update_apt_cache(verbosity) |
979 | - return self.run_in_container(apt_command_prefix(verbosity) + '--force-yes dist-upgrade') == 0 |
980 | + self.update_apt_cache() |
981 | + return self.run_in_container(_apt_command_prefix() + '--force-yes dist-upgrade') == 0 |
982 | |
983 | - def install_package(self, package_name, verbosity=1, no_dialog=False, update_cache=True): |
984 | + def install_package(self, package_name, no_dialog=False, update_cache=True): |
985 | """ |
986 | Installs a named package in the container. |
987 | |
988 | :param package_name: The name of the package as APT understands it or |
989 | a full path to a Debian package on the host. |
990 | - :param verbosity: the chattiness of the output on a range from 0 to 2 |
991 | """ |
992 | if update_cache: |
993 | - self.update_apt_cache(verbosity) |
994 | + self.update_apt_cache() |
995 | |
996 | if package_name.endswith('.deb'): |
997 | - delete_package = self.copy_package_to_container(package_name) |
998 | - |
999 | - self.run_in_container('ls -la ' + os.environ['HOME']) |
1000 | - self.run_in_container('dpkg -i ' + |
1001 | - os.path.join('/', 'tmp', package_name.split('/')[-1])) |
1002 | - |
1003 | - ret = self.run_in_container(apt_command_prefix(verbosity) + " install -f") == 0 |
1004 | - |
1005 | - if delete_package: |
1006 | - self.delete_package_in_container(package_name) |
1007 | + if not os.path.exists(package_name): |
1008 | + utils.get_logger().error("File {} does not exist.".format(package_name)) |
1009 | + return False |
1010 | + |
1011 | + dest = os.path.join('/', 'tmp', package_name.split('/')[-1]) |
1012 | + file_created = self.copy_file_to_container(package_name, dest) |
1013 | + |
1014 | + self.run_in_container('dpkg -i {}'.format(dest)) |
1015 | + ret = self.run_in_container(_apt_command_prefix() + " install -f") == 0 |
1016 | + |
1017 | + if file_created: |
1018 | + self.delete_file_in_container(dest) |
1019 | |
1020 | return ret |
1021 | else: |
1022 | if no_dialog: |
1023 | os.environ['DEBIAN_FRONTEND'] = 'teletype' |
1024 | - return self.run_in_container(apt_command_prefix(verbosity) + " install '" + package_name + "'") == 0 |
1025 | + return self.run_in_container(_apt_command_prefix() + " install '" + package_name + "'") == 0 |
1026 | |
1027 | - def remove_package(self, package_name, verbosity=1): |
1028 | + def remove_package(self, package_name): |
1029 | """ |
1030 | Removes a package from the container. |
1031 | |
1032 | :param package_name: The name of the package to be removed. |
1033 | - :param verbosity: The verbosity level of the progress reporting. |
1034 | """ |
1035 | - if self.run_in_container(apt_command_prefix(verbosity) + " purge '" + package_name + "'") != 0: |
1036 | + if self.run_in_container(_apt_command_prefix() + " purge '" + package_name + "'") != 0: |
1037 | return False |
1038 | - return self.run_in_container(apt_command_prefix(verbosity) + "autoremove --purge") == 0 |
1039 | + return self.run_in_container(_apt_command_prefix() + "autoremove --purge") == 0 |
1040 | |
1041 | - def configure_multiarch(self, should_enable, verbosity=1): |
1042 | + def configure_multiarch(self, should_enable): |
1043 | """ |
1044 | Enables or disables multiarch repositories. |
1045 | |
1046 | :param should_enable: Whether or not to enable multiarch support. |
1047 | - :param verbosity: the chattiness of the output on a range from 0 to 2 |
1048 | """ |
1049 | if should_enable: |
1050 | ret = self.run_in_container("dpkg --add-architecture i386") |
1051 | if ret or ret == 0: |
1052 | - self.update_apt_cache(verbosity) |
1053 | + self.update_apt_cache() |
1054 | return ret |
1055 | else: |
1056 | - self.run_in_container(apt_command_prefix(verbosity) + "purge \".*:i386\"") |
1057 | + self.run_in_container(_apt_command_prefix() + "purge \".*:i386\"") |
1058 | return self.run_in_container("dpkg --remove-architecture i386") |
1059 | |
1060 | - def configure_add_archive(self, archive, public_key_file, verbosity=1): |
1061 | + def configure_add_archive(self, archive, public_key_file): |
1062 | """ |
1063 | Adds the given archive. If this archive requires a key, prompt user. |
1064 | |
1065 | :param archive: The configuration command to run. |
1066 | :param public_key_file: file containing the public key used to sign this archive |
1067 | - :param verbosity: the chattiness of the output on a range from 0 to 2 |
1068 | """ |
1069 | if not os.path.exists(os.path.join(self.root_path, 'usr', 'bin', 'add-apt-repository')): |
1070 | - self.install_package("software-properties-common", verbosity) |
1071 | + self.install_package("software-properties-common") |
1072 | if 'https://' in archive and not os.path.exists(os.path.join(self.root_path, 'usr', 'lib', 'apt', 'methods', 'https')): |
1073 | - self.install_package("apt-transport-https", verbosity) |
1074 | + self.install_package("apt-transport-https") |
1075 | |
1076 | retcode = self.run_in_container("add-apt-repository -y " + archive) |
1077 | if retcode is 0 and public_key_file is not None: |
1078 | @@ -244,12 +231,11 @@ |
1079 | |
1080 | return retcode |
1081 | |
1082 | - def configure_remove_archive(self, archive, verbosity=1): |
1083 | + def configure_remove_archive(self, archive): |
1084 | """ |
1085 | Removes the given archive. |
1086 | |
1087 | :param archive: The configuration command to run. |
1088 | - :param verbosity: the chattiness of the output on a range from 0 to 2 |
1089 | """ |
1090 | return self.run_in_container("add-apt-repository -y -r " + archive) |
1091 | |
1092 | @@ -272,19 +258,19 @@ |
1093 | super().__init__(container_id) |
1094 | self.container_type = "mock" |
1095 | |
1096 | - def create_libertine_container(self, password=None, multiarch=False, verbosity=1): |
1097 | - return True |
1098 | - |
1099 | - def destroy_libertine_container(self, verbosity=1): |
1100 | - return True |
1101 | - |
1102 | - def update_packages(self, verbosity=1): |
1103 | - return True |
1104 | - |
1105 | - def install_package(self, package_name, verbosity=1, no_dialog=False, update_cache=True): |
1106 | - return True |
1107 | - |
1108 | - def remove_package(self, package_name, verbosity=1, no_dialog=False): |
1109 | + def create_libertine_container(self, password=None, multiarch=False): |
1110 | + return True |
1111 | + |
1112 | + def destroy_libertine_container(self): |
1113 | + return True |
1114 | + |
1115 | + def update_packages(self): |
1116 | + return True |
1117 | + |
1118 | + def install_package(self, package_name, no_dialog=False, update_cache=True): |
1119 | + return True |
1120 | + |
1121 | + def remove_package(self, package_name, no_dialog=False): |
1122 | return True |
1123 | |
1124 | def run_in_container(self, command_string): |
1125 | @@ -335,6 +321,9 @@ |
1126 | if container_type == "lxc": |
1127 | from libertine.LxcContainer import LibertineLXC |
1128 | self.container = LibertineLXC(container_id) |
1129 | + elif container_type == "lxd": |
1130 | + from libertine.LxdContainer import LibertineLXD |
1131 | + self.container = LibertineLXD(container_id, self.containers_config) |
1132 | elif container_type == "chroot": |
1133 | from libertine.ChrootContainer import LibertineChroot |
1134 | self.container = LibertineChroot(container_id) |
1135 | @@ -365,48 +354,47 @@ |
1136 | """ |
1137 | return self.container.destroy_libertine_container() |
1138 | |
1139 | - def create_libertine_container(self, password=None, multiarch=False, verbosity=1): |
1140 | + def create_libertine_container(self, password=None, multiarch=False): |
1141 | """ |
1142 | Creates the container. |
1143 | """ |
1144 | self.container.architecture = HostInfo().get_host_architecture() |
1145 | self.container.installed_release = self.containers_config.get_container_distro(self.container_id) |
1146 | |
1147 | - return self.container.create_libertine_container(password, multiarch, verbosity) |
1148 | + return self.container.create_libertine_container(password, multiarch) |
1149 | |
1150 | - def update_libertine_container(self, verbosity=1): |
1151 | + def update_libertine_container(self): |
1152 | """ |
1153 | Updates the contents of the container. |
1154 | """ |
1155 | try: |
1156 | with ContainerRunning(self.container): |
1157 | - return self.container.update_packages(verbosity) |
1158 | + return self.container.update_packages() |
1159 | except RuntimeError as e: |
1160 | return handle_runtime_error(e) |
1161 | |
1162 | - def install_package(self, package_name, verbosity=1, no_dialog=False, update_cache=True): |
1163 | + def install_package(self, package_name, no_dialog=False, update_cache=True): |
1164 | """ |
1165 | Installs a package in the container. |
1166 | """ |
1167 | try: |
1168 | with ContainerRunning(self.container): |
1169 | - return self.container.install_package(package_name, verbosity, no_dialog, update_cache) |
1170 | + return self.container.install_package(package_name, no_dialog, update_cache) |
1171 | except RuntimeError as e: |
1172 | return handle_runtime_error(e) |
1173 | |
1174 | - def remove_package(self, package_name, verbosity=1, no_dialog=False): |
1175 | + def remove_package(self, package_name, no_dialog=False): |
1176 | """ |
1177 | Removes a package from the container. |
1178 | |
1179 | :param package_name: The name of the package to be removed. |
1180 | - :param verbosity: The verbosity level of the progress reporting. |
1181 | """ |
1182 | try: |
1183 | with ContainerRunning(self.container): |
1184 | if no_dialog: |
1185 | os.environ['DEBIAN_FRONTEND'] = 'teletype' |
1186 | |
1187 | - return self.container.remove_package(package_name, verbosity) |
1188 | + return self.container.remove_package(package_name) |
1189 | except RuntimeError as e: |
1190 | return handle_runtime_error(e) |
1191 | |
1192 | @@ -484,23 +472,23 @@ |
1193 | except RuntimeError as e: |
1194 | return handle_runtime_error(e) |
1195 | |
1196 | - def configure_multiarch(self, should_enable, verbosity=1): |
1197 | - try: |
1198 | - with ContainerRunning(self.container): |
1199 | - return self.container.configure_multiarch(should_enable, verbosity) |
1200 | - except RuntimeError as e: |
1201 | - return handle_runtime_error(e) |
1202 | - |
1203 | - def configure_add_archive(self, archive, key, verbosity): |
1204 | - try: |
1205 | - with ContainerRunning(self.container): |
1206 | - return self.container.configure_add_archive(archive, key, verbosity) |
1207 | - except RuntimeError as e: |
1208 | - return handle_runtime_error(e) |
1209 | - |
1210 | - def configure_remove_archive(self, archive, verbosity): |
1211 | - try: |
1212 | - with ContainerRunning(self.container): |
1213 | - return self.container.configure_remove_archive(archive, verbosity) |
1214 | + def configure_multiarch(self, should_enable): |
1215 | + try: |
1216 | + with ContainerRunning(self.container): |
1217 | + return self.container.configure_multiarch(should_enable) |
1218 | + except RuntimeError as e: |
1219 | + return handle_runtime_error(e) |
1220 | + |
1221 | + def configure_add_archive(self, archive, key): |
1222 | + try: |
1223 | + with ContainerRunning(self.container): |
1224 | + return self.container.configure_add_archive(archive, key) |
1225 | + except RuntimeError as e: |
1226 | + return handle_runtime_error(e) |
1227 | + |
1228 | + def configure_remove_archive(self, archive): |
1229 | + try: |
1230 | + with ContainerRunning(self.container): |
1231 | + return self.container.configure_remove_archive(archive) |
1232 | except RuntimeError as e: |
1233 | return handle_runtime_error(e) |
1234 | |
1235 | === modified file 'python/libertine/LxcContainer.py' |
1236 | --- python/libertine/LxcContainer.py 2016-11-16 17:33:42 +0000 |
1237 | +++ python/libertine/LxcContainer.py 2017-01-05 20:19:05 +0000 |
1238 | @@ -23,8 +23,9 @@ |
1239 | import sys |
1240 | import tempfile |
1241 | |
1242 | +from .lifecycle import LifecycleResult |
1243 | from .Libertine import BaseContainer |
1244 | -from . import utils |
1245 | +from . import utils, HostInfo |
1246 | |
1247 | |
1248 | home_path = os.environ['HOME'] |
1249 | @@ -33,7 +34,7 @@ |
1250 | LIBERTINE_LXC_MANAGER_PATH = "/LxcManager" |
1251 | |
1252 | |
1253 | -def check_lxc_net_entry(entry): |
1254 | +def _check_lxc_net_entry(entry): |
1255 | lxc_net_file = open('/etc/lxc/lxc-usernet') |
1256 | found = False |
1257 | |
1258 | @@ -45,14 +46,14 @@ |
1259 | return found |
1260 | |
1261 | |
1262 | -def setup_host_environment(username): |
1263 | +def _setup_host_environment(username): |
1264 | lxc_net_entry = "%s veth lxcbr0 10" % str(username) |
1265 | |
1266 | - if not check_lxc_net_entry(lxc_net_entry): |
1267 | + if not _check_lxc_net_entry(lxc_net_entry): |
1268 | subprocess.Popen(["sudo", "libertine-lxc-setup", str(username)]).wait() |
1269 | |
1270 | |
1271 | -def get_lxc_default_config_path(): |
1272 | +def _get_lxc_default_config_path(): |
1273 | return os.path.join(home_path, '.config', 'lxc') |
1274 | |
1275 | |
1276 | @@ -74,21 +75,43 @@ |
1277 | return container |
1278 | |
1279 | |
1280 | -def lxc_start(container, lxc_log_file): |
1281 | - container.append_config_item("lxc.logfile", lxc_log_file) |
1282 | - container.append_config_item("lxc.logpriority", "3") |
1283 | +def _dump_lxc_log(logfile): |
1284 | + if os.path.exists(logfile): |
1285 | + try: |
1286 | + with open(logfile, 'r') as fd: |
1287 | + for line in fd: |
1288 | + print(line.lstrip()) |
1289 | + except Exception as ex: |
1290 | + utils.get_logger().error("Could not open LXC log file: %s" % ex) |
1291 | + |
1292 | + |
1293 | +def get_logfile(container): |
1294 | + try: |
1295 | + logfile = container.get_config_item('lxc.logfile') |
1296 | + except: |
1297 | + logfile = None |
1298 | + |
1299 | + if not container.running or logfile is None or not os.path.exists(logfile): |
1300 | + logfile = os.path.join(tempfile.mkdtemp(), 'lxc-start.log') |
1301 | + container.append_config_item("lxc.logfile", logfile) |
1302 | + container.append_config_item("lxc.logpriority", "3") |
1303 | + |
1304 | + return logfile |
1305 | + |
1306 | +def lxc_start(container): |
1307 | + lxc_log_file = get_logfile(container) |
1308 | |
1309 | if not container.start(): |
1310 | - return (False, "Container failed to start") |
1311 | + return LifecycleResult("Container failed to start.") |
1312 | |
1313 | if not container.wait("RUNNING", 10): |
1314 | - return (False, "Container failed to enter the RUNNING state") |
1315 | + return LifecycleResult("Container failed to enter the RUNNING state.") |
1316 | |
1317 | if not container.get_ips(timeout=30): |
1318 | lxc_stop(container) |
1319 | - return (False, "Not able to connect to the network.") |
1320 | + return LifecycleResult("Not able to connect to the network.") |
1321 | |
1322 | - return (True, "") |
1323 | + return LifecycleResult() |
1324 | |
1325 | |
1326 | def lxc_stop(container): |
1327 | @@ -96,13 +119,6 @@ |
1328 | container.stop() |
1329 | |
1330 | |
1331 | -def get_host_timezone(): |
1332 | - with open(os.path.join('/', 'etc', 'timezone'), 'r') as fd: |
1333 | - host_timezone = fd.read().strip('\n') |
1334 | - |
1335 | - return host_timezone |
1336 | - |
1337 | - |
1338 | class EnvLxcSettings(contextlib.ExitStack): |
1339 | """ |
1340 | Helper object providing a way to set the proxies for testing |
1341 | @@ -137,42 +153,31 @@ |
1342 | super().__init__(container_id) |
1343 | self.container_type = "lxc" |
1344 | self.container = lxc_container(container_id) |
1345 | - self._set_lxc_log() |
1346 | self.lxc_manager_interface = None |
1347 | self.window_manager = None |
1348 | + self.host_info = HostInfo.HostInfo() |
1349 | |
1350 | utils.set_session_dbus_env_var() |
1351 | |
1352 | try: |
1353 | bus = dbus.SessionBus() |
1354 | - lxc_mgr_service = bus.get_object(get_lxc_manager_dbus_name(), get_lxc_manager_dbus_path()) |
1355 | - self.lxc_manager_interface = dbus.Interface(lxc_mgr_service, get_lxc_manager_dbus_name()) |
1356 | + self.lxc_manager_interface = bus.get_object(get_lxc_manager_dbus_name(), get_lxc_manager_dbus_path()) |
1357 | except dbus.exceptions.DBusException: |
1358 | pass |
1359 | |
1360 | - def is_running(self): |
1361 | - return self.container.running |
1362 | - |
1363 | def timezone_needs_update(self): |
1364 | - host_timezone = get_host_timezone() |
1365 | - |
1366 | with open(os.path.join(self.root_path, 'etc', 'timezone'), 'r') as fd: |
1367 | - container_timezone = fd.read().strip('\n') |
1368 | - |
1369 | - if host_timezone == container_timezone: |
1370 | - return False |
1371 | - else: |
1372 | - return True |
1373 | + return fd.read().strip('\n') != self.host_info.get_host_timezone() |
1374 | |
1375 | def start_container(self): |
1376 | if self.lxc_manager_interface: |
1377 | - (result, error) = self.lxc_manager_interface.operation_start(self.container_id, self.lxc_log_file) |
1378 | + result = LifecycleResult.from_dict(self.lxc_manager_interface.operation_start(self.container_id)) |
1379 | else: |
1380 | - (result, error) = lxc_start(self.container, self.lxc_log_file) |
1381 | + result = lxc_start(self.container) |
1382 | |
1383 | - if not result: |
1384 | - self._dump_lxc_log() |
1385 | - raise RuntimeError(error) |
1386 | + if not result.success: |
1387 | + _dump_lxc_log(result.logfile) |
1388 | + raise RuntimeError(result.error) |
1389 | |
1390 | if self.run_in_container("mountpoint -q /tmp/.X11-unix") == 0: |
1391 | self.run_in_container("umount /tmp/.X11-unix") |
1392 | @@ -189,14 +194,14 @@ |
1393 | cmd_args = shlex.split(command_string) |
1394 | return self.container.attach_wait(lxc.attach_run_command, cmd_args) |
1395 | |
1396 | - def update_packages(self, verbosity=1): |
1397 | + def update_packages(self): |
1398 | if self.timezone_needs_update(): |
1399 | self.run_in_container("bash -c \'echo \"{}\" >/etc/timezone\'".format( |
1400 | - get_host_timezone())) |
1401 | + self.host_info.get_host_timezone())) |
1402 | self.run_in_container("rm -f /etc/localtime") |
1403 | self.run_in_container("dpkg-reconfigure -f noninteractive tzdata") |
1404 | |
1405 | - return super().update_packages(verbosity) |
1406 | + return super().update_packages() |
1407 | |
1408 | def destroy_libertine_container(self): |
1409 | if not self.container.defined: |
1410 | @@ -206,7 +211,7 @@ |
1411 | self.container.destroy() |
1412 | return True |
1413 | |
1414 | - def create_libertine_container(self, password=None, multiarch=False, verbosity=1): |
1415 | + def create_libertine_container(self, password=None, multiarch=False): |
1416 | if password is None: |
1417 | return False |
1418 | |
1419 | @@ -214,10 +219,10 @@ |
1420 | user_id = os.getuid() |
1421 | group_id = os.getgid() |
1422 | |
1423 | - setup_host_environment(username) |
1424 | + _setup_host_environment(username) |
1425 | |
1426 | # Generate the default lxc default config, if it doesn't exist |
1427 | - config_path = get_lxc_default_config_path() |
1428 | + config_path = _get_lxc_default_config_path() |
1429 | config_file = "%s/default.conf" % config_path |
1430 | |
1431 | if not os.path.exists(config_path): |
1432 | @@ -236,25 +241,27 @@ |
1433 | fd.write("lxc.id_map = u %s %s %s\n" % (user_id + 1, (user_id + 1) + 100000, 65536 - (user_id + 1))) |
1434 | fd.write("lxc.id_map = g %s %s %s\n" % (group_id + 1, (group_id + 1) + 100000, 65536 - (user_id + 1))) |
1435 | |
1436 | + self.container.load_config(config_file) |
1437 | + |
1438 | utils.create_libertine_user_data_dir(self.container_id) |
1439 | |
1440 | with EnvLxcSettings(): |
1441 | + lxc_logfile = get_logfile(self.container) |
1442 | if not self.container.create("download", 0, |
1443 | {"dist": "ubuntu", |
1444 | "release": self.installed_release, |
1445 | "arch": self.architecture}): |
1446 | - print("Failed to create container") |
1447 | - self._dump_lxc_log() |
1448 | + utils.get_logger().error("Failed to create container") |
1449 | + _dump_lxc_log(lxc_logfile) |
1450 | return False |
1451 | |
1452 | self.create_libertine_config() |
1453 | |
1454 | - if verbosity == 1: |
1455 | - print("starting container ...") |
1456 | + utils.get_logger().info("starting container ...") |
1457 | try: |
1458 | self.start_container() |
1459 | except RuntimeError as e: |
1460 | - print("Container failed to start: %s" % e) |
1461 | + utils.get_logger().error("Container failed to start: %s" % e) |
1462 | self.destroy_libertine_container() |
1463 | return False |
1464 | |
1465 | @@ -263,22 +270,19 @@ |
1466 | str(user_id), crypt.crypt(password), str(username))) |
1467 | |
1468 | if multiarch and self.architecture == 'amd64': |
1469 | - if verbosity == 1: |
1470 | - print("Adding i386 multiarch support...") |
1471 | + utils.get_logger().info("Adding i386 multiarch support...") |
1472 | self.run_in_container("dpkg --add-architecture i386") |
1473 | |
1474 | - if verbosity == 1: |
1475 | - print("Updating the contents of the container after creation...") |
1476 | - self.update_packages(verbosity) |
1477 | + utils.get_logger().info("Updating the contents of the container after creation...") |
1478 | + self.update_packages() |
1479 | |
1480 | for package in self.default_packages: |
1481 | - if not self.install_package(package, verbosity, update_cache=False): |
1482 | - print("Failure installing %s during container creation" % package) |
1483 | + if not self.install_package(package, update_cache=False): |
1484 | + utils.get_logger().error("Failure installing %s during container creation" % package) |
1485 | self.destroy_libertine_container() |
1486 | return False |
1487 | |
1488 | - if verbosity == 1: |
1489 | - print("stopping container ...") |
1490 | + utils.get_logger().info("stopping container ...") |
1491 | self.stop_container() |
1492 | |
1493 | return True |
1494 | @@ -316,17 +320,17 @@ |
1495 | |
1496 | def start_application(self, app_exec_line, environ): |
1497 | if self.lxc_manager_interface == None: |
1498 | - print("No interface to libertine-lxc-manager. Failing application launch.") |
1499 | + utils.get_logger().error("No interface to libertine-lxc-manager. Failing application launch.") |
1500 | return |
1501 | |
1502 | os.environ.clear() |
1503 | os.environ.update(environ) |
1504 | |
1505 | - (result, error) = self.lxc_manager_interface.app_start(self.container_id, self.lxc_log_file) |
1506 | + result = LifecycleResult.from_dict(self.lxc_manager_interface.app_start(self.container_id)) |
1507 | |
1508 | - if not result: |
1509 | - self._dump_lxc_log() |
1510 | - print("%s" % error) |
1511 | + if not result.success: |
1512 | + _dump_lxc_log(get_logfile(self.container)) |
1513 | + utils.get_logger().error("%s" % result.error) |
1514 | return |
1515 | |
1516 | self.window_manager = self.container.attach(lxc.attach_run_command, |
1517 | @@ -348,17 +352,3 @@ |
1518 | |
1519 | # Tell libertine-lxc-manager that the app has stopped. |
1520 | self.lxc_manager_interface.app_stop(self.container_id) |
1521 | - |
1522 | - def _set_lxc_log(self): |
1523 | - self.lxc_log_file = os.path.join(tempfile.mkdtemp(), 'lxc-start.log') |
1524 | - self.container.append_config_item("lxc.logfile", self.lxc_log_file) |
1525 | - self.container.append_config_item("lxc.logpriority", "3") |
1526 | - |
1527 | - def _dump_lxc_log(self): |
1528 | - if os.path.exists(self.lxc_log_file): |
1529 | - try: |
1530 | - with open(self.lxc_log_file, 'r') as fd: |
1531 | - for line in fd: |
1532 | - print(line.lstrip()) |
1533 | - except Exception as ex: |
1534 | - print("Could not open LXC log file: %s" % ex, file=sys.stderr) |
1535 | |
1536 | === added file 'python/libertine/LxdContainer.py' |
1537 | --- python/libertine/LxdContainer.py 1970-01-01 00:00:00 +0000 |
1538 | +++ python/libertine/LxdContainer.py 2017-01-05 20:19:05 +0000 |
1539 | @@ -0,0 +1,455 @@ |
1540 | +# Copyright 2016 Canonical Ltd. |
1541 | +# |
1542 | +# This program is free software: you can redistribute it and/or modify it |
1543 | +# under the terms of the GNU General Public License version 3, as published |
1544 | +# by the Free Software Foundation. |
1545 | +# |
1546 | +# This program is distributed in the hope that it will be useful, but |
1547 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
1548 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1549 | +# PURPOSE. See the GNU General Public License for more details. |
1550 | +# |
1551 | +# You should have received a copy of the GNU General Public License along |
1552 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
1553 | + |
1554 | +import crypt |
1555 | +import dbus |
1556 | +import os |
1557 | +import psutil |
1558 | +import pylxd |
1559 | +import shlex |
1560 | +import subprocess |
1561 | +import time |
1562 | + |
1563 | +from .lifecycle import LifecycleResult |
1564 | +from libertine import Libertine, utils, HostInfo |
1565 | + |
1566 | + |
1567 | +LIBERTINE_LXC_MANAGER_NAME = "com.canonical.libertine.LxdManager" |
1568 | +LIBERTINE_LXC_MANAGER_PATH = "/LxdManager" |
1569 | + |
1570 | + |
1571 | +def get_lxd_manager_dbus_name(): |
1572 | + return LIBERTINE_LXC_MANAGER_NAME |
1573 | + |
1574 | + |
1575 | +def get_lxd_manager_dbus_path(): |
1576 | + return LIBERTINE_LXC_MANAGER_PATH |
1577 | + |
1578 | + |
1579 | +def _get_devices_map(): |
1580 | + devices = { |
1581 | + '/dev/tty0': {'path': '/dev/tty0', 'type': 'unix-char'}, |
1582 | + '/dev/tty7': {'path': '/dev/tty7', 'type': 'unix-char'}, |
1583 | + '/dev/tty8': {'path': '/dev/tty8', 'type': 'unix-char'}, |
1584 | + '/dev/fb0': {'path': '/dev/fb0', 'type': 'unix-char'}, |
1585 | + 'x11-socket': {'source': '/tmp/.X11-unix', 'path': '/tmp/.X11-unix', 'type': 'disk', 'optional': 'true'}, |
1586 | + } |
1587 | + |
1588 | + if os.path.exists('/dev/video0'): |
1589 | + devices['/dev/video0'] = {'path': '/dev/video0', 'type': 'unix-char'} |
1590 | + |
1591 | + # every regular file in /dev/snd |
1592 | + for f in ['/dev/snd/{}'.format(f) for f in os.listdir('/dev/snd') if not os.path.isdir('/dev/snd/{}'.format(f))]: |
1593 | + devices[f] = {'path': f, 'type': 'unix-char'} |
1594 | + |
1595 | + # every regular file in /dev/dri |
1596 | + for f in ['/dev/dri/{}'.format(f) for f in os.listdir('/dev/dri') if not os.path.isdir('/dev/dri/{}'.format(f))]: |
1597 | + devices[f] = {'path': f, 'type': 'unix-char'} |
1598 | + |
1599 | + # Some devices require special mappings for snap |
1600 | + if utils.is_snap_environment(): |
1601 | + devices['x11-socket']['source'] = '/var/lib/snapd/hostfs/tmp/.X11-unix' |
1602 | + |
1603 | + return devices |
1604 | + |
1605 | +def _readlink(source): |
1606 | + while os.path.islink(source): |
1607 | + new_source = os.readlink(source) |
1608 | + if not os.path.isabs(new_source): |
1609 | + new_source = os.path.join(os.path.dirname(source), new_source) |
1610 | + source = new_source |
1611 | + |
1612 | + return source |
1613 | + |
1614 | +def _setup_lxd(): |
1615 | + utils.get_logger().info("Running LXD setup.") |
1616 | + import pexpect |
1617 | + child = pexpect.spawnu('sudo libertine-lxd-setup {}'.format(os.environ['USER']), env={'TERM': 'dumb'}) |
1618 | + |
1619 | + while True: |
1620 | + index = child.expect(['.+\[default=.+\].*', pexpect.EOF, pexpect.TIMEOUT, |
1621 | + # The following are required for lxd=2.0.x |
1622 | + '.+\[yes/no\].*', |
1623 | + '.+\(e.g. (?P<example>[a-z0-9\.:]+)\).+']) |
1624 | + if index == 0: |
1625 | + child.sendline() |
1626 | + elif index == 1: |
1627 | + return True |
1628 | + elif index == 2: |
1629 | + return False |
1630 | + elif index == 3: |
1631 | + child.sendline('yes') |
1632 | + elif index == 4: |
1633 | + child.sendline(child.match.group('example')) |
1634 | + |
1635 | + if child.exitstatus is not None: |
1636 | + return child.exitstatus == 0 |
1637 | + |
1638 | + |
1639 | +def _setup_bind_mount_service(container, uid, username): |
1640 | + utils.get_logger().info("Creating systemd mount override service") |
1641 | + service = ''' |
1642 | +[Unit] |
1643 | +Description=Fix system mounts for libertine |
1644 | + |
1645 | +[Service] |
1646 | +ExecStart=/usr/bin/libertine-lxd-mount-update |
1647 | + |
1648 | +[Install] |
1649 | +WantedBy=multi-user.target |
1650 | +'''[1:-1] |
1651 | + container.files.put('/etc/systemd/system/libertine-lxd-mount-update.service', service.encode('utf-8')) |
1652 | + container.execute(shlex.split('chmod 644 /etc/systemd/system/libertine-lxd-mount-update.service')) |
1653 | + |
1654 | + utils.get_logger().info("Creating mount update shell script") |
1655 | + script = ''' |
1656 | +#!/bin/sh |
1657 | + |
1658 | +mkdir -p /run/user/{uid} |
1659 | +chown {username}:{username} /run/user/{uid} |
1660 | +chmod 755 /run/user/{uid} |
1661 | +mount -o bind /var/tmp/run/user/{uid} /run/user/{uid} |
1662 | + |
1663 | +chgrp audio /dev/snd/* |
1664 | +chgrp video /dev/dri/* |
1665 | +[ -n /dev/video0 ] && chgrp video /dev/video0 |
1666 | +'''[1:-1] |
1667 | + container.files.put('/usr/bin/libertine-lxd-mount-update', script.format(uid=uid, username=username).encode('utf-8')) |
1668 | + container.execute(shlex.split('chmod 755 /usr/bin/libertine-lxd-mount-update')) |
1669 | + |
1670 | + utils.get_logger().info("Enabling systemd mount update service") |
1671 | + container.execute(shlex.split('systemctl enable libertine-lxd-mount-update.service')) |
1672 | + |
1673 | + |
1674 | +def lxd_container(client, container_id): |
1675 | + try: |
1676 | + return client.containers.get(container_id) |
1677 | + except pylxd.exceptions.LXDAPIException: |
1678 | + return None |
1679 | + |
1680 | + |
1681 | +def _wait_for_network(container): |
1682 | + for retries in range(0, 10): |
1683 | + out, err = container.execute(shlex.split('ping -c 1 ubuntu.com')) |
1684 | + if out: |
1685 | + utils.get_logger().info("Network connection active") |
1686 | + return True |
1687 | + time.sleep(1) |
1688 | + return False |
1689 | + |
1690 | + |
1691 | +def lxd_start(container): |
1692 | + if container.status != 'Running': |
1693 | + container.start(wait=True) |
1694 | + |
1695 | + container.sync(rollback=True) # required for pylxd=2.0.x |
1696 | + |
1697 | + if container.status != 'Running': |
1698 | + return LifecycleResult("Container {} failed to start".format(container.name)) |
1699 | + |
1700 | + return LifecycleResult() |
1701 | + |
1702 | + |
1703 | +def lxd_stop(container, wait): |
1704 | + if container.status == 'Stopped': |
1705 | + return LifecycleResult() |
1706 | + |
1707 | + container.stop(wait=wait) |
1708 | + container.sync(rollback=True) # required for pylxd=2.0.x |
1709 | + |
1710 | + if wait and container.status != 'Stopped': |
1711 | + return LifecycleResult("Container {} failed to stop".format(container.name)) |
1712 | + |
1713 | + return LifecycleResult() |
1714 | + |
1715 | + |
1716 | +def update_bind_mounts(container, config): |
1717 | + home_path = os.environ['HOME'] |
1718 | + |
1719 | + container.devices.clear() |
1720 | + container.devices['root'] = {'type': 'disk', 'path': '/'} |
1721 | + |
1722 | + if os.path.exists(os.path.join(home_path, '.config', 'dconf')): |
1723 | + container.devices['dconf'] = { |
1724 | + 'type': 'disk', |
1725 | + 'source': os.path.join(home_path, '.config', 'dconf'), |
1726 | + 'path': os.path.join(home_path, '.config', 'dconf') |
1727 | + } |
1728 | + |
1729 | + run_user = '/run/user/{}'.format(os.getuid()) |
1730 | + container.devices[run_user] = {'source': run_user, 'path': '/var/tmp{}'.format(run_user), 'type': 'disk'} |
1731 | + |
1732 | + mounts = utils.get_common_xdg_user_directories() + \ |
1733 | + config.get_container_bind_mounts(container.name) |
1734 | + for user_dir in utils.generate_binding_directories(mounts, home_path): |
1735 | + if not os.path.exists(user_dir[0]): |
1736 | + utils.get_logger().warning('Bind-mount path \'{}\' does not exist.'.format(user_dir[0])) |
1737 | + continue |
1738 | + |
1739 | + if os.path.isabs(user_dir[1]): |
1740 | + path = user_dir[1] |
1741 | + else: |
1742 | + path = os.path.join(home_path, user_dir[1]) |
1743 | + |
1744 | + utils.get_logger().debug("Mounting {}:{} in container {}".format(user_dir[0], path, container.name)) |
1745 | + |
1746 | + container.devices[user_dir[1]] = { |
1747 | + 'source': _readlink(user_dir[0]), |
1748 | + 'path': path, |
1749 | + 'optional': 'true', |
1750 | + 'type': 'disk' |
1751 | + } |
1752 | + |
1753 | + try: |
1754 | + container.save(wait=True) |
1755 | + except pylxd.exceptions.LXDAPIException as e: |
1756 | + utils.get_logger().warning('Saving bind mounts for container \'{}\' raised: {}'.format(container.name, str(e))) |
1757 | + # This is most likely the result of the container currently running |
1758 | + |
1759 | + |
1760 | +def update_libertine_profile(client): |
1761 | + try: |
1762 | + profile = client.profiles.get('libertine') |
1763 | + |
1764 | + utils.get_logger().info('Updating existing lxd profile.') |
1765 | + profile.devices = _get_devices_map() |
1766 | + profile.config['raw.idmap'] = 'both 1000 1000' |
1767 | + |
1768 | + try: |
1769 | + profile.save() |
1770 | + except pylxd.exceptions.LXDAPIException as e: |
1771 | + utils.get_logger().warning('Saving libertine lxd profile raised: {}'.format(str(e))) |
1772 | + # This is most likely the result of an older container currently |
1773 | + # running and/or containing a conflicting device entry |
1774 | + except pylxd.exceptions.LXDAPIException: |
1775 | + utils.get_logger().info('Creating libertine lxd profile.') |
1776 | + client.profiles.create('libertine', config={'raw.idmap': 'both 1000 1000'}, devices=_get_devices_map()) |
1777 | + |
1778 | + |
1779 | +class LibertineLXD(Libertine.BaseContainer): |
1780 | + def __init__(self, name, config): |
1781 | + super().__init__(name) |
1782 | + self._id = name |
1783 | + self._config = config |
1784 | + self._host_info = HostInfo.HostInfo() |
1785 | + self._container = None |
1786 | + self._matchbox_pid = None |
1787 | + |
1788 | + if not _setup_lxd(): |
1789 | + raise Exception("Failed to setup lxd.") |
1790 | + |
1791 | + self._client = pylxd.Client() |
1792 | + self._window_manager = None |
1793 | + self.root_path = '{}/containers/{}/rootfs'.format(os.getenv('LXD_DIR', '/var/lib/lxd'), name) |
1794 | + |
1795 | + utils.set_session_dbus_env_var() |
1796 | + try: |
1797 | + bus = dbus.SessionBus() |
1798 | + self._manager = bus.get_object(get_lxd_manager_dbus_name(), get_lxd_manager_dbus_path()) |
1799 | + except dbus.exceptions.DBusException: |
1800 | + utils.get_logger().warning("D-Bus Service not found.") |
1801 | + self._manager = None |
1802 | + |
1803 | + |
1804 | + def create_libertine_container(self, password=None, multiarch=False): |
1805 | + if self._try_get_container(): |
1806 | + utils.get_logger().error("Container already exists") |
1807 | + return False |
1808 | + |
1809 | + update_libertine_profile(self._client) |
1810 | + |
1811 | + utils.get_logger().info("Creating container '%s' with distro '%s'" % (self._id, self.installed_release)) |
1812 | + create = subprocess.Popen(shlex.split('lxc launch ubuntu-daily:{distro} {id} --profile ' |
1813 | + 'default --profile libertine'.format( |
1814 | + distro=self.installed_release, id=self._id))) |
1815 | + if create.wait() is not 0: |
1816 | + utils.get_logger().error("Creating container '{}' failed with code '{}'".format(self._id, create.returncode)) |
1817 | + return False |
1818 | + |
1819 | + if not self.start_container(): |
1820 | + utils.get_logger().error("Failed to start container '{}'".format(self._id)) |
1821 | + self.destroy_libertine_container() |
1822 | + return False |
1823 | + |
1824 | + username = os.environ['USER'] |
1825 | + uid = str(os.getuid()) |
1826 | + self.run_in_container("userdel -r ubuntu") |
1827 | + self.run_in_container("useradd -u {} -U -p {} -G sudo,audio,video {}".format( |
1828 | + uid, crypt.crypt(''), username)) |
1829 | + self.run_in_container("mkdir -p /home/{}".format(username)) |
1830 | + self.run_in_container("chown {0}:{0} /home/{0}".format(username)) |
1831 | + |
1832 | + _setup_bind_mount_service(self._container, uid, username) |
1833 | + |
1834 | + if multiarch and self.architecture == 'amd64': |
1835 | + utils.get_logger().info("Adding i386 multiarch support to container '{}'".format(self._id)) |
1836 | + self.run_in_container("dpkg --add-architecture i386") |
1837 | + |
1838 | + self.update_packages() |
1839 | + |
1840 | + for package in self.default_packages: |
1841 | + utils.get_logger().info("Installing package '%s' in container '%s'" % (package, self._id)) |
1842 | + if not self.install_package(package, no_dialog=True, update_cache=False): |
1843 | + utils.get_logger().error("Failure installing '%s' during container creation" % package) |
1844 | + self.destroy_libertine_container() |
1845 | + return False |
1846 | + |
1847 | + return True |
1848 | + |
1849 | + def update_packages(self): |
1850 | + if not self._timezone_in_sync(): |
1851 | + utils.get_logger().info("Re-syncing timezones") |
1852 | + self.run_in_container("bash -c 'echo \"%s\" > /etc/timezone'" % self._host_info.get_host_timezone()) |
1853 | + self.run_in_container("rm -f /etc/localtime") |
1854 | + self.run_in_container("dpkg-reconfigure -f noninteractive tzdata") |
1855 | + |
1856 | + return super().update_packages() |
1857 | + |
1858 | + def destroy_libertine_container(self): |
1859 | + if not self._try_get_container(): |
1860 | + utils.get_logger().error("No such container '%s'" % self._id) |
1861 | + return False |
1862 | + |
1863 | + self.stop_container(wait=True) |
1864 | + self._container.delete() |
1865 | + return True |
1866 | + |
1867 | + def _timezone_in_sync(self): |
1868 | + proc = subprocess.Popen(self._lxc_args('cat /etc/timezone'), stdout=subprocess.PIPE) |
1869 | + out, err = proc.communicate() |
1870 | + return out.decode('UTF-8').strip('\n') == self._host_info.get_host_timezone() |
1871 | + |
1872 | + def _lxc_args(self, command, environ={}): |
1873 | + env_as_args = ' ' |
1874 | + for k, v in environ.items(): |
1875 | + env_as_args += '--env {}="{}" '.format(k, v) |
1876 | + |
1877 | + return shlex.split('lxc exec {}{}-- {}'.format(self._id, |
1878 | + env_as_args, |
1879 | + command)) |
1880 | + |
1881 | + def run_in_container(self, command): |
1882 | + proc = subprocess.Popen(self._lxc_args(command)) |
1883 | + return proc.wait() |
1884 | + |
1885 | + def start_container(self): |
1886 | + if not self._try_get_container(): |
1887 | + return False |
1888 | + |
1889 | + if self._manager: |
1890 | + result = LifecycleResult.from_dict(self._manager.operation_start(self._id)) |
1891 | + else: |
1892 | + result = lxd_start(self._container) |
1893 | + |
1894 | + if not result.success: |
1895 | + utils.get_logger().error(result.error) |
1896 | + return False |
1897 | + |
1898 | + if not _wait_for_network(self._container): |
1899 | + utils.get_logger().warning("Network unavailable in container '{}'".format(self._id)) |
1900 | + |
1901 | + return result.success |
1902 | + |
1903 | + def stop_container(self, wait=False): |
1904 | + if not self._try_get_container(): |
1905 | + return False |
1906 | + |
1907 | + if self._manager: |
1908 | + result = LifecycleResult.from_dict(self._manager.operation_stop(self._id)) |
1909 | + else: |
1910 | + result = lxd_stop(self._container, wait) |
1911 | + |
1912 | + if not result.success: |
1913 | + utils.get_logger().error(result.error) |
1914 | + |
1915 | + return result.success |
1916 | + |
1917 | + def _get_matchbox_pids(self): |
1918 | + p = subprocess.Popen(self._lxc_args('pgrep matchbox'), stdout=subprocess.PIPE) |
1919 | + out, err = p.communicate() |
1920 | + return out.decode('utf-8').split('\n') |
1921 | + |
1922 | + # FIXME: Remove once window management logic has been moved to the host |
1923 | + def _start_window_manager(self, args): |
1924 | + args.extend(utils.setup_window_manager(self._id)) |
1925 | + |
1926 | + if 'matchbox-window-manager' in args: |
1927 | + pids = self._get_matchbox_pids() |
1928 | + |
1929 | + self._window_manager = psutil.Popen(args) |
1930 | + |
1931 | + time.sleep(.25) # Give matchbox a moment to start in the container |
1932 | + if self._window_manager.poll() is not None: |
1933 | + utils.get_logger().warning("Window manager terminated prematurely with exit code '{}'".format( |
1934 | + self._window_manager.returncode)) |
1935 | + self._window_manager = None |
1936 | + elif 'matchbox-window-manager' in args: |
1937 | + after_pids = self._get_matchbox_pids() |
1938 | + if len(after_pids) > len(pids): |
1939 | + self._matchbox_pid = (set(after_pids) - set(pids)).pop() |
1940 | + |
1941 | + def start_application(self, app_exec_line, environ): |
1942 | + if not self._try_get_container(): |
1943 | + utils.get_logger().error("Could not get container '{}'".format(self._id)) |
1944 | + return None |
1945 | + |
1946 | + if self._manager: |
1947 | + result = LifecycleResult.from_dict(self._manager.app_start(self._id)) |
1948 | + else: |
1949 | + update_libertine_profile(self._client) |
1950 | + update_bind_mounts(self._container, self._config) |
1951 | + result = lxd_start(self._container) |
1952 | + |
1953 | + if not result.success: |
1954 | + utils.get_logger().error(result.error) |
1955 | + return False |
1956 | + |
1957 | + args = self._lxc_args("sudo -E -u {} env PATH={}".format(os.environ['USER'], environ['PATH']), environ) |
1958 | + |
1959 | + self._start_window_manager(args.copy()) |
1960 | + |
1961 | + args.extend(app_exec_line) |
1962 | + return psutil.Popen(args) |
1963 | + |
1964 | + def finish_application(self, app): |
1965 | + if self._window_manager is not None: |
1966 | + utils.terminate_window_manager(self._window_manager) |
1967 | + self._window_manager = None |
1968 | + |
1969 | + # This is a workaround for an issue on xenial where the process |
1970 | + # running the window manager is not killed with the application |
1971 | + if self._matchbox_pid is not None: |
1972 | + utils.get_logger().debug("Manually killing matchbox-window-manager") |
1973 | + self.run_in_container("kill -9 {}".format(self._matchbox_pid)) |
1974 | + self._matchbox_pid = None |
1975 | + |
1976 | + app.wait() |
1977 | + |
1978 | + if self._manager: |
1979 | + self._manager.app_stop(self.container_id) |
1980 | + else: |
1981 | + lxd_stop(self._container, False) |
1982 | + |
1983 | + def copy_file_to_container(self, source, dest): |
1984 | + with open(source, 'rb') as f: |
1985 | + return self._container.files.put(dest, f.read()) |
1986 | + |
1987 | + def delete_file_in_container(self, path): |
1988 | + return self.run_in_container('rm {}'.format(path)) |
1989 | + |
1990 | + def _try_get_container(self): |
1991 | + if self._container is None: |
1992 | + self._container = lxd_container(self._client, self._id) |
1993 | + |
1994 | + return self._container is not None |
1995 | |
1996 | === modified file 'python/libertine/launcher/session.py' |
1997 | --- python/libertine/launcher/session.py 2016-10-27 16:57:03 +0000 |
1998 | +++ python/libertine/launcher/session.py 2017-01-05 20:19:05 +0000 |
1999 | @@ -29,9 +29,6 @@ |
2000 | from .task import LaunchServiceTask, TaskType |
2001 | |
2002 | |
2003 | -log = utils.get_logger() |
2004 | - |
2005 | - |
2006 | def translate_to_real_address(abstract_address): |
2007 | """Translate the notional text address to a real UNIX-domain address string. |
2008 | |
2009 | @@ -101,12 +98,12 @@ |
2010 | b = from_socket.recv(4096) |
2011 | if len(b) > 0: |
2012 | to_socket.sendall(b) |
2013 | - log.debug('copied {} bytes from fd to {}'.format(len(b), from_socket, to_socket)) |
2014 | + utils.get_logger().debug('copied {} bytes from fd to {}'.format(len(b), from_socket, to_socket)) |
2015 | else: |
2016 | - log.info('close detected on {}'.format(from_socket)) |
2017 | + utils.get_logger().info('close detected on {}'.format(from_socket)) |
2018 | return len(b) |
2019 | except Exception as e: |
2020 | - log.debug(e) |
2021 | + utils.get_logger().debug(e) |
2022 | return 0 |
2023 | |
2024 | def _close_up_shop(self, session): |
2025 | @@ -172,7 +169,7 @@ |
2026 | with suppress(AttributeError): |
2027 | for task_config in self._config.prelaunch_tasks: |
2028 | if task_config.task_type == TaskType.LAUNCH_SERVICE: |
2029 | - log.info("launching {}".format(task_config.datum[0])) |
2030 | + utils.get_logger().info("launching {}".format(task_config.datum[0])) |
2031 | task = LaunchServiceTask(task_config) |
2032 | self._child_processes.append(task) |
2033 | task.start(self._config.host_environ) |
2034 | @@ -267,7 +264,7 @@ |
2035 | create a socket bridge to the host when a connection from the session is |
2036 | made. |
2037 | """ |
2038 | - log.debug('creating bridge listener for {} on {}'. |
2039 | + utils.get_logger().debug('creating bridge listener for {} on {}'. |
2040 | format(bridge_config.env_var, bridge_config.session_address)) |
2041 | sock = socket(AF_UNIX, SOCK_STREAM) |
2042 | sock.bind(translate_to_real_address(bridge_config.session_address)) |
2043 | @@ -286,7 +283,7 @@ |
2044 | """ |
2045 | (bridge_config, sock) = datum |
2046 | conn = sock.accept() |
2047 | - log.debug('connection of session socket {} accepted'.format(bridge_config.session_address)) |
2048 | + utils.get_logger().debug('connection of session socket {} accepted'.format(bridge_config.session_address)) |
2049 | self.add_bridge_pair(BridgePair(conn[0], bridge_config.host_address)) |
2050 | |
2051 | def _ensure_paths_exist(self): |
2052 | @@ -316,17 +313,17 @@ |
2053 | data = os.read(fd, 4) |
2054 | sig = struct.unpack('%uB' % len(data), data) |
2055 | if sig[0] == signal.SIGCHLD: |
2056 | - log.info('SIGCHLD received') |
2057 | + utils.get_logger().info('SIGCHLD received') |
2058 | if self._handle_child_died(): |
2059 | raise StopIteration('launched program exited') |
2060 | elif sig[0] == signal.SIGINT: |
2061 | - log.info('SIGINT received') |
2062 | + utils.get_logger().info('SIGINT received') |
2063 | raise StopIteration('keyboard interrupt') |
2064 | elif sig[0] == signal.SIGTERM: |
2065 | - log.info('SIGTERM received') |
2066 | + utils.get_logger().info('SIGTERM received') |
2067 | raise StopIteration('terminate') |
2068 | else: |
2069 | - log.warning('unknown signal {} received'.format(sig[0])) |
2070 | + utils.get_logger().warning('unknown signal {} received'.format(sig[0])) |
2071 | |
2072 | def _set_signal_handlers(self): |
2073 | """Set the signal handlers.""" |
2074 | |
2075 | === modified file 'python/libertine/launcher/task.py' |
2076 | --- python/libertine/launcher/task.py 2016-10-27 17:25:08 +0000 |
2077 | +++ python/libertine/launcher/task.py 2017-01-05 20:19:05 +0000 |
2078 | @@ -44,7 +44,7 @@ |
2079 | |
2080 | def __init__(self, config): |
2081 | """ |
2082 | - :param config: The task configuraiton. |
2083 | + :param config: The task configuration. |
2084 | :type config: TaskConfig |
2085 | |
2086 | The constructor unpacks the service commandline from the config datum. |
2087 | @@ -54,13 +54,13 @@ |
2088 | |
2089 | def start(self, environ=None): |
2090 | """Start the service. |
2091 | - |
2092 | + |
2093 | :param env: An alternate environment dictionary. |
2094 | """ |
2095 | self._process = Popen(self._command_line, env=environ) |
2096 | |
2097 | def stop(self): |
2098 | - """Shuts the service down. """ |
2099 | + """Shuts the service down.""" |
2100 | try: |
2101 | self._process.terminate() |
2102 | except ProcessLookupError: |
2103 | |
2104 | === added directory 'python/libertine/lifecycle' |
2105 | === added file 'python/libertine/lifecycle/ContainerLifecycleService.py' |
2106 | --- python/libertine/lifecycle/ContainerLifecycleService.py 1970-01-01 00:00:00 +0000 |
2107 | +++ python/libertine/lifecycle/ContainerLifecycleService.py 2017-01-05 20:19:05 +0000 |
2108 | @@ -0,0 +1,108 @@ |
2109 | +#!/usr/bin/python3 |
2110 | +# -*- coding: utf-8 -*- |
2111 | + |
2112 | +# Copyright (C) 2016 Canonical Ltd. |
2113 | + |
2114 | +# This program is free software: you can redistribute it and/or modify |
2115 | +# it under the terms of the GNU General Public License as published by |
2116 | +# the Free Software Foundation; version 3 of the License. |
2117 | +# |
2118 | +# This program is distributed in the hope that it will be useful, |
2119 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2120 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2121 | +# GNU General Public License for more details. |
2122 | +# |
2123 | +# You should have received a copy of the GNU General Public License |
2124 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2125 | + |
2126 | + |
2127 | +import dbus.exceptions |
2128 | +import dbus.service |
2129 | + |
2130 | +from .LifecycleResult import LifecycleResult |
2131 | +from collections import Counter |
2132 | +from dbus.mainloop.glib import DBusGMainLoop |
2133 | +from libertine import utils |
2134 | + |
2135 | + |
2136 | +LIBERTINE_CONTAINER_LIFECYCLE_INTERFACE = 'com.canonical.libertine.ContainerLifecycle' |
2137 | + |
2138 | + |
2139 | +class ContainerLifecycleService(dbus.service.Object): |
2140 | + def __init__(self, service_name, service_path): |
2141 | + self._apps = Counter() |
2142 | + self._operations = Counter() |
2143 | + |
2144 | + DBusGMainLoop(set_as_default=True) |
2145 | + try: |
2146 | + bus_name = dbus.service.BusName(service_name, |
2147 | + bus=dbus.SessionBus(), |
2148 | + do_not_queue=True) |
2149 | + except dbus.exceptions.NameExistsException: |
2150 | + utils.get_logger().error("service is already running") |
2151 | + raise |
2152 | + super().__init__(bus_name, service_path) |
2153 | + |
2154 | + def start(self, container): |
2155 | + raise NotImplementedError("Subclasses must implement start(container)") |
2156 | + |
2157 | + def stop(self, container): |
2158 | + raise NotImplementedError("Subclasses must implement stop(container)") |
2159 | + |
2160 | + @dbus.service.method(LIBERTINE_CONTAINER_LIFECYCLE_INTERFACE, |
2161 | + in_signature='s', |
2162 | + out_signature='a{ss}') |
2163 | + def app_start(self, container): |
2164 | + utils.get_logger().debug("app_start({})".format(container)) |
2165 | + if self._operations[container] != 0: |
2166 | + return LifecycleResult("Libertine container operation already running: cannot launch application.") |
2167 | + |
2168 | + result = self.start(container, True) |
2169 | + |
2170 | + if result.success: |
2171 | + self._apps[container] += 1 |
2172 | + |
2173 | + return result.to_dict() |
2174 | + |
2175 | + @dbus.service.method(LIBERTINE_CONTAINER_LIFECYCLE_INTERFACE, |
2176 | + in_signature='s', |
2177 | + out_signature='a{ss}') |
2178 | + def app_stop(self, container): |
2179 | + utils.get_logger().debug("app_stop({})".format(container)) |
2180 | + self._apps[container] -= 1 |
2181 | + result = LifecycleResult() |
2182 | + |
2183 | + if self._apps[container] == 0: |
2184 | + result = self.stop(container) |
2185 | + del self._apps[container] |
2186 | + |
2187 | + return result.to_dict() |
2188 | + |
2189 | + @dbus.service.method(LIBERTINE_CONTAINER_LIFECYCLE_INTERFACE, |
2190 | + in_signature='s', |
2191 | + out_signature='a{ss}') |
2192 | + def operation_start(self, container): |
2193 | + utils.get_logger().debug("operation_start({})".format(container)) |
2194 | + if self._apps[container] != 0: |
2195 | + return LifecycleResult("Application already running in container: cannot run operation.") |
2196 | + |
2197 | + result = self.start(container, False) |
2198 | + |
2199 | + if result.success: |
2200 | + self._operations[container] += 1 |
2201 | + |
2202 | + return result.to_dict() |
2203 | + |
2204 | + @dbus.service.method(LIBERTINE_CONTAINER_LIFECYCLE_INTERFACE, |
2205 | + in_signature='s', |
2206 | + out_signature='a{ss}') |
2207 | + def operation_stop(self, container): |
2208 | + utils.get_logger().debug("operation_stop({})".format(container)) |
2209 | + self._operations[container] -= 1 |
2210 | + result = LifecycleResult() |
2211 | + |
2212 | + if self._operations[container] == 0: |
2213 | + result = self.stop(container) |
2214 | + del self._operations[container] |
2215 | + |
2216 | + return result.to_dict() |
2217 | |
2218 | === added file 'python/libertine/lifecycle/ContainerLifecycleServiceRunner.py' |
2219 | --- python/libertine/lifecycle/ContainerLifecycleServiceRunner.py 1970-01-01 00:00:00 +0000 |
2220 | +++ python/libertine/lifecycle/ContainerLifecycleServiceRunner.py 2017-01-05 20:19:05 +0000 |
2221 | @@ -0,0 +1,46 @@ |
2222 | +#!/usr/bin/python3 |
2223 | +# -*- coding: utf-8 -*- |
2224 | + |
2225 | +# Copyright (C) 2016 Canonical Ltd. |
2226 | + |
2227 | +# This program is free software: you can redistribute it and/or modify |
2228 | +# it under the terms of the GNU General Public License as published by |
2229 | +# the Free Software Foundation; version 3 of the License. |
2230 | +# |
2231 | +# This program is distributed in the hope that it will be useful, |
2232 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2233 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2234 | +# GNU General Public License for more details. |
2235 | +# |
2236 | +# You should have received a copy of the GNU General Public License |
2237 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2238 | + |
2239 | +import signal |
2240 | + |
2241 | +from gi.repository import GLib |
2242 | +from libertine import utils |
2243 | + |
2244 | + |
2245 | +class ContainerLifecycleServiceRunner(object): |
2246 | + def __init__(self, service): |
2247 | + self._service = service |
2248 | + |
2249 | + def _sigterm(self, sig): |
2250 | + utils.get_logger().warning("Received SIGTERM") |
2251 | + self._shutdown() |
2252 | + |
2253 | + def _shutdown(self): |
2254 | + utils.get_logger().info("Shutting down") |
2255 | + GLib.MainLoop().quit() |
2256 | + |
2257 | + def run(self): |
2258 | + GLib.unix_signal_add(GLib.PRIORITY_HIGH, |
2259 | + signal.SIGTERM, |
2260 | + self._sigterm, |
2261 | + None) |
2262 | + |
2263 | + try: |
2264 | + utils.get_logger().info("Starting main loop") |
2265 | + GLib.MainLoop().run() |
2266 | + except KeyboardInterrupt: |
2267 | + self._shutdown() |
2268 | |
2269 | === added file 'python/libertine/lifecycle/LifecycleResult.py' |
2270 | --- python/libertine/lifecycle/LifecycleResult.py 1970-01-01 00:00:00 +0000 |
2271 | +++ python/libertine/lifecycle/LifecycleResult.py 2017-01-05 20:19:05 +0000 |
2272 | @@ -0,0 +1,37 @@ |
2273 | +#!/usr/bin/python3 |
2274 | +# -*- coding: utf-8 -*- |
2275 | + |
2276 | +# Copyright (C) 2016 Canonical Ltd. |
2277 | + |
2278 | +# This program is free software: you can redistribute it and/or modify |
2279 | +# it under the terms of the GNU General Public License as published by |
2280 | +# the Free Software Foundation; version 3 of the License. |
2281 | +# |
2282 | +# This program is distributed in the hope that it will be useful, |
2283 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2284 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2285 | +# GNU General Public License for more details. |
2286 | +# |
2287 | +# You should have received a copy of the GNU General Public License |
2288 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2289 | + |
2290 | +class LifecycleResult(object): |
2291 | + def __init__(self, error=''): |
2292 | + self._error = error |
2293 | + |
2294 | + @property |
2295 | + def error(self): |
2296 | + return self._error or '' |
2297 | + |
2298 | + @property |
2299 | + def success(self): |
2300 | + return self.error == '' |
2301 | + |
2302 | + @classmethod |
2303 | + def from_dict(kls, d): |
2304 | + return LifecycleResult(d.get('error', None)) |
2305 | + |
2306 | + def to_dict(self): |
2307 | + return { |
2308 | + 'error': self.error |
2309 | + } |
2310 | |
2311 | === added file 'python/libertine/lifecycle/__init__.py' |
2312 | --- python/libertine/lifecycle/__init__.py 1970-01-01 00:00:00 +0000 |
2313 | +++ python/libertine/lifecycle/__init__.py 2017-01-05 20:19:05 +0000 |
2314 | @@ -0,0 +1,23 @@ |
2315 | +# Copyright 2016 Canonical Ltd. |
2316 | +# |
2317 | +# This program is free software: you can redistribute it and/or modify it |
2318 | +# under the terms of the GNU General Public License version 3, as published |
2319 | +# by the Free Software Foundation. |
2320 | +# |
2321 | +# This program is distributed in the hope that it will be useful, but |
2322 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
2323 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2324 | +# PURPOSE. See the GNU General Public License for more details. |
2325 | +# |
2326 | +# You should have received a copy of the GNU General Public License along |
2327 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
2328 | + |
2329 | +from .ContainerLifecycleServiceRunner import ContainerLifecycleServiceRunner |
2330 | +from .ContainerLifecycleService import ContainerLifecycleService |
2331 | +from .LifecycleResult import LifecycleResult |
2332 | + |
2333 | +__all__ = [ |
2334 | + 'ContainerLifecycleServiceRunner', |
2335 | + 'ContainerLifecycleService', |
2336 | + 'LifecycleResult' |
2337 | + ] |
2338 | |
2339 | === modified file 'python/libertine/utils.py' |
2340 | --- python/libertine/utils.py 2016-12-02 16:38:11 +0000 |
2341 | +++ python/libertine/utils.py 2017-01-05 20:19:05 +0000 |
2342 | @@ -25,6 +25,7 @@ |
2343 | require_version('Libertine', '1') |
2344 | from gi.repository import Libertine |
2345 | |
2346 | + |
2347 | def get_logger(): |
2348 | logger = logging.getLogger('__libertine_logger__') |
2349 | |
2350 | @@ -40,13 +41,27 @@ |
2351 | logger.addHandler(stream_handler) |
2352 | |
2353 | if 'LIBERTINE_DEBUG' in os.environ: |
2354 | - logger.setLevel(logging.DEBUG) |
2355 | + if os.environ['LIBERTINE_DEBUG'] == '0': |
2356 | + logger.setLevel(logging.WARNING) |
2357 | + elif os.environ['LIBERTINE_DEBUG'] == '1': |
2358 | + logger.setLevel(logging.INFO) |
2359 | + else: |
2360 | + logger.setLevel(logging.DEBUG) |
2361 | else: |
2362 | logger.setLevel(logging.WARNING) |
2363 | |
2364 | return logger |
2365 | |
2366 | |
2367 | +def set_environmental_verbosity(verbosity): |
2368 | + # Set debug levels if not overridden in environment |
2369 | + if 'LIBERTINE_DEBUG' not in os.environ: |
2370 | + if verbosity is None: |
2371 | + os.environ['LIBERTINE_DEBUG'] = '1' |
2372 | + else: |
2373 | + os.environ['LIBERTINE_DEBUG'] = str(verbosity) |
2374 | + |
2375 | + |
2376 | def get_libertine_container_rootfs_path(container_id): |
2377 | path = Libertine.container_path(container_id) |
2378 | |
2379 | @@ -112,8 +127,8 @@ |
2380 | |
2381 | name = dir |
2382 | if name.startswith(prefix): |
2383 | - name = name.replace(prefix, '', 1) |
2384 | - name = name.lstrip('/') |
2385 | + name = name.replace(prefix, '', 1).lstrip('/') |
2386 | + |
2387 | if name in names: |
2388 | binding_dirs.append((dir, "%s (%i)" % (name, names.count(name)))) |
2389 | else: |
2390 | @@ -201,3 +216,9 @@ |
2391 | return True |
2392 | else: |
2393 | return False |
2394 | + |
2395 | + |
2396 | +def get_deb_package_name(package): |
2397 | + pkg_name_proc = subprocess.Popen(shlex.split('dpkg --field {} Package'.format(package)), stdout=subprocess.PIPE) |
2398 | + out, err = pkg_name_proc.communicate() |
2399 | + return out.decode('utf-8').strip() |
2400 | |
2401 | === added file 'snapcraft.yaml' |
2402 | --- snapcraft.yaml 1970-01-01 00:00:00 +0000 |
2403 | +++ snapcraft.yaml 2017-01-05 20:19:05 +0000 |
2404 | @@ -0,0 +1,32 @@ |
2405 | +name: libertine |
2406 | +version: "1.5" |
2407 | +summary: Libertine suite |
2408 | +description: | |
2409 | + Suite for maintaining deb-based applications in a non-deb environment |
2410 | +confinement: devmode |
2411 | +grade: devel |
2412 | + |
2413 | +apps: |
2414 | + libertine-container-manager: |
2415 | + command: usr/bin/snap-runner.wrapper libertine-container-manager |
2416 | + libertine-manager-app: |
2417 | + command: usr/bin/snap-runner.wrapper libertine-manager-app |
2418 | + libertine-launch: |
2419 | + command: usr/bin/snap-runner.wrapper libertine-launch |
2420 | + |
2421 | + # For debugging purposes |
2422 | + bash: |
2423 | + command: usr/bin/snap-runner.wrapper bash |
2424 | + |
2425 | +parts: |
2426 | + libertine-source: |
2427 | + plugin: libertine |
2428 | + source: . |
2429 | + after: |
2430 | + - desktop-qt5 |
2431 | + libertine-deps: |
2432 | + plugin: libertine-deps |
2433 | + env: |
2434 | + plugin: dump |
2435 | + organize: |
2436 | + data/snap-runner.wrapper: usr/bin/snap-runner.wrapper |
2437 | |
2438 | === modified file 'tests/unit/test_libertine_gir.py' |
2439 | --- tests/unit/test_libertine_gir.py 2016-08-28 00:17:54 +0000 |
2440 | +++ tests/unit/test_libertine_gir.py 2017-01-05 20:19:05 +0000 |
2441 | @@ -49,14 +49,12 @@ |
2442 | self.assertThat(container_home_path, Equals(self.cmake_source_dir + '/libertine-home/libertine-container/user-data/wily')) |
2443 | |
2444 | def test_container_name(self): |
2445 | - container_id = 'wily' |
2446 | with patch.dict('os.environ', {'XDG_DATA_HOME': self.cmake_source_dir + '/libertine-config'}): |
2447 | - container_name = Libertine.container_name(container_id) |
2448 | + container_name = Libertine.container_name('wily') |
2449 | |
2450 | self.assertThat(container_name, Equals("Ubuntu 'Wily Werewolf'")) |
2451 | |
2452 | - container_id = 'wily-2' |
2453 | - container_name = Libertine.container_name(container_id) |
2454 | + container_name = Libertine.container_name('wily-2') |
2455 | |
2456 | self.assertThat(container_name, Equals("Ubuntu 'Wily Werewolf' (2)")) |
2457 | |
2458 | |
2459 | === modified file 'tests/unit/test_logger.py' |
2460 | --- tests/unit/test_logger.py 2016-08-25 01:49:07 +0000 |
2461 | +++ tests/unit/test_logger.py 2017-01-05 20:19:05 +0000 |
2462 | @@ -34,6 +34,11 @@ |
2463 | def test_logger_on_with_env_var(self): |
2464 | with patch.dict('os.environ', {'LIBERTINE_DEBUG': '1'}): |
2465 | l = libertine.utils.get_logger() |
2466 | + self.assertThat(l.getEffectiveLevel(), Equals(logging.INFO)) |
2467 | + |
2468 | + def test_logger_on_with_env_var(self): |
2469 | + with patch.dict('os.environ', {'LIBERTINE_DEBUG': '2'}): |
2470 | + l = libertine.utils.get_logger() |
2471 | self.assertThat(l.getEffectiveLevel(), Equals(logging.DEBUG)) |
2472 | |
2473 | def test_logger_only_inits_once(self): |
2474 | @@ -42,4 +47,3 @@ |
2475 | l2 = libertine.utils.get_logger() |
2476 | l3 = libertine.utils.get_logger() |
2477 | self.assertThat(len(l3.handlers), Equals(1)) |
2478 | - |
2479 | |
2480 | === modified file 'tools/CMakeLists.txt' |
2481 | --- tools/CMakeLists.txt 2016-10-28 19:39:13 +0000 |
2482 | +++ tools/CMakeLists.txt 2017-01-05 20:19:05 +0000 |
2483 | @@ -1,10 +1,9 @@ |
2484 | -install(PROGRAMS libertine-container-manager libertine-launch libertine-lxc-manager libertine-xmir libertine-lxc-setup libertined |
2485 | +install(PROGRAMS libertine-container-manager libertine-launch libertine-lxc-manager libertine-lxd-manager |
2486 | + libertine-xmir libertine-lxc-setup libertine-lxd-setup libertined |
2487 | DESTINATION ${CMAKE_INSTALL_BINDIR}) |
2488 | -install(FILES libertine-launch.1 libertine-container-manager.1 libertine-lxc-manager.1 libertine-xmir.1 |
2489 | +install(FILES libertine-launch.1 libertine-container-manager.1 libertine-lxc-manager.1 |
2490 | + libertine-lxd-manager.1 libertine-xmir.1 |
2491 | DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 |
2492 | COMPONENT doc) |
2493 | -install(PROGRAMS update-puritine-containers |
2494 | - DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/libertine) |
2495 | - |
2496 | install(FILES completions/libertine-container-manager |
2497 | DESTINATION ${DESTDIR}/usr/share/bash-completion/completions/) |
2498 | |
2499 | === modified file 'tools/libertine-container-manager' |
2500 | --- tools/libertine-container-manager 2016-11-16 17:33:42 +0000 |
2501 | +++ tools/libertine-container-manager 2017-01-05 20:19:05 +0000 |
2502 | @@ -23,7 +23,6 @@ |
2503 | import sys |
2504 | import re |
2505 | |
2506 | -from apt.debfile import DebPackage |
2507 | from libertine import LibertineContainer |
2508 | from libertine.ContainersConfig import ContainersConfig |
2509 | from libertine.HostInfo import HostInfo |
2510 | @@ -39,22 +38,22 @@ |
2511 | password = None |
2512 | |
2513 | if args.distro and not self.host_info.is_distro_valid(args.distro, args.force): |
2514 | - print("Invalid distro %s" % args.distro, file=sys.stderr) |
2515 | + libertine.utils.get_logger().error("Invalid distro %s" % args.distro) |
2516 | sys.exit(1) |
2517 | |
2518 | if self.containers_config.container_exists(args.id): |
2519 | - print("Container id '%s' is already used." % args.id, file=sys.stderr) |
2520 | + libertine.utils.get_logger().error("Container id '%s' is already used." % args.id) |
2521 | sys.exit(1) |
2522 | elif re.match("^[a-z0-9][a-z0-9+.-]+$", args.id) is None: |
2523 | - print("Container id '%s' invalid. ID must be of form ([a-z0-9][a-z0-9+.-]+)." % args.id, file=sys.stderr) |
2524 | + libertine.utils.get_logger().error("Container id '%s' invalid. ID must be of form ([a-z0-9][a-z0-9+.-]+)." % args.id) |
2525 | sys.exit(1) |
2526 | |
2527 | if not args.type: |
2528 | container_type = self.host_info.select_container_type_by_kernel() |
2529 | else: |
2530 | - if args.type == 'lxc' and not self.host_info.has_lxc_support(): |
2531 | - print("System kernel does not support lxc type containers. " |
2532 | - "Please either use chroot or omit the -t option.") |
2533 | + if (args.type == 'lxc' or args.type == 'lxd') and not self.host_info.has_lxc_support(): |
2534 | + libertine.utils.get_logger().error("System kernel does not support lxc type containers. " |
2535 | + "Please either use chroot or omit the -t option.") |
2536 | sys.exit(1) |
2537 | container_type = args.type |
2538 | |
2539 | @@ -64,8 +63,8 @@ |
2540 | host_distro = self.host_info.get_host_distro_release() |
2541 | |
2542 | if args.distro != host_distro: |
2543 | - print("The container distribution needs to match the host ditribution for chroot" |
2544 | - " based containers. Please either use \'%s\' or omit the -d/--distro option." |
2545 | + libertine.utils.get_logger().error("The container distribution needs to match the host ditribution for chroot" |
2546 | + " based containers. Please either use \'%s\' or omit the -d/--distro option." |
2547 | % host_distro) |
2548 | sys.exit(1) |
2549 | |
2550 | @@ -88,11 +87,18 @@ |
2551 | multiarch = 'enabled' |
2552 | self.containers_config.update_container_multiarch_support(args.id, multiarch) |
2553 | |
2554 | - container = LibertineContainer(args.id) |
2555 | - self.containers_config.update_container_install_status(args.id, "installing") |
2556 | - if not container.create_libertine_container(password, args.multiarch, args.verbosity): |
2557 | + try: |
2558 | + container = LibertineContainer(args.id) |
2559 | + self.containers_config.update_container_install_status(args.id, "installing") |
2560 | + if not container.create_libertine_container(password, args.multiarch): |
2561 | + libertine.utils.get_logger().error("Failed to create container") |
2562 | + self.containers_config.delete_container(args.id) |
2563 | + sys.exit(1) |
2564 | + except Exception as e: |
2565 | + libertine.utils.get_logger().error("Failed to create container: '{}'".format(str(e))) |
2566 | self.containers_config.delete_container(args.id) |
2567 | sys.exit(1) |
2568 | + |
2569 | self.containers_config.update_container_install_status(args.id, "ready") |
2570 | |
2571 | libertine.utils.refresh_libertine_scope() |
2572 | @@ -119,16 +125,16 @@ |
2573 | |
2574 | if is_debian_package: |
2575 | if os.path.exists(args.package): |
2576 | - package = DebPackage(args.package).pkgname |
2577 | + package = libertine.utils.get_deb_package_name(args.package) |
2578 | else: |
2579 | - print("%s does not exist." % args.package) |
2580 | + libertine.utils.get_logger().error("%s does not exist." % args.package) |
2581 | sys.exit(1) |
2582 | else: |
2583 | package = args.package |
2584 | |
2585 | if self.containers_config.package_exists(container_id, package): |
2586 | if not is_debian_package: |
2587 | - print("Package \'%s\' is already installed." % package) |
2588 | + libertine.utils.get_logger().error("Package '%s' is already installed." % package) |
2589 | sys.exit(1) |
2590 | else: |
2591 | self.containers_config.add_new_package(container_id, package) |
2592 | @@ -136,7 +142,9 @@ |
2593 | container = LibertineContainer(container_id) |
2594 | |
2595 | self.containers_config.update_package_install_status(container_id, package, "installing") |
2596 | - if not container.install_package(args.package, args.verbosity, args.no_dialog): |
2597 | + if not container.install_package(args.package, args.no_dialog): |
2598 | + libertine.utils.get_logger().error("Package '{}' failed to install in container '{}'" |
2599 | + .format(package, container_id)) |
2600 | self.containers_config.delete_package(container_id, package) |
2601 | sys.exit(1) |
2602 | |
2603 | @@ -144,12 +152,12 @@ |
2604 | |
2605 | libertine.utils.refresh_libertine_scope() |
2606 | |
2607 | - def remove_package_by_name(self, container_id, package_name, verbosity=1, no_dialog=False): |
2608 | + def remove_package_by_name(self, container_id, package_name, no_dialog=False): |
2609 | fallback_status = self.containers_config.get_package_install_status(container_id, package_name) |
2610 | self.containers_config.update_package_install_status(container_id, package_name, "removing") |
2611 | |
2612 | container = LibertineContainer(container_id) |
2613 | - if not container.remove_package(package_name, verbosity, no_dialog) and fallback_status == 'installed': |
2614 | + if not container.remove_package(package_name, no_dialog) and fallback_status == 'installed': |
2615 | self.containers_config.update_package_install_status(container_id, package_name, fallback_status) |
2616 | return False |
2617 | |
2618 | @@ -162,10 +170,12 @@ |
2619 | container_id = self.containers_config.check_container_id(args.id) |
2620 | |
2621 | if self.containers_config.get_package_install_status(container_id, args.package) != 'installed': |
2622 | - print("Package \'%s\' is not installed." % args.package) |
2623 | + libertine.utils.get_logger().error("Package \'%s\' is not installed." % args.package) |
2624 | sys.exit(1) |
2625 | |
2626 | - if not self.remove_package_by_name(container_id, args.package, args.verbosity, args.no_dialog): |
2627 | + if not self.remove_package_by_name(container_id, args.package, args.no_dialog): |
2628 | + libertine.utils.get_logger().error("Package '{}' failed to be removed from container '{}'" |
2629 | + .format(args.package, container_id)) |
2630 | sys.exit(1) |
2631 | |
2632 | libertine.utils.refresh_libertine_scope() |
2633 | @@ -175,6 +185,8 @@ |
2634 | |
2635 | container = LibertineContainer(container_id) |
2636 | if container.search_package_cache(args.search_string) is not 0: |
2637 | + libertine.utils.get_logger().error("Search for '{}' in container '{}' exited with non-zero status" |
2638 | + .format(args.id, args.search_string)) |
2639 | sys.exit(1) |
2640 | |
2641 | def update(self, args): |
2642 | @@ -183,7 +195,7 @@ |
2643 | container = LibertineContainer(container_id) |
2644 | |
2645 | self.containers_config.update_container_install_status(container_id, "updating") |
2646 | - if not container.update_libertine_container(args.verbosity): |
2647 | + if not container.update_libertine_container(): |
2648 | self.containers_config.update_container_install_status(container_id, "ready") |
2649 | sys.exit(1) |
2650 | |
2651 | @@ -208,10 +220,10 @@ |
2652 | if not container.exec_command(args.command): |
2653 | sys.exit(1) |
2654 | |
2655 | - def delete_archive_by_name(self, container_id, archive_name, verbosity=1): |
2656 | + def delete_archive_by_name(self, container_id, archive_name): |
2657 | if self.containers_config.get_archive_install_status(container_id, archive_name) == 'installed': |
2658 | self.containers_config.update_archive_install_status(container_id, archive_name, 'removing') |
2659 | - if LibertineContainer(container_id).configure_remove_archive("\"" + archive_name + "\"", verbosity) is not 0: |
2660 | + if LibertineContainer(container_id).configure_remove_archive("\"" + archive_name + "\"") is not 0: |
2661 | self.containers_config.update_archive_install_status(container_id, archive_name, 'installed') |
2662 | return False |
2663 | |
2664 | @@ -230,17 +242,17 @@ |
2665 | |
2666 | current_multiarch = self.containers_config.get_container_multiarch_support(container_id) |
2667 | if current_multiarch == multiarch: |
2668 | - print("i386 multiarch support is already %s" % multiarch) |
2669 | + libertine.utils.get_logger().error("i386 multiarch support is already %s" % multiarch) |
2670 | sys.exit(1) |
2671 | |
2672 | - if container.configure_multiarch(args.multiarch, args.verbosity) is not 0: |
2673 | + if container.configure_multiarch(args.multiarch) is not 0: |
2674 | sys.exit(1) |
2675 | |
2676 | self.containers_config.update_container_multiarch_support(container_id, multiarch) |
2677 | |
2678 | elif args.archive is not None: |
2679 | if args.archive_name is None: |
2680 | - print("Configure archive called with no archive name. See configure --help for usage.") |
2681 | + libertine.utils.get_logger().error("Configure archive called with no archive name. See configure --help for usage.") |
2682 | sys.exit(1) |
2683 | |
2684 | archive_name = args.archive_name.strip("\'\"") |
2685 | @@ -248,12 +260,12 @@ |
2686 | |
2687 | if args.archive == 'add': |
2688 | if self.containers_config.archive_exists(container_id, archive_name): |
2689 | - print("%s already added in container." % archive_name) |
2690 | + libertine.utils.get_logger().error("%s already added in container." % archive_name) |
2691 | sys.exit(1) |
2692 | |
2693 | self.containers_config.add_container_archive(container_id, archive_name) |
2694 | self.containers_config.update_archive_install_status(container_id, archive_name, 'installing') |
2695 | - if container.configure_add_archive(archive_name_esc, args.public_key_file, args.verbosity) is not 0: |
2696 | + if container.configure_add_archive(archive_name_esc, args.public_key_file) is not 0: |
2697 | self.containers_config.delete_container_archive(container_id, archive_name) |
2698 | sys.exit(1) |
2699 | |
2700 | @@ -261,48 +273,47 @@ |
2701 | |
2702 | elif args.archive == 'remove': |
2703 | if not self.containers_config.archive_exists(container_id, archive_name): |
2704 | - print("%s is not added in container." % archive_name) |
2705 | + libertine.utils.get_logger().error("%s is not added in container." % archive_name) |
2706 | sys.exit(1) |
2707 | |
2708 | if not self.delete_archive_by_name(container_id, archive_name): |
2709 | - print("%s was not properly deleted." % archive_name) |
2710 | + libertine.utils.get_logger().error("%s was not properly deleted." % archive_name) |
2711 | sys.exit(1) |
2712 | |
2713 | elif args.bind_mount is not None: |
2714 | if args.mount_path is None: |
2715 | - print("Configure bind-mounts called without mount path. See configure --help for usage") |
2716 | + libertine.utils.get_logger().error("Configure bind-mounts called without mount path. See configure --help for usage") |
2717 | sys.exit(1) |
2718 | |
2719 | mount_path = args.mount_path.rstrip('/') |
2720 | |
2721 | # validate bind-mount |
2722 | if not mount_path.startswith(os.environ['HOME']) and not mount_path.startswith('/media/%s' % os.environ['USER']): |
2723 | - print("Cannot mount '%s', mount path must be in $HOME or /media/$USER." % mount_path) |
2724 | + libertine.utils.get_logger().error("Cannot mount {}, mount path must be in {} or /media/{}.".format(mount_path, os.environ['HOME'], os.environ['USER'])) |
2725 | sys.exit(1) |
2726 | if mount_path.startswith('/media/%s' % os.environ['USER']) and \ |
2727 | self.containers_config.get_container_type(container_id) == 'lxc': |
2728 | - print("/media mounts not currently supported in lxc.") |
2729 | + libertine.utils.get_logger().error("/media mounts not currently supported in lxc.") |
2730 | sys.exit(1) |
2731 | if not os.path.isdir(mount_path): |
2732 | - print("Cannot mount '%s', mount path must be an existing directory." % mount_path) |
2733 | + libertine.utils.get_logger().error("Cannot mount '%s', mount path must be an existing directory." % mount_path) |
2734 | sys.exit(1) |
2735 | |
2736 | # update database with new bind-mount |
2737 | container_bind_mounts = self.containers_config.get_container_bind_mounts(container_id) |
2738 | if args.bind_mount == 'add': |
2739 | if mount_path in container_bind_mounts: |
2740 | - print("Cannot add mount '%s', bind-mount already exists." % mount_path) |
2741 | + libertine.utils.get_logger().error("Cannot add mount '%s', bind-mount already exists." % mount_path) |
2742 | sys.exit(1) |
2743 | self.containers_config.add_new_bind_mount(container_id, mount_path) |
2744 | elif args.bind_mount == 'remove': |
2745 | if mount_path not in container_bind_mounts: |
2746 | - print("Cannot remove mount '%s', bind-mount does not exist." % mount_path) |
2747 | + libertine.utils.get_logger().error("Cannot remove mount '%s', bind-mount does not exist." % mount_path) |
2748 | sys.exit(1) |
2749 | self.containers_config.delete_bind_mount(container_id, mount_path) |
2750 | |
2751 | - |
2752 | else: |
2753 | - print("Configure called with no subcommand. See configure --help for usage.") |
2754 | + libertine.utils.get_logger().error("Configure called with no subcommand. See configure --help for usage.") |
2755 | sys.exit(1) |
2756 | |
2757 | |
2758 | @@ -340,17 +351,17 @@ |
2759 | parser = argparse.ArgumentParser(description="Legacy X application support for Unity 8") |
2760 | |
2761 | if not os.geteuid(): |
2762 | - print("Please do not run %s using sudo" % parser.prog) |
2763 | + libertine.utils.get_logger().error("Please do not run %s using sudo" % parser.prog) |
2764 | sys.exit(1) |
2765 | |
2766 | container_manager = LibertineContainerManager() |
2767 | |
2768 | parser.add_argument('-q', '--quiet', |
2769 | action='store_const', dest='verbosity', const=0, |
2770 | - help=('do not print status updates on stdout')) |
2771 | - parser.add_argument('-v', '--verbose', |
2772 | + help=('disables all non-vital output')) |
2773 | + parser.add_argument('-v', '--verbosity', |
2774 | action='store_const', dest='verbosity', const=2, |
2775 | - help=('extra verbose output')) |
2776 | + help=('enables debug output')) |
2777 | subparsers = parser.add_subparsers(dest="subparser_name", |
2778 | title="subcommands", |
2779 | metavar='create, destroy, install-package, remove-package, search-cache, update, list, list-apps, configure') |
2780 | @@ -556,8 +567,8 @@ |
2781 | |
2782 | # Actually parse the args |
2783 | args = parser.parse_args() |
2784 | - if args.verbosity is None: |
2785 | - args.verbosity = 1 |
2786 | + |
2787 | + libertine.utils.set_environmental_verbosity(args.verbosity) |
2788 | |
2789 | if args.subparser_name == None: |
2790 | parser.print_help() |
2791 | |
2792 | === modified file 'tools/libertine-container-manager.1' |
2793 | --- tools/libertine-container-manager.1 2016-10-14 14:44:17 +0000 |
2794 | +++ tools/libertine-container-manager.1 2017-01-05 20:19:05 +0000 |
2795 | @@ -26,10 +26,10 @@ |
2796 | .SH OPTIONS |
2797 | .TP |
2798 | .BR \-q ", " \-\-quiet "" |
2799 | -do not print status updates on stdout |
2800 | +disable all non-vital output |
2801 | .TP |
2802 | .BR \-v ", " \-\-verbose "" |
2803 | -extra verbose output |
2804 | +enable debug output |
2805 | .TP |
2806 | .BR \-h ", " \-\-help "" |
2807 | Print a summary of the command line options and exit. |
2808 | @@ -319,6 +319,12 @@ |
2809 | Clear default container. |
2810 | .RE |
2811 | .TP |
2812 | +.BR |
2813 | + |
2814 | +.SH ENVIRONMENT VARIABLES |
2815 | +.TP |
2816 | +.BR LIBERTINE_DEBUG |
2817 | +Overrides verbosity arguments. 0 for quiet, 1 for standard, 2 for debug. |
2818 | |
2819 | .SH SEE ALSO |
2820 | .UR https://launchpad.net/libertine |
2821 | |
2822 | === modified file 'tools/libertine-lxc-manager' |
2823 | --- tools/libertine-lxc-manager 2016-11-09 20:59:27 +0000 |
2824 | +++ tools/libertine-lxc-manager 2017-01-05 20:19:05 +0000 |
2825 | @@ -16,90 +16,50 @@ |
2826 | # You should have received a copy of the GNU General Public License |
2827 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
2828 | |
2829 | -import dbus |
2830 | -import dbus.service |
2831 | import libertine.LxcContainer |
2832 | import libertine.utils |
2833 | import os |
2834 | import shlex |
2835 | -import signal |
2836 | import subprocess |
2837 | |
2838 | -from collections import Counter |
2839 | -from dbus.mainloop.glib import DBusGMainLoop |
2840 | -from gi.repository import GLib |
2841 | from libertine.ContainersConfig import ContainersConfig |
2842 | - |
2843 | - |
2844 | -home_path = os.environ['HOME'] |
2845 | +from libertine.lifecycle import * |
2846 | + |
2847 | |
2848 | LIBERTINE_LXC_MANAGER_NAME = libertine.LxcContainer.get_lxc_manager_dbus_name() |
2849 | LIBERTINE_LXC_MANAGER_PATH = libertine.LxcContainer.get_lxc_manager_dbus_path() |
2850 | |
2851 | |
2852 | -class Service(dbus.service.Object): |
2853 | +class Service(ContainerLifecycleService): |
2854 | |
2855 | def __init__(self): |
2856 | - self.is_pulse_setup = False |
2857 | - self.app_counter = Counter() |
2858 | - self.containers_config = ContainersConfig() |
2859 | - self.operation_counter = Counter() |
2860 | - |
2861 | - DBusGMainLoop(set_as_default=True) |
2862 | - try: |
2863 | - bus_name = dbus.service.BusName(LIBERTINE_LXC_MANAGER_NAME, |
2864 | - bus=dbus.SessionBus(), |
2865 | - do_not_queue=True) |
2866 | - except dbus.exceptions.NameExistsException: |
2867 | - print("service is already running") |
2868 | - raise |
2869 | - super().__init__(bus_name, LIBERTINE_LXC_MANAGER_PATH) |
2870 | - |
2871 | - @dbus.service.method(LIBERTINE_LXC_MANAGER_NAME, |
2872 | - in_signature='ss', |
2873 | - out_signature='(bs)') |
2874 | - def app_start(self, container_id, lxc_logfile): |
2875 | - if self.operation_counter[container_id] != 0: |
2876 | - return (False, "Libertine container operation already running: cannot launch application.") |
2877 | - |
2878 | - (started, reason) = self._launch_lxc_container(container_id, lxc_logfile) |
2879 | - |
2880 | - if started: |
2881 | - self.app_counter[container_id] += 1 |
2882 | - |
2883 | - return (started, reason) |
2884 | - |
2885 | - @dbus.service.method(LIBERTINE_LXC_MANAGER_NAME, |
2886 | - in_signature='s') |
2887 | - def app_stop(self, container_id): |
2888 | - self.app_counter[container_id] -= 1 |
2889 | - |
2890 | - if self.app_counter[container_id] == 0: |
2891 | - self._stop_lxc_container(container_id) |
2892 | - del self.app_counter[container_id] |
2893 | - |
2894 | - @dbus.service.method(LIBERTINE_LXC_MANAGER_NAME, |
2895 | - in_signature='ss', |
2896 | - out_signature='(bs)') |
2897 | - def operation_start(self, container_id, lxc_log_file): |
2898 | - if self.app_counter[container_id] != 0: |
2899 | - return (False, "Application already running in container: cannot run operation.") |
2900 | - |
2901 | - (started, reason) = self._launch_lxc_container(container_id, lxc_log_file, launchable=False) |
2902 | - |
2903 | - if started: |
2904 | - self.operation_counter[container_id] += 1 |
2905 | - |
2906 | - return (started, reason) |
2907 | - |
2908 | - @dbus.service.method(LIBERTINE_LXC_MANAGER_NAME, |
2909 | - in_signature='s') |
2910 | - def operation_stop(self, container_id): |
2911 | - self.operation_counter[container_id] -= 1 |
2912 | - |
2913 | - if self.operation_counter[container_id] == 0: |
2914 | - self._stop_lxc_container(container_id) |
2915 | - del self.operation_counter[container_id] |
2916 | + super().__init__(LIBERTINE_LXC_MANAGER_NAME, LIBERTINE_LXC_MANAGER_PATH) |
2917 | + self._home = os.environ['HOME'] |
2918 | + self._containers_config = ContainersConfig() |
2919 | + self._is_pulse_setup = False |
2920 | + |
2921 | + def start(self, container_id, launchable): |
2922 | + container = libertine.LxcContainer.lxc_container(container_id) |
2923 | + |
2924 | + if not container.defined: |
2925 | + return LifecycleResult("Container {} is not valid".format(container_id)) |
2926 | + |
2927 | + if launchable and not self._is_pulse_setup: |
2928 | + self._setup_pulse() |
2929 | + |
2930 | + if not container.running: |
2931 | + if launchable: |
2932 | + self._dynamic_bind_mounts(container, container_id) |
2933 | + |
2934 | + return libertine.LxcContainer.lxc_start(container) |
2935 | + |
2936 | + return LifecycleResult() |
2937 | + |
2938 | + def stop(self, container_id): |
2939 | + container = libertine.LxcContainer.lxc_container(container_id) |
2940 | + libertine.LxcContainer.lxc_stop(container) |
2941 | + |
2942 | + return LifecycleResult() # no error case |
2943 | |
2944 | def _setup_pulse(self): |
2945 | pulse_socket_path = os.path.join(libertine.utils.get_libertine_runtime_dir(), 'pulse_socket') |
2946 | @@ -118,42 +78,26 @@ |
2947 | |
2948 | self.is_pulse_setup = True |
2949 | |
2950 | - def _launch_lxc_container(self, container_id, lxc_log_file, launchable=True): |
2951 | - container = libertine.LxcContainer.lxc_container(container_id) |
2952 | - |
2953 | - if not container.defined: |
2954 | - return (False, "Container {} is not valid".format(container_id)) |
2955 | - |
2956 | - if launchable and not self.is_pulse_setup: |
2957 | - self._setup_pulse() |
2958 | - |
2959 | - if not container.running: |
2960 | - if launchable: |
2961 | - self._dynamic_bind_mounts(container, container_id) |
2962 | - |
2963 | - return libertine.LxcContainer.lxc_start(container, lxc_log_file) |
2964 | - |
2965 | - return (True, "") |
2966 | - |
2967 | - def _stop_lxc_container(self, container_id): |
2968 | - container = libertine.LxcContainer.lxc_container(container_id) |
2969 | - |
2970 | - libertine.LxcContainer.lxc_stop(container) |
2971 | - |
2972 | def _dynamic_bind_mounts(self, container, container_id): |
2973 | - self.containers_config.refresh_database() |
2974 | + self._containers_config.refresh_database() |
2975 | mounts = self._sanitize_bind_mounts(libertine.utils.get_common_xdg_user_directories() + \ |
2976 | - self.containers_config.get_container_bind_mounts(container_id)) |
2977 | + self._containers_config.get_container_bind_mounts(container_id)) |
2978 | |
2979 | data_dir = libertine.utils.get_libertine_container_userdata_dir_path(container_id) |
2980 | - for user_dir in libertine.utils.generate_binding_directories(mounts, home_path): |
2981 | - user_dir_fullpath = os.path.join(data_dir, user_dir[1]) |
2982 | - if not os.path.exists(user_dir_fullpath): |
2983 | - os.makedirs(user_dir_fullpath, exist_ok=True) |
2984 | - |
2985 | + for user_dir in libertine.utils.generate_binding_directories(mounts, self._home): |
2986 | + if os.path.isabs(user_dir[1]): |
2987 | + path = user_dir[1].strip('/') |
2988 | + fullpath = os.path.join(libertine.utils.get_libertine_container_rootfs_path(container_id), path) |
2989 | + else: |
2990 | + path = "{}/{}".format(self._home.strip('/'), user_dir[1]) |
2991 | + fullpath = os.path.join(data_dir, user_dir[1]) |
2992 | + |
2993 | + os.makedirs(fullpath, exist_ok=True) |
2994 | + |
2995 | + libertine.utils.get_logger().debug("Mounting {}:{} in container {}".format(user_dir[0], path, container_id)) |
2996 | xdg_user_dir_entry = ( |
2997 | - "%s %s/%s none bind,create=dir,optional" |
2998 | - % (user_dir[0], home_path.strip('/'), user_dir[1]) |
2999 | + "%s %s none bind,create=dir,optional" |
3000 | + % (user_dir[0], path) |
3001 | ) |
3002 | container.append_config_item("lxc.mount.entry", xdg_user_dir_entry) |
3003 | |
3004 | @@ -161,26 +105,5 @@ |
3005 | return [mount.replace(" ", "\\040") for mount in mounts] |
3006 | |
3007 | |
3008 | -def sigterm(self): |
3009 | - shutdown() |
3010 | - |
3011 | - |
3012 | -def shutdown(): |
3013 | - GLib.MainLoop().quit() |
3014 | - |
3015 | - |
3016 | -def main(): |
3017 | - service = Service() |
3018 | - GLib.unix_signal_add(GLib.PRIORITY_HIGH, |
3019 | - signal.SIGTERM, |
3020 | - sigterm, |
3021 | - None) |
3022 | - |
3023 | - try: |
3024 | - GLib.MainLoop().run() |
3025 | - except KeyboardInterrupt: |
3026 | - shutdown() |
3027 | - |
3028 | - |
3029 | if __name__ == '__main__': |
3030 | - main() |
3031 | + ContainerLifecycleServiceRunner(Service()).run() |
3032 | |
3033 | === added file 'tools/libertine-lxd-manager' |
3034 | --- tools/libertine-lxd-manager 1970-01-01 00:00:00 +0000 |
3035 | +++ tools/libertine-lxd-manager 2017-01-05 20:19:05 +0000 |
3036 | @@ -0,0 +1,57 @@ |
3037 | +#!/usr/bin/python3 |
3038 | +# -*- coding: utf-8 -*- |
3039 | + |
3040 | +# Copyright (C) 2016 Canonical Ltd. |
3041 | + |
3042 | +# This program is free software: you can redistribute it and/or modify |
3043 | +# it under the terms of the GNU General Public License as published by |
3044 | +# the Free Software Foundation; version 3 of the License. |
3045 | +# |
3046 | +# This program is distributed in the hope that it will be useful, |
3047 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3048 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3049 | +# GNU General Public License for more details. |
3050 | +# |
3051 | +# You should have received a copy of the GNU General Public License |
3052 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
3053 | + |
3054 | +import libertine.LxdContainer |
3055 | +import libertine.utils |
3056 | +import pylxd |
3057 | + |
3058 | +from libertine.ContainersConfig import ContainersConfig |
3059 | +from libertine.lifecycle import * |
3060 | + |
3061 | + |
3062 | +LIBERTINE_LXD_MANAGER_NAME = libertine.LxdContainer.get_lxd_manager_dbus_name() |
3063 | +LIBERTINE_LXD_MANAGER_PATH = libertine.LxdContainer.get_lxd_manager_dbus_path() |
3064 | + |
3065 | + |
3066 | +class Service(ContainerLifecycleService): |
3067 | + |
3068 | + def __init__(self): |
3069 | + super().__init__(LIBERTINE_LXD_MANAGER_NAME, LIBERTINE_LXD_MANAGER_PATH) |
3070 | + self._config = ContainersConfig() |
3071 | + self._client = pylxd.Client() |
3072 | + |
3073 | + def start(self, container_id, launchable): |
3074 | + container = libertine.LxdContainer.lxd_container(self._client, container_id) |
3075 | + |
3076 | + if not container: |
3077 | + return LifecycleResult("Container {} is not valid".format(container_id)) |
3078 | + |
3079 | + if container.status != 'Running': |
3080 | + libertine.LxdContainer.update_libertine_profile(self._client) |
3081 | + if launchable: |
3082 | + libertine.LxdContainer.update_bind_mounts(container, self._config) |
3083 | + |
3084 | + return libertine.LxdContainer.lxd_start(container) |
3085 | + |
3086 | + return LifecycleResult() |
3087 | + |
3088 | + def stop(self, container_id): |
3089 | + return libertine.LxdContainer.lxd_stop(libertine.LxdContainer.lxd_container(self._client, container_id), False) |
3090 | + |
3091 | + |
3092 | +if __name__ == '__main__': |
3093 | + ContainerLifecycleServiceRunner(Service()).run() |
3094 | |
3095 | === added file 'tools/libertine-lxd-manager.1' |
3096 | --- tools/libertine-lxd-manager.1 1970-01-01 00:00:00 +0000 |
3097 | +++ tools/libertine-lxd-manager.1 2017-01-05 20:19:05 +0000 |
3098 | @@ -0,0 +1,9 @@ |
3099 | +.TH libertine-lxd-manager "1" "Dec 2016" "libertine-lxd-manager 0.99" "User Commands" |
3100 | + |
3101 | +.SH NAME |
3102 | +libertine-lxd-manager \- monitors LXD activity performed by libertine |
3103 | + |
3104 | +.SH DESCRIPTION |
3105 | +usage: libertine\-lxd\-manager |
3106 | +.PP |
3107 | +monitors LXD activity performed by libertine |
3108 | |
3109 | === added file 'tools/libertine-lxd-setup' |
3110 | --- tools/libertine-lxd-setup 1970-01-01 00:00:00 +0000 |
3111 | +++ tools/libertine-lxd-setup 2017-01-05 20:19:05 +0000 |
3112 | @@ -0,0 +1,40 @@ |
3113 | +#!/bin/sh |
3114 | + |
3115 | +if [ -z $1 ]; then |
3116 | + echo "Usage: $0 username" |
3117 | + exit 1 |
3118 | +fi |
3119 | + |
3120 | +USERNAME=$1 |
3121 | + |
3122 | +# Create the lxd group and add given user |
3123 | +if [ -z "`groups ${USERNAME} | grep lxd`" ]; then |
3124 | + groupadd --force --system lxd |
3125 | + usermod -G lxd -a $USERNAME |
3126 | +fi |
3127 | + |
3128 | +# Map the given user to the container root user |
3129 | +uid=`id --user ${USERNAME}` |
3130 | +idmap="root:$uid:1" |
3131 | +if [ -z "`grep ${idmap} /etc/subuid`" ]; then |
3132 | + echo ${idmap} | tee -a /etc/subuid /etc/subgid |
3133 | +fi |
3134 | + |
3135 | +# find the right lxc command |
3136 | +lxc=`which lxc` |
3137 | +if [ -z "${lxc}" ]; then |
3138 | + if [ -n `which lxd.lxc` ]; then |
3139 | + lxc=`which lxd.lxc` |
3140 | + else |
3141 | + echo "No lxc command found on this system." |
3142 | + exit 1 |
3143 | + fi |
3144 | +fi |
3145 | + |
3146 | +# Run lxd init if there are no containers already on the system |
3147 | +if [ 3 -ge `${lxc} list | wc -l` ]; then |
3148 | + lxd init |
3149 | +fi |
3150 | + |
3151 | +mkdir -p /home/$USERNAME/.config/lxc |
3152 | +chown -R $USERNAME:$USERNAME /home/$USERNAME/.config/lxc |
3153 | |
3154 | === modified file 'tools/libertined' |
3155 | --- tools/libertined 2016-10-26 17:05:34 +0000 |
3156 | +++ tools/libertined 2017-01-05 20:19:05 +0000 |
3157 | @@ -62,11 +62,10 @@ |
3158 | os.rename(self.cache_file, '%s.1' % self.cache_file) |
3159 | |
3160 | def __enter__(self): |
3161 | - if self.config.debug: |
3162 | - os.environ['LIBERTINE_DEBUG'] = '1' |
3163 | - else: |
3164 | + utils.set_environmental_verbosity(self.config.verbosity) |
3165 | + |
3166 | + if not self.config.debug: |
3167 | if self.config.cache_output: |
3168 | - os.environ['LIBERTINE_DEBUG'] = '1' |
3169 | self._rotate_logs() |
3170 | self.output_file = open(self.cache_file, 'w') |
3171 | else: |
3172 | @@ -94,7 +93,13 @@ |
3173 | self._arg_parser.add_argument(u'-d', u"--debug", |
3174 | action='store_true', |
3175 | default=False, |
3176 | - help=u"allow all output on stdout") |
3177 | + help=u"print output to stdout") |
3178 | + self._arg_parser.add_argument('-q', '--quiet', action='store_const', |
3179 | + dest='verbosity', const=0, |
3180 | + help=('disables all non-vital output')) |
3181 | + self._arg_parser.add_argument('-v', '--verbosity', action='store_const', |
3182 | + dest='verbosity', const=2, |
3183 | + help=('enables debug output')) |
3184 | self._arg_parser.add_argument(u'-c', u"--cache-output", |
3185 | action='store_true', |
3186 | default=False, |
3187 | @@ -111,7 +116,7 @@ |
3188 | self.loop = GLib.MainLoop() |
3189 | |
3190 | def sigterm(self, code): |
3191 | - utils.get_logger().info("terminate ('%s') signal received" % code) |
3192 | + utils.get_logger().debug("terminate ('%s') signal received" % code) |
3193 | self.shutdown() |
3194 | |
3195 | def shutdown(self): |
3196 | |
3197 | === removed file 'tools/update-puritine-containers' |
3198 | --- tools/update-puritine-containers 2016-10-25 15:24:08 +0000 |
3199 | +++ tools/update-puritine-containers 1970-01-01 00:00:00 +0000 |
3200 | @@ -1,183 +0,0 @@ |
3201 | -#!/usr/bin/python3 |
3202 | -# -*- coding: utf-8 -*- |
3203 | - |
3204 | -# Copyright (C) 2016 Canonical Ltd. |
3205 | -# Author: Christopher Townsend <christopher.townsend@canonical.com> |
3206 | - |
3207 | -# This program is free software: you can redistribute it and/or modify |
3208 | -# it under the terms of the GNU General Public License as published by |
3209 | -# the Free Software Foundation; version 3 of the License. |
3210 | -# |
3211 | -# This program is distributed in the hope that it will be useful, |
3212 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3213 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3214 | -# GNU General Public License for more details. |
3215 | -# |
3216 | -# You should have received a copy of the GNU General Public License |
3217 | -# along with this program. If not, see <http://www.gnu.org/licenses/>. |
3218 | - |
3219 | -import json |
3220 | -import libertine.utils |
3221 | -import os |
3222 | -import shlex |
3223 | -import shutil |
3224 | -import subprocess |
3225 | - |
3226 | -from gi.repository import GLib, Gio |
3227 | - |
3228 | -puritine_hook_dir = os.path.join(os.environ['HOME'], '.cache', 'libertine', 'puritine') |
3229 | -puritine_symlink_farm_file = os.path.join(puritine_hook_dir, 'PuritineSymlinkFarm.json') |
3230 | -puritine_click_config_file = os.path.join('libertine-config', 'libertine', 'ContainersConfig.json') |
3231 | - |
3232 | - |
3233 | -def symlink_farm_entries_count(): |
3234 | - if (puritine_symlink_farm_list and |
3235 | - puritine_symlink_farm_list['customContainers']): |
3236 | - return len(puritine_symlink_farm_list['customContainers']) |
3237 | - |
3238 | - return 0 |
3239 | - |
3240 | - |
3241 | -def puritine_symlink_exists(symlink): |
3242 | - for file in os.listdir(puritine_hook_dir): |
3243 | - if file == symlink: |
3244 | - return True |
3245 | - |
3246 | - return False |
3247 | - |
3248 | - |
3249 | -def get_symlink_name(puritine_symlink_farm_list, container_id): |
3250 | - if puritine_symlink_farm_list: |
3251 | - for container in puritine_symlink_farm_list['customContainers']: |
3252 | - if container['id'] == container_id: |
3253 | - return (puritine_symlink_farm_list['customContainers'].index(container), container['symlinkName']) |
3254 | - |
3255 | - return (0, None) |
3256 | - |
3257 | - |
3258 | -def find_removed_puritine_symlinks(puritine_symlink_farm_list): |
3259 | - for container in puritine_symlink_farm_list['customContainers']: |
3260 | - if puritine_symlink_exists(container['symlinkName']): |
3261 | - continue |
3262 | - else: |
3263 | - manager_cmd = "libertine-container-manager destroy -i " + container['id'] |
3264 | - cmd = shlex.split(manager_cmd) |
3265 | - subprocess.Popen(cmd).wait() |
3266 | - puritine_symlink_farm_list['customContainers'].remove(container) |
3267 | - |
3268 | - return puritine_symlink_farm_list |
3269 | - |
3270 | - |
3271 | -def find_new_or_updated_puritine_symlinks(puritine_symlink_farm_list): |
3272 | - symlinks = os.listdir(puritine_hook_dir) |
3273 | - |
3274 | - for symlink in symlinks: |
3275 | - puritine_click_path = os.path.join(puritine_hook_dir, symlink) |
3276 | - if not os.path.islink(puritine_click_path): |
3277 | - continue |
3278 | - |
3279 | - config_file_path = os.path.join(puritine_click_path, puritine_click_config_file) |
3280 | - |
3281 | - with open(config_file_path, 'r') as fd: |
3282 | - container_list = json.load(fd) |
3283 | - |
3284 | - container_id = container_list['containerList'][0]['id'] |
3285 | - |
3286 | - (index, symlink_name) = get_symlink_name(puritine_symlink_farm_list, container_id) |
3287 | - |
3288 | - if symlink_name: |
3289 | - # Package exists and symlink name is the same- no update |
3290 | - if symlink_name == symlink: |
3291 | - continue |
3292 | - else: |
3293 | - puritine_symlink_farm_list['customContainers'][index]['symlinkName'] = symlink |
3294 | - else: |
3295 | - symlink_obj = {'symlinkName': symlink, 'id': container_id} |
3296 | - if 'customContainers' not in puritine_symlink_farm_list: |
3297 | - puritine_symlink_farm_list['customContainers'] = [symlink_obj] |
3298 | - else: |
3299 | - puritine_symlink_farm_list['customContainers'].append(symlink_obj) |
3300 | - |
3301 | - puritine_click_rootfs_path = os.path.join(puritine_click_path, 'libertine-data', 'libertine-container', |
3302 | - container_id, 'rootfs') |
3303 | - |
3304 | - libertine_containers_path = libertine.utils.get_libertine_containers_dir_path() |
3305 | - puritine_container_path = os.path.join(libertine_containers_path, container_id) |
3306 | - puritine_container_rootfs_path = os.path.join(puritine_container_path, 'rootfs') |
3307 | - |
3308 | - if not os.path.exists(puritine_container_path): |
3309 | - os.makedirs(puritine_container_path) |
3310 | - else: |
3311 | - os.remove(puritine_container_rootfs_path) |
3312 | - |
3313 | - # Link to the click packages container |
3314 | - os.symlink(puritine_click_rootfs_path, puritine_container_rootfs_path) |
3315 | - |
3316 | - # Copy any user data that does not exist |
3317 | - libertine_user_data_path = libertine.utils.get_libertine_container_userdata_dir_path(container_id) |
3318 | - if not os.path.exists(libertine_user_data_path): |
3319 | - puritine_click_user_data_path = os.path.join(puritine_click_path, 'libertine-config', 'libertine-container', |
3320 | - 'user-data', container_id) |
3321 | - shutil.copytree(puritine_click_user_data_path, libertine_user_data_path) |
3322 | - |
3323 | - # Update the main ContainerConfig.json |
3324 | - manager_cmd = "libertine-container-manager merge-configs -f " + config_file_path |
3325 | - cmd = shlex.split(manager_cmd) |
3326 | - subprocess.Popen(cmd).wait() |
3327 | - |
3328 | - return puritine_symlink_farm_list |
3329 | - |
3330 | - |
3331 | -def favorite_libertine_scope(): |
3332 | - libertine_scope_value = 'scope://libertine-scope.ubuntu_libertine-scope' |
3333 | - click_scope_value = 'scope://clickscope' |
3334 | - schema = 'com.canonical.Unity.Dash' |
3335 | - schema_key = 'favorite-scopes' |
3336 | - |
3337 | - settings = Gio.Settings.new(schema) |
3338 | - |
3339 | - favorites = settings.get_value(schema_key) |
3340 | - |
3341 | - array = favorites.dup_strv() |
3342 | - |
3343 | - if not libertine_scope_value in array: |
3344 | - array.insert(array.index(click_scope_value) + 1, libertine_scope_value) |
3345 | - new_favorites = GLib.Variant.new_strv(array) |
3346 | - settings.set_value(schema_key, new_favorites) |
3347 | - del new_favorites |
3348 | - |
3349 | - del array |
3350 | - del favorites |
3351 | - del settings |
3352 | - |
3353 | - |
3354 | -if __name__ == '__main__': |
3355 | - puritine_symlink_farm_list = {} |
3356 | - update_libertine_scope = libertine.utils.set_session_dbus_env_var() |
3357 | - |
3358 | - if not os.path.exists(puritine_hook_dir): |
3359 | - os.makedirs(puritine_hook_dir) |
3360 | - |
3361 | - if (os.path.exists(puritine_symlink_farm_file) and |
3362 | - os.path.getsize(puritine_symlink_farm_file) != 0): |
3363 | - with open(puritine_symlink_farm_file, 'r') as fd: |
3364 | - puritine_symlink_farm_list = json.load(fd) |
3365 | - |
3366 | - count_at_start = symlink_farm_entries_count() |
3367 | - |
3368 | - puritine_symlink_farm_list = find_new_or_updated_puritine_symlinks(puritine_symlink_farm_list) |
3369 | - |
3370 | - if puritine_symlink_farm_list: |
3371 | - puritine_symlink_farm_list = find_removed_puritine_symlinks(puritine_symlink_farm_list) |
3372 | - |
3373 | - count_at_finish = symlink_farm_entries_count() |
3374 | - |
3375 | - with open(puritine_symlink_farm_file, 'w') as fd: |
3376 | - json.dump(puritine_symlink_farm_list, fd, sort_keys=True, indent=4) |
3377 | - fd.write('\n') |
3378 | - |
3379 | - if count_at_start == 0 and count_at_finish > 0 and update_libertine_scope: |
3380 | - favorite_libertine_scope() |
3381 | - |
3382 | - if update_libertine_scope: |
3383 | - libertine.utils.refresh_libertine_scope() |
okok let's do this