~ubuntu-branches/ubuntu/quantal/lxc/quantal-201208301614

« back to all changes in this revision

Viewing changes to debian/python-lxc/lxc/__init__.py

  • Committer: Package Import Robot
  • Author(s): Stéphane Graber, Stéphane Graber, Serge Hallyn
  • Date: 2012-08-22 11:50:51 UTC
  • Revision ID: package-import@ubuntu.com-20120822115051-2cmb0vjrkyadjovf
Tags: 0.8.0~rc1-4ubuntu28
[ Stéphane Graber ]
* Merge liblxc changes:
  - Build-depend on automake as autogen.sh is now run at build time.
  - Introduce new liblxc0 binary package
  - Make lxc to depend on liblxc0
  - Move library to the new binary package
  - Change libdir to be the public multi-arch path
  - Build with --disable-rpath
  - Move all the test binaries to a lxc-test-* namespace
* Merge python3-lxc changes:
  - Introduce new python3-lxc binary package
  - Update debian/rules to build the python3 code
* Update lxc-start-ephemeral:
  - Replace tabs by 4 spaces, fix indentation
  - Fix code to work properly as non-root (calling sudo where needed)

[ Serge Hallyn ]
* confile.c: support hooks in save_config().
* conf.h: Add array of hook names

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
21
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 
22
#
 
23
 
 
24
import _lxc
 
25
import glob
 
26
import os
 
27
import subprocess
 
28
import tempfile
 
29
import time
 
30
 
 
31
 
 
32
class ContainerNetwork():
 
33
    props = {}
 
34
 
 
35
    def __init__(self, container, index):
 
36
        self.container = container
 
37
        self.index = index
 
38
 
 
39
        for key in self.container.get_keys("lxc.network.%s" % self.index):
 
40
            if "." in key:
 
41
                self.props[key.replace(".", "_")] = key
 
42
            else:
 
43
                self.props[key] = key
 
44
 
 
45
        if not self.props:
 
46
            return False
 
47
 
 
48
    def __delattr__(self, key):
 
49
        if key in ["container", "index", "props"]:
 
50
            return object.__delattr__(self, key)
 
51
 
 
52
        if key not in self.props:
 
53
            raise AttributeError("'%s' network has no attribute '%s'" % (
 
54
                    self.__get_network_item("type"), key))
 
55
 
 
56
        return self.__clear_network_item(self.props[key])
 
57
 
 
58
    def __dir__(self):
 
59
        return sorted(self.props.keys())
 
60
 
 
61
    def __getattr__(self, key):
 
62
        if key in ["container", "index", "props"]:
 
63
            return object.__getattribute__(self, key)
 
64
 
 
65
        if key not in self.props:
 
66
            raise AttributeError("'%s' network has no attribute '%s'" % (
 
67
                    self.__get_network_item("type"), key))
 
68
 
 
69
        return self.__get_network_item(self.props[key])
 
70
 
 
71
    def __hasattr__(self, key):
 
72
        if key in ["container", "index", "props"]:
 
73
            return object.__hasattr__(self, key)
 
74
 
 
75
        if key not in self.props:
 
76
            raise AttributeError("'%s' network has no attribute '%s'" % (
 
77
                    self.__get_network_item("type"), key))
 
78
 
 
79
        return True
 
80
 
 
81
    def __repr__(self):
 
82
        return "'%s' network at index '%s'" % (
 
83
            self.__get_network_item("type"), self.index)
 
84
 
 
85
    def __setattr__(self, key, value):
 
86
        if key in ["container", "index", "props"]:
 
87
            return object.__setattr__(self, key, value)
 
88
 
 
89
        if key not in self.props:
 
90
            raise AttributeError("'%s' network has no attribute '%s'" % (
 
91
                    self.__get_network_item("type"), key))
 
92
 
 
93
        return self.__set_network_item(self.props[key], value)
 
94
 
 
95
    def __clear_network_item(self, key):
 
96
        return self.container.clear_config_item("lxc.network.%s.%s" % (
 
97
                    self.index, key))
 
98
 
 
99
    def __get_network_item(self, key):
 
100
        return self.container.get_config_item("lxc.network.%s.%s" % (
 
101
                    self.index, key))
 
102
 
 
103
    def __set_network_item(self, key, value):
 
104
        return self.container.set_config_item("lxc.network.%s.%s" % (
 
105
                    self.index, key), value)
 
106
 
 
107
 
 
108
class ContainerNetworkList():
 
109
    def __init__(self, container):
 
110
        self.container = container
 
111
 
 
112
    def __getitem__(self, index):
 
113
        count = len(self.container.get_config_item("lxc.network"))
 
114
        if index >= count:
 
115
            raise IndexError("list index out of range")
 
116
 
 
117
        return ContainerNetwork(self.container, index)
 
118
 
 
119
    def __len__(self):
 
120
        return len(self.container.get_config_item("lxc.network"))
 
121
 
 
122
    def add(self, network_type):
 
123
        index = len(self.container.get_config_item("lxc.network"))
 
124
 
 
125
        return self.container.set_config_item("lxc.network.%s.type" % index,
 
126
                    network_type)
 
127
 
 
128
    def remove(self, index):
 
129
        count = len(self.container.get_config_item("lxc.network"))
 
130
        if index >= count:
 
131
            raise IndexError("list index out of range")
 
132
 
 
133
        return self.container.clear_config_item("lxc.network.%s" % index)
 
134
 
 
135
 
 
136
class Container(_lxc.Container):
 
137
    def __init__(self, name):
 
138
        """
 
139
            Creates a new Container instance.
 
140
        """
 
141
 
 
142
        _lxc.Container.__init__(self, name)
 
143
        self.network = ContainerNetworkList(self)
 
144
 
 
145
    def append_config_item(self, key, value):
 
146
        """
 
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'.
 
149
        """
 
150
 
 
151
        return _lxc.Container.set_config_item(self, key, value)
 
152
 
 
153
    def attach(self, namespace="ALL", *cmd):
 
154
        """
 
155
            Attach to a running container.
 
156
        """
 
157
 
 
158
        if not self.running:
 
159
            return False
 
160
 
 
161
        attach = ["lxc-attach", "-n", self.name]
 
162
        if namespace != "ALL":
 
163
            attach += ["-s", namespace]
 
164
 
 
165
        if cmd:
 
166
            attach += ["--"] + list(cmd)
 
167
 
 
168
        if subprocess.call(
 
169
                attach,
 
170
                universal_newlines=True) != 0:
 
171
            return False
 
172
        return True
 
173
 
 
174
    def create(self, template, args={}):
 
175
        """
 
176
            Create a new rootfs for the container.
 
177
 
 
178
            "template" must be a valid template name.
 
179
 
 
180
            "args" (optional) is a dictionary of parameters and values to pass
 
181
            to the template.
 
182
        """
 
183
 
 
184
        template_args = []
 
185
        for item in args.items():
 
186
            template_args.append("--%s" % item[0])
 
187
            template_args.append("%s" % item[1])
 
188
 
 
189
        return _lxc.Container.create(self, template, tuple(template_args))
 
190
 
 
191
    def clone(self, container):
 
192
        """
 
193
            Clone an existing container into a new one.
 
194
        """
 
195
 
 
196
        if self.defined:
 
197
            return False
 
198
 
 
199
        if isinstance(container, Container):
 
200
            source = container
 
201
        else:
 
202
            source = Container(container)
 
203
 
 
204
        if not source.defined:
 
205
            return False
 
206
 
 
207
        if subprocess.call(
 
208
                    ["lxc-clone", "-o", source.name, "-n", self.name],
 
209
                    universal_newlines=True) != 0:
 
210
            return False
 
211
 
 
212
        self.load_config()
 
213
        return True
 
214
 
 
215
    def console(self, tty="1"):
 
216
        """
 
217
            Access the console of a container.
 
218
        """
 
219
 
 
220
        if not self.running:
 
221
            return False
 
222
 
 
223
        if subprocess.call(
 
224
                    ["lxc-console", "-n", self.name, "-t", "%s" % tty],
 
225
                    universal_newlines=True) != 0:
 
226
            return False
 
227
        return True
 
228
 
 
229
    def get_config_item(self, key):
 
230
        """
 
231
            Returns the value for a given config key.
 
232
            A list is returned when multiple values are set.
 
233
        """
 
234
        value = _lxc.Container.get_config_item(self, key)
 
235
 
 
236
        if value is False:
 
237
            return False
 
238
        elif value.endswith("\n"):
 
239
            return value.rstrip("\n").split("\n")
 
240
        else:
 
241
            return value
 
242
 
 
243
    def get_ips(self, timeout=60, interface=None, protocol=None):
 
244
        """
 
245
            Returns the list of IP addresses for the container.
 
246
        """
 
247
 
 
248
        if not self.defined or not self.running:
 
249
            return False
 
250
 
 
251
        try:
 
252
            os.makedirs("/run/netns")
 
253
        except:
 
254
            pass
 
255
 
 
256
        path = tempfile.mktemp(dir="/run/netns")
 
257
 
 
258
        os.symlink("/proc/%s/ns/net" % self.init_pid, path)
 
259
 
 
260
        ips = []
 
261
 
 
262
        count = 0
 
263
        while count < timeout:
 
264
            base_cmd = ["ip", "netns", "exec", path.split("/")[-1], "ip"]
 
265
 
 
266
            # Get IPv6
 
267
            if protocol in ("ipv6", None):
 
268
                ip6_cmd = base_cmd + ["-6", "addr", "show", "scope", "global"]
 
269
                if interface:
 
270
                    ip = subprocess.Popen(ip6_cmd + ["dev", interface],
 
271
                            stdout=subprocess.PIPE, universal_newlines=True)
 
272
                else:
 
273
                    ip = subprocess.Popen(ip6_cmd, stdout=subprocess.PIPE,
 
274
                            universal_newlines=True)
 
275
 
 
276
                ip.wait()
 
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])
 
281
 
 
282
            # Get IPv4
 
283
            if protocol in ("ipv4", None):
 
284
                ip4_cmd = base_cmd + ["-4", "addr", "show", "scope", "global"]
 
285
                if interface:
 
286
                    ip = subprocess.Popen(ip4_cmd + ["dev", interface],
 
287
                            stdout=subprocess.PIPE, universal_newlines=True)
 
288
                else:
 
289
                    ip = subprocess.Popen(ip4_cmd, stdout=subprocess.PIPE,
 
290
                            universal_newlines=True)
 
291
 
 
292
                ip.wait()
 
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])
 
297
 
 
298
            if ips:
 
299
                break
 
300
 
 
301
            time.sleep(1)
 
302
            count += 1
 
303
 
 
304
        os.remove(path)
 
305
        return ips
 
306
 
 
307
    def get_keys(self, key):
 
308
        """
 
309
            Returns a list of valid sub-keys.
 
310
        """
 
311
        value = _lxc.Container.get_keys(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
 
 
320
    def set_config_item(self, key, value):
 
321
        """
 
322
            Set a config key to a provided value.
 
323
            The value can be a list for the keys supporting multiple values.
 
324
        """
 
325
        old_value = self.get_config_item(key)
 
326
 
 
327
        # Check if it's a list
 
328
        def set_key(key, value):
 
329
            self.clear_config_item(key)
 
330
            if isinstance(value, list):
 
331
                for entry in value:
 
332
                    if not _lxc.Container.set_config_item(self, key, entry):
 
333
                        return False
 
334
            else:
 
335
                _lxc.Container.set_config_item(self, key, value)
 
336
 
 
337
        set_key(key, value)
 
338
        new_value = self.get_config_item(key)
 
339
 
 
340
        if isinstance(value, str) and isinstance(new_value, str) and \
 
341
           value == new_value:
 
342
            return True
 
343
        elif isinstance(value, list) and isinstance(new_value, list) and \
 
344
           set(value) == set(new_value):
 
345
            return True
 
346
        elif isinstance(value, str) and isinstance(new_value, list) and \
 
347
           set([value]) == set(new_value):
 
348
            return True
 
349
        elif old_value:
 
350
            set_key(key, old_value)
 
351
            return False
 
352
        else:
 
353
            self.clear_config_item(key)
 
354
            return False
 
355
 
 
356
 
 
357
def list_containers(as_object=False):
 
358
    """
 
359
        List the containers on the system.
 
360
    """
 
361
    containers = []
 
362
    for entry in glob.glob("/var/lib/lxc/*/config"):
 
363
        if as_object:
 
364
            containers.append(Container(entry.split("/")[-2]))
 
365
        else:
 
366
            containers.append(entry.split("/")[-2])
 
367
    return containers