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