2
# python-lxc: Python bindings for LXC
4
# (C) Copyright Canonical Ltd. 2012
7
# Stéphane Graber <stgraber@ubuntu.com>
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.
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.
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
21
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32
class ContainerNetwork():
35
def __init__(self, container, index):
36
self.container = container
39
for key in self.container.get_keys("lxc.network.%s" % self.index):
41
self.props[key.replace(".", "_")] = key
48
def __delattr__(self, key):
49
if key in ["container", "index", "props"]:
50
return object.__delattr__(self, key)
52
if key not in self.props:
53
raise AttributeError("'%s' network has no attribute '%s'" % (
54
self.__get_network_item("type"), key))
56
return self.__clear_network_item(self.props[key])
59
return sorted(self.props.keys())
61
def __getattr__(self, key):
62
if key in ["container", "index", "props"]:
63
return object.__getattribute__(self, key)
65
if key not in self.props:
66
raise AttributeError("'%s' network has no attribute '%s'" % (
67
self.__get_network_item("type"), key))
69
return self.__get_network_item(self.props[key])
71
def __hasattr__(self, key):
72
if key in ["container", "index", "props"]:
73
return object.__hasattr__(self, key)
75
if key not in self.props:
76
raise AttributeError("'%s' network has no attribute '%s'" % (
77
self.__get_network_item("type"), key))
82
return "'%s' network at index '%s'" % (
83
self.__get_network_item("type"), self.index)
85
def __setattr__(self, key, value):
86
if key in ["container", "index", "props"]:
87
return object.__setattr__(self, key, value)
89
if key not in self.props:
90
raise AttributeError("'%s' network has no attribute '%s'" % (
91
self.__get_network_item("type"), key))
93
return self.__set_network_item(self.props[key], value)
95
def __clear_network_item(self, key):
96
return self.container.clear_config_item("lxc.network.%s.%s" % (
99
def __get_network_item(self, key):
100
return self.container.get_config_item("lxc.network.%s.%s" % (
103
def __set_network_item(self, key, value):
104
return self.container.set_config_item("lxc.network.%s.%s" % (
105
self.index, key), value)
108
class ContainerNetworkList():
109
def __init__(self, container):
110
self.container = container
112
def __getitem__(self, index):
113
count = len(self.container.get_config_item("lxc.network"))
115
raise IndexError("list index out of range")
117
return ContainerNetwork(self.container, index)
120
return len(self.container.get_config_item("lxc.network"))
122
def add(self, network_type):
123
index = len(self.container.get_config_item("lxc.network"))
125
return self.container.set_config_item("lxc.network.%s.type" % index,
128
def remove(self, index):
129
count = len(self.container.get_config_item("lxc.network"))
131
raise IndexError("list index out of range")
133
return self.container.clear_config_item("lxc.network.%s" % index)
136
class Container(_lxc.Container):
137
def __init__(self, name):
139
Creates a new Container instance.
142
_lxc.Container.__init__(self, name)
143
self.network = ContainerNetworkList(self)
145
def append_config_item(self, key, value):
147
Append 'value' to 'key', assuming 'key' is a list.
148
If 'key' isn't a list, 'value' will be set as the value of 'key'.
151
return _lxc.Container.set_config_item(self, key, value)
153
def attach(self, namespace="ALL", *cmd):
155
Attach to a running container.
161
attach = ["lxc-attach", "-n", self.name]
162
if namespace != "ALL":
163
attach += ["-s", namespace]
166
attach += ["--"] + list(cmd)
170
universal_newlines=True) != 0:
174
def create(self, template, args={}):
176
Create a new rootfs for the container.
178
"template" must be a valid template name.
180
"args" (optional) is a dictionary of parameters and values to pass
185
for item in args.items():
186
template_args.append("--%s" % item[0])
187
template_args.append("%s" % item[1])
189
return _lxc.Container.create(self, template, tuple(template_args))
191
def clone(self, container):
193
Clone an existing container into a new one.
199
if isinstance(container, Container):
202
source = Container(container)
204
if not source.defined:
208
["lxc-clone", "-o", source.name, "-n", self.name],
209
universal_newlines=True) != 0:
215
def console(self, tty="1"):
217
Access the console of a container.
224
["lxc-console", "-n", self.name, "-t", "%s" % tty],
225
universal_newlines=True) != 0:
229
def get_config_item(self, key):
231
Returns the value for a given config key.
232
A list is returned when multiple values are set.
234
value = _lxc.Container.get_config_item(self, key)
238
elif value.endswith("\n"):
239
return value.rstrip("\n").split("\n")
243
def get_ips(self, timeout=60, interface=None, protocol=None):
245
Returns the list of IP addresses for the container.
248
if not self.defined or not self.running:
252
os.makedirs("/run/netns")
256
path = tempfile.mktemp(dir="/run/netns")
258
os.symlink("/proc/%s/ns/net" % self.init_pid, path)
263
while count < timeout:
264
base_cmd = ["ip", "netns", "exec", path.split("/")[-1], "ip"]
267
if protocol in ("ipv6", None):
268
ip6_cmd = base_cmd + ["-6", "addr", "show", "scope", "global"]
270
ip = subprocess.Popen(ip6_cmd + ["dev", interface],
271
stdout=subprocess.PIPE, universal_newlines=True)
273
ip = subprocess.Popen(ip6_cmd, stdout=subprocess.PIPE,
274
universal_newlines=True)
277
for line in ip.stdout.read().split("\n"):
278
fields = line.split()
279
if len(fields) > 2 and fields[0] == "inet6":
280
ips.append(fields[1].split('/')[0])
283
if protocol in ("ipv4", None):
284
ip4_cmd = base_cmd + ["-4", "addr", "show", "scope", "global"]
286
ip = subprocess.Popen(ip4_cmd + ["dev", interface],
287
stdout=subprocess.PIPE, universal_newlines=True)
289
ip = subprocess.Popen(ip4_cmd, stdout=subprocess.PIPE,
290
universal_newlines=True)
293
for line in ip.stdout.read().split("\n"):
294
fields = line.split()
295
if len(fields) > 2 and fields[0] == "inet":
296
ips.append(fields[1].split('/')[0])
307
def get_keys(self, key):
309
Returns a list of valid sub-keys.
311
value = _lxc.Container.get_keys(self, key)
315
elif value.endswith("\n"):
316
return value.rstrip("\n").split("\n")
320
def set_config_item(self, key, value):
322
Set a config key to a provided value.
323
The value can be a list for the keys supporting multiple values.
325
old_value = self.get_config_item(key)
327
# Check if it's a list
328
def set_key(key, value):
329
self.clear_config_item(key)
330
if isinstance(value, list):
332
if not _lxc.Container.set_config_item(self, key, entry):
335
_lxc.Container.set_config_item(self, key, value)
338
new_value = self.get_config_item(key)
340
if isinstance(value, str) and isinstance(new_value, str) and \
343
elif isinstance(value, list) and isinstance(new_value, list) and \
344
set(value) == set(new_value):
346
elif isinstance(value, str) and isinstance(new_value, list) and \
347
set([value]) == set(new_value):
350
set_key(key, old_value)
353
self.clear_config_item(key)
357
def list_containers(as_object=False):
359
List the containers on the system.
362
for entry in glob.glob("/var/lib/lxc/*/config"):
364
containers.append(Container(entry.split("/")[-2]))
366
containers.append(entry.split("/")[-2])