]> git.proxmox.com Git - mirror_lxc.git/blame - src/python-lxc/lxc/__init__.py
licensing: Add missing headers and FSF address
[mirror_lxc.git] / src / python-lxc / lxc / __init__.py
CommitLineData
be2e4e54
SG
1#
2# python-lxc: Python bindings for LXC
3#
4# (C) Copyright Canonical Ltd. 2012
5#
6# Authors:
7# Stéphane Graber <stgraber@ubuntu.com>
8#
9# This library is free software; you can redistribute it and/or
10# modify it under the terms of the GNU Lesser General Public
11# License as published by the Free Software Foundation; either
12# version 2.1 of the License, or (at your option) any later version.
13#
14# This library is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17# Lesser General Public License for more details.
18#
19# You should have received a copy of the GNU Lesser General Public
20# License along with this library; if not, write to the Free Software
250b1eec 21# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
be2e4e54
SG
22#
23
24import _lxc
25import glob
26import os
27import subprocess
2a41cf5d 28import stat
b0f9616f 29import time
be2e4e54
SG
30import warnings
31
32warnings.warn("The python-lxc API isn't yet stable "
33 "and may change at any point in the future.", Warning, 2)
34
24fcdb39 35default_config_path = _lxc.get_default_config_path()
b6adc92b 36version = _lxc.get_version()
edb09f8d 37
cbd4c464 38
be2e4e54
SG
39class ContainerNetwork():
40 props = {}
41
42 def __init__(self, container, index):
43 self.container = container
44 self.index = index
45
46 for key in self.container.get_keys("lxc.network.%s" % self.index):
47 if "." in key:
48 self.props[key.replace(".", "_")] = key
49 else:
50 self.props[key] = key
51
52 if not self.props:
53 return False
54
55 def __delattr__(self, key):
56 if key in ["container", "index", "props"]:
57 return object.__delattr__(self, key)
58
59 if key not in self.props:
60 raise AttributeError("'%s' network has no attribute '%s'" % (
bde18539 61 self.__get_network_item("type"), key))
be2e4e54
SG
62
63 return self.__clear_network_item(self.props[key])
64
65 def __dir__(self):
66 return sorted(self.props.keys())
67
68 def __getattr__(self, key):
69 if key in ["container", "index", "props"]:
70 return object.__getattribute__(self, key)
71
72 if key not in self.props:
73 raise AttributeError("'%s' network has no attribute '%s'" % (
bde18539 74 self.__get_network_item("type"), key))
be2e4e54
SG
75
76 return self.__get_network_item(self.props[key])
77
78 def __hasattr__(self, key):
79 if key in ["container", "index", "props"]:
80 return object.__hasattr__(self, key)
81
82 if key not in self.props:
83 raise AttributeError("'%s' network has no attribute '%s'" % (
bde18539 84 self.__get_network_item("type"), key))
be2e4e54
SG
85
86 return True
87
88 def __repr__(self):
89 return "'%s' network at index '%s'" % (
90 self.__get_network_item("type"), self.index)
91
92 def __setattr__(self, key, value):
93 if key in ["container", "index", "props"]:
94 return object.__setattr__(self, key, value)
95
96 if key not in self.props:
97 raise AttributeError("'%s' network has no attribute '%s'" % (
bde18539 98 self.__get_network_item("type"), key))
be2e4e54
SG
99
100 return self.__set_network_item(self.props[key], value)
101
102 def __clear_network_item(self, key):
103 return self.container.clear_config_item("lxc.network.%s.%s" % (
bde18539 104 self.index, key))
be2e4e54
SG
105
106 def __get_network_item(self, key):
107 return self.container.get_config_item("lxc.network.%s.%s" % (
bde18539 108 self.index, key))
be2e4e54
SG
109
110 def __set_network_item(self, key, value):
111 return self.container.set_config_item("lxc.network.%s.%s" % (
bde18539 112 self.index, key), value)
be2e4e54
SG
113
114
115class ContainerNetworkList():
116 def __init__(self, container):
117 self.container = container
118
119 def __getitem__(self, index):
2cdb945b 120 if index >= len(self):
be2e4e54
SG
121 raise IndexError("list index out of range")
122
123 return ContainerNetwork(self.container, index)
124
125 def __len__(self):
2cdb945b
SG
126 values = self.container.get_config_item("lxc.network")
127
128 if values:
129 return len(values)
130 else:
131 return 0
be2e4e54
SG
132
133 def add(self, network_type):
2cdb945b 134 index = len(self)
be2e4e54
SG
135
136 return self.container.set_config_item("lxc.network.%s.type" % index,
bde18539 137 network_type)
be2e4e54
SG
138
139 def remove(self, index):
2cdb945b 140 count = len(self)
be2e4e54
SG
141 if index >= count:
142 raise IndexError("list index out of range")
143
144 return self.container.clear_config_item("lxc.network.%s" % index)
145
146
147class Container(_lxc.Container):
edb09f8d 148 def __init__(self, name, config_path=None):
be2e4e54
SG
149 """
150 Creates a new Container instance.
151 """
152
cbd4c464
SG
153 if os.geteuid() != 0:
154 raise Exception("Running as non-root.")
155
edb09f8d
SG
156 if config_path:
157 _lxc.Container.__init__(self, name, config_path)
158 else:
159 _lxc.Container.__init__(self, name)
160
be2e4e54
SG
161 self.network = ContainerNetworkList(self)
162
20cf2e97 163 def add_device_node(self, path, destpath=None):
2a41cf5d 164 """
20cf2e97 165 Add block/char device to running container.
2a41cf5d
SG
166 """
167
20cf2e97
SG
168 if not self.running:
169 return False
170
2a41cf5d
SG
171 if not destpath:
172 destpath = path
173
174 if not os.path.exists(path):
175 return False
176
177 # Lookup the source
178 path_stat = os.stat(path)
179 mode = stat.S_IMODE(path_stat.st_mode)
180
2a41cf5d 181 # Allow the target
d8521cc3
SG
182 if stat.S_ISBLK(path_stat.st_mode):
183 self.set_cgroup_item("devices.allow",
184 "b %s:%s rwm" %
185 (int(path_stat.st_rdev / 256),
186 int(path_stat.st_rdev % 256)))
187 elif stat.S_ISCHR(path_stat.st_mode):
188 self.set_cgroup_item("devices.allow",
189 "c %s:%s rwm" %
190 (int(path_stat.st_rdev / 256),
191 int(path_stat.st_rdev % 256)))
2a41cf5d
SG
192
193 # Create the target
194 rootfs = "/proc/%s/root/" % self.init_pid
195 container_path = "%s/%s" % (rootfs, destpath)
196
197 if os.path.exists(container_path):
198 os.remove(container_path)
199
200 os.mknod(container_path, path_stat.st_mode, path_stat.st_rdev)
201 os.chmod(container_path, mode)
202 os.chown(container_path, 0, 0)
203
204 return True
205
20cf2e97
SG
206 def add_device_net(self, name, destname=None):
207 """
208 Add network device to running container.
209 """
210
211 if not self.running:
212 return False
213
214 if not destname:
215 destname = name
216
217 if not os.path.exists("/sys/class/net/%s/" % name):
218 return False
219
220 return subprocess.call(['ip', 'link', 'set',
221 'dev', name,
222 'netns', str(self.init_pid),
223 'name', destname]) == 0
224
be2e4e54
SG
225 def append_config_item(self, key, value):
226 """
227 Append 'value' to 'key', assuming 'key' is a list.
228 If 'key' isn't a list, 'value' will be set as the value of 'key'.
229 """
230
231 return _lxc.Container.set_config_item(self, key, value)
232
be2e4e54
SG
233 def create(self, template, args={}):
234 """
235 Create a new rootfs for the container.
236
237 "template" must be a valid template name.
238
239 "args" (optional) is a dictionary of parameters and values to pass
240 to the template.
241 """
242
243 template_args = []
244 for item in args.items():
245 template_args.append("--%s" % item[0])
246 template_args.append("%s" % item[1])
247
248 return _lxc.Container.create(self, template, tuple(template_args))
249
250 def clone(self, container):
251 """
252 Clone an existing container into a new one.
253 """
254
255 if self.defined:
256 return False
257
258 if isinstance(container, Container):
259 source = container
260 else:
261 source = Container(container)
262
263 if not source.defined:
264 return False
265
bde18539
SG
266 if subprocess.call(["lxc-clone", "-o", source.name, "-n", self.name],
267 universal_newlines=True) != 0:
be2e4e54
SG
268 return False
269
270 self.load_config()
271 return True
272
39ffde30 273 def console(self, ttynum=-1, stdinfd=0, stdoutfd=1, stderrfd=2, escape=1):
be2e4e54 274 """
b5159817 275 Attach to console of running container.
be2e4e54
SG
276 """
277
278 if not self.running:
279 return False
280
39ffde30
SG
281 return _lxc.Container.console(self, ttynum, stdinfd, stdoutfd,
282 stderrfd, escape)
b5159817 283
39ffde30 284 def console_getfd(self, ttynum=-1):
b5159817
DE
285 """
286 Attach to console of running container.
287 """
288
289 if not self.running:
be2e4e54 290 return False
b5159817
DE
291
292 return _lxc.Container.console_getfd(self, ttynum)
be2e4e54 293
f4d3a9fd
SG
294 def get_cgroup_item(self, key):
295 """
296 Returns the value for a given cgroup entry.
297 A list is returned when multiple values are set.
298 """
299 value = _lxc.Container.get_cgroup_item(self, key)
300
301 if value is False:
302 return False
303 else:
304 return value.rstrip("\n")
305
be2e4e54
SG
306 def get_config_item(self, key):
307 """
308 Returns the value for a given config key.
309 A list is returned when multiple values are set.
310 """
311 value = _lxc.Container.get_config_item(self, key)
312
313 if value is False:
314 return False
315 elif value.endswith("\n"):
316 return value.rstrip("\n").split("\n")
317 else:
318 return value
319
703c562d 320 def get_keys(self, key=None):
be2e4e54
SG
321 """
322 Returns a list of valid sub-keys.
323 """
703c562d
SG
324 if key:
325 value = _lxc.Container.get_keys(self, key)
326 else:
327 value = _lxc.Container.get_keys(self)
be2e4e54
SG
328
329 if value is False:
330 return False
331 elif value.endswith("\n"):
332 return value.rstrip("\n").split("\n")
333 else:
334 return value
335
b0f9616f
SG
336 def get_ips(self, interface=None, family=None, scope=None, timeout=0):
337 """
338 Get a tuple of IPs for the container.
339 """
340
341 kwargs = {}
342 if interface:
343 kwargs['interface'] = interface
344 if family:
345 kwargs['family'] = family
346 if scope:
347 kwargs['scope'] = scope
348
349 ips = None
819554fe 350 timeout = int(os.environ.get('LXC_GETIP_TIMEOUT', timeout))
b0f9616f
SG
351
352 while not ips:
353 ips = _lxc.Container.get_ips(self, **kwargs)
354 if timeout == 0:
355 break
356
357 timeout -= 1
358 time.sleep(1)
359
360 return ips
361
be2e4e54
SG
362 def set_config_item(self, key, value):
363 """
364 Set a config key to a provided value.
365 The value can be a list for the keys supporting multiple values.
366 """
6c5db2af
SG
367 try:
368 old_value = self.get_config_item(key)
369 except KeyError:
370 old_value = None
be2e4e54
SG
371
372 # Check if it's a list
373 def set_key(key, value):
374 self.clear_config_item(key)
375 if isinstance(value, list):
376 for entry in value:
377 if not _lxc.Container.set_config_item(self, key, entry):
378 return False
379 else:
380 _lxc.Container.set_config_item(self, key, value)
381
382 set_key(key, value)
383 new_value = self.get_config_item(key)
384
bde18539
SG
385 if (isinstance(value, str) and isinstance(new_value, str) and
386 value == new_value):
be2e4e54 387 return True
bde18539
SG
388 elif (isinstance(value, list) and isinstance(new_value, list) and
389 set(value) == set(new_value)):
be2e4e54 390 return True
bde18539
SG
391 elif (isinstance(value, str) and isinstance(new_value, list) and
392 set([value]) == set(new_value)):
be2e4e54
SG
393 return True
394 elif old_value:
395 set_key(key, old_value)
396 return False
397 else:
398 self.clear_config_item(key)
399 return False
400
bde18539 401 def wait(self, state, timeout=-1):
87540ad7
SG
402 """
403 Wait for the container to reach a given state or timeout.
404 """
405
406 if isinstance(state, str):
407 state = state.upper()
408
225b52ef 409 return _lxc.Container.wait(self, state, timeout)
be2e4e54 410
bde18539 411
edb09f8d 412def list_containers(as_object=False, config_path=None):
be2e4e54
SG
413 """
414 List the containers on the system.
415 """
edb09f8d
SG
416
417 if not config_path:
418 config_path = default_config_path
419
be2e4e54 420 containers = []
edb09f8d 421 for entry in glob.glob("%s/*/config" % config_path):
be2e4e54 422 if as_object:
edb09f8d 423 containers.append(Container(entry.split("/")[-2], config_path))
be2e4e54
SG
424 else:
425 containers.append(entry.split("/")[-2])
426 return containers
d7a09c63
CS
427
428def attach_run_command(cmd):
429 """
430 Run a command when attaching
55c76589 431
d7a09c63
CS
432 Please do not call directly, this will execvp the command.
433 This is to be used in conjunction with the attach method
434 of a container.
435 """
436 if isinstance(cmd, tuple):
437 return _lxc.attach_run_command(cmd)
438 elif isinstance(cmd, list):
439 return _lxc.attach_run_command((cmd[0], cmd))
440 else:
441 return _lxc.attach_run_command((cmd, [cmd]))
442
443def attach_run_shell():
444 """
445 Run a shell when attaching
55c76589 446
d7a09c63
CS
447 Please do not call directly, this will execvp the shell.
448 This is to be used in conjunction with the attach method
449 of a container.
450 """
451 return _lxc.attach_run_shell(None)
452
c9ec9055
CS
453def arch_to_personality(arch):
454 """
455 Determine the process personality corresponding to the architecture
456 """
457 if isinstance(arch, bytes):
458 arch = str(arch, 'utf-8')
459 return _lxc.arch_to_personality(arch)
460
d7a09c63
CS
461# Some constants for attach
462LXC_ATTACH_KEEP_ENV = _lxc.LXC_ATTACH_KEEP_ENV
463LXC_ATTACH_CLEAR_ENV = _lxc.LXC_ATTACH_CLEAR_ENV
464LXC_ATTACH_MOVE_TO_CGROUP = _lxc.LXC_ATTACH_MOVE_TO_CGROUP
465LXC_ATTACH_DROP_CAPABILITIES = _lxc.LXC_ATTACH_DROP_CAPABILITIES
466LXC_ATTACH_SET_PERSONALITY = _lxc.LXC_ATTACH_SET_PERSONALITY
467LXC_ATTACH_APPARMOR = _lxc.LXC_ATTACH_APPARMOR
468LXC_ATTACH_REMOUNT_PROC_SYS = _lxc.LXC_ATTACH_REMOUNT_PROC_SYS
469LXC_ATTACH_DEFAULT = _lxc.LXC_ATTACH_DEFAULT
01bfae14
CS
470CLONE_NEWUTS = _lxc.CLONE_NEWUTS
471CLONE_NEWIPC = _lxc.CLONE_NEWIPC
472CLONE_NEWUSER = _lxc.CLONE_NEWUSER
473CLONE_NEWPID = _lxc.CLONE_NEWPID
474CLONE_NEWNET = _lxc.CLONE_NEWNET
475CLONE_NEWNS = _lxc.CLONE_NEWNS