]>
Commit | Line | Data |
---|---|---|
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 | #include <Python.h> | |
25 | #include "structmember.h" | |
26 | #include <lxc/lxccontainer.h> | |
27 | #include <stdio.h> | |
28 | #include <sys/wait.h> | |
29 | ||
30 | typedef struct { | |
31 | PyObject_HEAD | |
32 | struct lxc_container *container; | |
33 | } Container; | |
34 | ||
35 | char** | |
36 | convert_tuple_to_char_pointer_array(PyObject *argv) { | |
37 | int argc = PyTuple_Size(argv); | |
38 | int i; | |
39 | ||
40 | char **result = (char**) malloc(sizeof(char*)*argc + 1); | |
41 | ||
42 | for (i = 0; i < argc; i++) { | |
43 | PyObject *pyobj = PyTuple_GetItem(argv, i); | |
44 | ||
45 | char *str = NULL; | |
46 | PyObject *pystr; | |
47 | if (!PyUnicode_Check(pyobj)) { | |
48 | PyErr_SetString(PyExc_ValueError, "Expected a string"); | |
49 | return NULL; | |
50 | } | |
51 | ||
52 | pystr = PyUnicode_AsUTF8String(pyobj); | |
53 | str = PyBytes_AsString(pystr); | |
54 | memcpy((char *) &result[i], (char *) &str, sizeof(str)); | |
55 | } | |
56 | ||
57 | result[argc] = NULL; | |
58 | ||
59 | return result; | |
60 | } | |
61 | ||
62 | static void | |
63 | Container_dealloc(Container* self) | |
64 | { | |
65 | Py_TYPE(self)->tp_free((PyObject*)self); | |
66 | } | |
67 | ||
68 | static PyObject * | |
69 | Container_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | |
70 | { | |
71 | Container *self; | |
72 | ||
73 | self = (Container *)type->tp_alloc(type, 0); | |
74 | ||
75 | return (PyObject *)self; | |
76 | } | |
77 | ||
78 | static int | |
79 | Container_init(Container *self, PyObject *args, PyObject *kwds) | |
80 | { | |
81 | static char *kwlist[] = {"name", NULL}; | |
82 | char *name = NULL; | |
83 | ||
84 | if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, | |
85 | &name)) | |
86 | return -1; | |
87 | ||
88 | self->container = lxc_container_new(name); | |
89 | if (!self->container) { | |
90 | fprintf(stderr, "%d: error creating lxc_container %s\n", __LINE__, name); | |
91 | return -1; | |
92 | } | |
93 | ||
94 | return 0; | |
95 | } | |
96 | ||
97 | // Container properties | |
98 | static PyObject * | |
99 | Container_config_file_name(Container *self, PyObject *args, PyObject *kwds) | |
100 | { | |
101 | return PyUnicode_FromString(self->container->config_file_name(self->container)); | |
102 | } | |
103 | ||
104 | static PyObject * | |
105 | Container_defined(Container *self, PyObject *args, PyObject *kwds) | |
106 | { | |
107 | if (self->container->is_defined(self->container)) { | |
108 | Py_RETURN_TRUE; | |
109 | } | |
110 | ||
111 | Py_RETURN_FALSE; | |
112 | } | |
113 | ||
114 | static PyObject * | |
115 | Container_init_pid(Container *self, PyObject *args, PyObject *kwds) | |
116 | { | |
117 | return Py_BuildValue("i", self->container->init_pid(self->container)); | |
118 | } | |
119 | ||
120 | static PyObject * | |
121 | Container_name(Container *self, PyObject *args, PyObject *kwds) | |
122 | { | |
123 | return PyUnicode_FromString(self->container->name); | |
124 | } | |
125 | ||
126 | static PyObject * | |
127 | Container_running(Container *self, PyObject *args, PyObject *kwds) | |
128 | { | |
129 | if (self->container->is_running(self->container)) { | |
130 | Py_RETURN_TRUE; | |
131 | } | |
132 | ||
133 | Py_RETURN_FALSE; | |
134 | } | |
135 | ||
136 | static PyObject * | |
137 | Container_state(Container *self, PyObject *args, PyObject *kwds) | |
138 | { | |
139 | return PyUnicode_FromString(self->container->state(self->container)); | |
140 | } | |
141 | ||
142 | // Container Functions | |
143 | static PyObject * | |
144 | Container_clear_config_item(Container *self, PyObject *args, PyObject *kwds) | |
145 | { | |
146 | static char *kwlist[] = {"key", NULL}; | |
147 | char *key = NULL; | |
148 | ||
149 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, | |
150 | &key)) | |
151 | Py_RETURN_FALSE; | |
152 | ||
153 | if (self->container->clear_config_item(self->container, key)) { | |
154 | Py_RETURN_TRUE; | |
155 | } | |
156 | ||
157 | Py_RETURN_FALSE; | |
158 | } | |
159 | ||
160 | static PyObject * | |
161 | Container_create(Container *self, PyObject *args, PyObject *kwds) | |
162 | { | |
163 | char* template_name = NULL; | |
164 | char** create_args = {NULL}; | |
165 | PyObject *vargs = NULL; | |
166 | static char *kwlist[] = {"template", "args", NULL}; | |
167 | ||
168 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|O", kwlist, | |
169 | &template_name, &vargs)) | |
170 | Py_RETURN_FALSE; | |
171 | ||
172 | if (vargs && PyTuple_Check(vargs)) { | |
173 | create_args = convert_tuple_to_char_pointer_array(vargs); | |
174 | if (!create_args) { | |
175 | return NULL; | |
176 | } | |
177 | } | |
178 | ||
179 | if (self->container->create(self->container, template_name, create_args)) { | |
180 | Py_RETURN_TRUE; | |
181 | } | |
182 | ||
183 | Py_RETURN_FALSE; | |
184 | } | |
185 | ||
186 | static PyObject * | |
187 | Container_destroy(Container *self, PyObject *args, PyObject *kwds) | |
188 | { | |
189 | if (self->container->destroy(self->container)) { | |
190 | Py_RETURN_TRUE; | |
191 | } | |
192 | ||
193 | Py_RETURN_FALSE; | |
194 | } | |
195 | ||
196 | static PyObject * | |
197 | Container_freeze(Container *self, PyObject *args, PyObject *kwds) | |
198 | { | |
199 | if (self->container->freeze(self->container)) { | |
200 | Py_RETURN_TRUE; | |
201 | } | |
202 | ||
203 | Py_RETURN_FALSE; | |
204 | } | |
205 | ||
206 | static PyObject * | |
207 | Container_get_cgroup_item(Container *self, PyObject *args, PyObject *kwds) | |
208 | { | |
209 | static char *kwlist[] = {"key", NULL}; | |
210 | char* key = NULL; | |
211 | int len = 0; | |
212 | ||
213 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, | |
214 | &key)) | |
215 | Py_RETURN_FALSE; | |
216 | ||
217 | len = self->container->get_cgroup_item(self->container, key, NULL, 0); | |
218 | ||
219 | if (len <= 0) { | |
220 | Py_RETURN_FALSE; | |
221 | } | |
222 | ||
223 | char* value = (char*) malloc(sizeof(char)*len + 1); | |
224 | if (self->container->get_cgroup_item(self->container, key, value, len + 1) != len) { | |
225 | Py_RETURN_FALSE; | |
226 | } | |
227 | ||
228 | return PyUnicode_FromString(value); | |
229 | } | |
230 | ||
231 | static PyObject * | |
232 | Container_get_config_item(Container *self, PyObject *args, PyObject *kwds) | |
233 | { | |
234 | static char *kwlist[] = {"key", NULL}; | |
235 | char* key = NULL; | |
236 | int len = 0; | |
237 | ||
238 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|", kwlist, | |
239 | &key)) | |
240 | Py_RETURN_FALSE; | |
241 | ||
242 | len = self->container->get_config_item(self->container, key, NULL, 0); | |
243 | ||
244 | if (len <= 0) { | |
245 | Py_RETURN_FALSE; | |
246 | } | |
247 | ||
248 | char* value = (char*) malloc(sizeof(char)*len + 1); | |
249 | if (self->container->get_config_item(self->container, key, value, len + 1) != len) { | |
250 | Py_RETURN_FALSE; | |
251 | } | |
252 | ||
253 | return PyUnicode_FromString(value); | |
254 | } | |
255 | ||
256 | static PyObject * | |
257 | Container_get_keys(Container *self, PyObject *args, PyObject *kwds) | |
258 | { | |
259 | static char *kwlist[] = {"key", NULL}; | |
260 | char* key = NULL; | |
261 | int len = 0; | |
262 | ||
263 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist, | |
264 | &key)) | |
265 | Py_RETURN_FALSE; | |
266 | ||
267 | len = self->container->get_keys(self->container, key, NULL, 0); | |
268 | ||
269 | if (len <= 0) { | |
270 | Py_RETURN_FALSE; | |
271 | } | |
272 | ||
273 | char* value = (char*) malloc(sizeof(char)*len + 1); | |
274 | if (self->container->get_keys(self->container, key, value, len + 1) != len) { | |
275 | Py_RETURN_FALSE; | |
276 | } | |
277 | ||
278 | return PyUnicode_FromString(value); | |
279 | } | |
280 | ||
281 | static PyObject * | |
282 | Container_load_config(Container *self, PyObject *args, PyObject *kwds) | |
283 | { | |
284 | static char *kwlist[] = {"path", NULL}; | |
285 | char* path = NULL; | |
286 | ||
287 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist, | |
288 | &path)) | |
289 | Py_RETURN_FALSE; | |
290 | ||
291 | if (self->container->load_config(self->container, path)) { | |
292 | Py_RETURN_TRUE; | |
293 | } | |
294 | ||
295 | Py_RETURN_FALSE; | |
296 | } | |
297 | ||
298 | static PyObject * | |
299 | Container_save_config(Container *self, PyObject *args, PyObject *kwds) | |
300 | { | |
301 | static char *kwlist[] = {"path", NULL}; | |
302 | char* path = NULL; | |
303 | ||
304 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist, | |
305 | &path)) | |
306 | Py_RETURN_FALSE; | |
307 | ||
308 | if (self->container->save_config(self->container, path)) { | |
309 | Py_RETURN_TRUE; | |
310 | } | |
311 | ||
312 | Py_RETURN_FALSE; | |
313 | } | |
314 | ||
315 | static PyObject * | |
316 | Container_set_cgroup_item(Container *self, PyObject *args, PyObject *kwds) | |
317 | { | |
318 | static char *kwlist[] = {"key", "value", NULL}; | |
319 | char *key = NULL; | |
320 | char *value = NULL; | |
321 | ||
322 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "ss|", kwlist, | |
323 | &key, &value)) | |
324 | Py_RETURN_FALSE; | |
325 | ||
326 | if (self->container->set_cgroup_item(self->container, key, value)) { | |
327 | Py_RETURN_TRUE; | |
328 | } | |
329 | ||
330 | Py_RETURN_FALSE; | |
331 | } | |
332 | ||
333 | static PyObject * | |
334 | Container_set_config_item(Container *self, PyObject *args, PyObject *kwds) | |
335 | { | |
336 | static char *kwlist[] = {"key", "value", NULL}; | |
337 | char *key = NULL; | |
338 | char *value = NULL; | |
339 | ||
340 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "ss|", kwlist, | |
341 | &key, &value)) | |
342 | Py_RETURN_FALSE; | |
343 | ||
344 | if (self->container->set_config_item(self->container, key, value)) { | |
345 | Py_RETURN_TRUE; | |
346 | } | |
347 | ||
348 | Py_RETURN_FALSE; | |
349 | } | |
350 | ||
351 | static PyObject * | |
352 | Container_shutdown(Container *self, PyObject *args, PyObject *kwds) | |
353 | { | |
354 | static char *kwlist[] = {"timeout", NULL}; | |
355 | int timeout = -1; | |
356 | ||
357 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist, | |
358 | &timeout)) | |
359 | Py_RETURN_FALSE; | |
360 | ||
361 | if (self->container->shutdown(self->container, timeout)) { | |
362 | Py_RETURN_TRUE; | |
363 | } | |
364 | ||
365 | Py_RETURN_FALSE; | |
366 | } | |
367 | ||
368 | static PyObject * | |
369 | Container_start(Container *self, PyObject *args, PyObject *kwds) | |
370 | { | |
371 | char** init_args = {NULL}; | |
372 | PyObject *useinit = NULL, *vargs = NULL; | |
373 | int init_useinit = 0; | |
374 | static char *kwlist[] = {"useinit", "cmd", NULL}; | |
375 | ||
376 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OO", kwlist, | |
377 | &useinit, &vargs)) | |
378 | Py_RETURN_FALSE; | |
379 | ||
380 | if (useinit && useinit == Py_True) { | |
381 | init_useinit = 1; | |
382 | } | |
383 | ||
384 | if (vargs && PyTuple_Check(vargs)) { | |
385 | init_args = convert_tuple_to_char_pointer_array(vargs); | |
386 | if (!init_args) { | |
387 | return NULL; | |
388 | } | |
389 | } | |
390 | ||
391 | self->container->want_daemonize(self->container); | |
392 | ||
393 | if (self->container->start(self->container, init_useinit, init_args)) { | |
394 | Py_RETURN_TRUE; | |
395 | } | |
396 | ||
397 | Py_RETURN_FALSE; | |
398 | } | |
399 | ||
400 | static PyObject * | |
401 | Container_stop(Container *self, PyObject *args, PyObject *kwds) | |
402 | { | |
403 | if (self->container->stop(self->container)) { | |
404 | Py_RETURN_TRUE; | |
405 | } | |
406 | ||
407 | Py_RETURN_FALSE; | |
408 | } | |
409 | ||
410 | static PyObject * | |
411 | Container_unfreeze(Container *self, PyObject *args, PyObject *kwds) | |
412 | { | |
413 | if (self->container->unfreeze(self->container)) { | |
414 | Py_RETURN_TRUE; | |
415 | } | |
416 | ||
417 | Py_RETURN_FALSE; | |
418 | } | |
419 | ||
420 | static PyObject * | |
421 | Container_wait(Container *self, PyObject *args, PyObject *kwds) | |
422 | { | |
423 | static char *kwlist[] = {"state", "timeout", NULL}; | |
424 | char *state = NULL; | |
425 | int timeout = -1; | |
426 | ||
427 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, | |
428 | &state, &timeout)) | |
429 | Py_RETURN_FALSE; | |
430 | ||
431 | if (self->container->wait(self->container, state, timeout)) { | |
432 | Py_RETURN_TRUE; | |
433 | } | |
434 | ||
435 | Py_RETURN_FALSE; | |
436 | } | |
437 | ||
438 | static PyGetSetDef Container_getseters[] = { | |
439 | {"config_file_name", | |
440 | (getter)Container_config_file_name, 0, | |
441 | "Path to the container configuration", | |
442 | NULL}, | |
443 | {"defined", | |
444 | (getter)Container_defined, 0, | |
445 | "Boolean indicating whether the container configuration exists", | |
446 | NULL}, | |
447 | {"init_pid", | |
448 | (getter)Container_init_pid, 0, | |
449 | "PID of the container's init process in the host's PID namespace", | |
450 | NULL}, | |
451 | {"name", | |
452 | (getter)Container_name, 0, | |
453 | "Container name", | |
454 | NULL}, | |
455 | {"running", | |
456 | (getter)Container_running, 0, | |
457 | "Boolean indicating whether the container is running or not", | |
458 | NULL}, | |
459 | {"state", | |
460 | (getter)Container_state, 0, | |
461 | "Container state", | |
462 | NULL}, | |
463 | }; | |
464 | ||
465 | static PyMethodDef Container_methods[] = { | |
466 | {"clear_config_item", (PyCFunction)Container_clear_config_item, METH_VARARGS | METH_KEYWORDS, | |
467 | "clear_config_item(key) -> boolean\n" | |
468 | "\n" | |
469 | "Clear the current value of a config key." | |
470 | }, | |
471 | {"create", (PyCFunction)Container_create, METH_VARARGS | METH_KEYWORDS, | |
472 | "create(template, args = (,)) -> boolean\n" | |
473 | "\n" | |
474 | "Create a new rootfs for the container, using the given template " | |
475 | "and passing some optional arguments to it." | |
476 | }, | |
477 | {"destroy", (PyCFunction)Container_destroy, METH_NOARGS, | |
478 | "destroy() -> boolean\n" | |
479 | "\n" | |
480 | "Destroys the container." | |
481 | }, | |
482 | {"freeze", (PyCFunction)Container_freeze, METH_NOARGS, | |
483 | "freeze() -> boolean\n" | |
484 | "\n" | |
485 | "Freezes the container and returns its return code." | |
486 | }, | |
487 | {"get_cgroup_item", (PyCFunction)Container_get_cgroup_item, METH_VARARGS | METH_KEYWORDS, | |
488 | "get_cgroup_item(key) -> string\n" | |
489 | "\n" | |
490 | "Get the current value of a cgroup entry." | |
491 | }, | |
492 | {"get_config_item", (PyCFunction)Container_get_config_item, METH_VARARGS | METH_KEYWORDS, | |
493 | "get_config_item(key) -> string\n" | |
494 | "\n" | |
495 | "Get the current value of a config key." | |
496 | }, | |
497 | {"get_keys", (PyCFunction)Container_get_keys, METH_VARARGS | METH_KEYWORDS, | |
498 | "get_keys(key) -> string\n" | |
499 | "\n" | |
500 | "Get a list of valid sub-keys for a key." | |
501 | }, | |
502 | {"load_config", (PyCFunction)Container_load_config, METH_VARARGS | METH_KEYWORDS, | |
503 | "load_config(path = DEFAULT) -> boolean\n" | |
504 | "\n" | |
505 | "Read the container configuration from its default " | |
506 | "location or from an alternative location if provided." | |
507 | }, | |
508 | {"save_config", (PyCFunction)Container_save_config, METH_VARARGS | METH_KEYWORDS, | |
509 | "save_config(path = DEFAULT) -> boolean\n" | |
510 | "\n" | |
511 | "Save the container configuration to its default " | |
512 | "location or to an alternative location if provided." | |
513 | }, | |
514 | {"set_cgroup_item", (PyCFunction)Container_set_cgroup_item, METH_VARARGS | METH_KEYWORDS, | |
515 | "set_cgroup_item(key, value) -> boolean\n" | |
516 | "\n" | |
517 | "Set a cgroup entry to the provided value." | |
518 | }, | |
519 | {"set_config_item", (PyCFunction)Container_set_config_item, METH_VARARGS | METH_KEYWORDS, | |
520 | "set_config_item(key, value) -> boolean\n" | |
521 | "\n" | |
522 | "Set a config key to the provided value." | |
523 | }, | |
524 | {"shutdown", (PyCFunction)Container_shutdown, METH_VARARGS | METH_KEYWORDS, | |
525 | "shutdown(timeout = -1) -> boolean\n" | |
526 | "\n" | |
527 | "Sends SIGPWR to the container and wait for it to shutdown " | |
528 | "unless timeout is set to a positive value, in which case " | |
529 | "the container will be killed when the timeout is reached." | |
530 | }, | |
531 | {"start", (PyCFunction)Container_start, METH_VARARGS | METH_KEYWORDS, | |
532 | "start(useinit = False, cmd = (,)) -> boolean\n" | |
533 | "\n" | |
534 | "Start the container, optionally using lxc-init and " | |
535 | "an alternate init command, then returns its return code." | |
536 | }, | |
537 | {"stop", (PyCFunction)Container_stop, METH_NOARGS, | |
538 | "stop() -> boolean\n" | |
539 | "\n" | |
540 | "Stop the container and returns its return code." | |
541 | }, | |
542 | {"unfreeze", (PyCFunction)Container_unfreeze, METH_NOARGS, | |
543 | "unfreeze() -> boolean\n" | |
544 | "\n" | |
545 | "Unfreezes the container and returns its return code." | |
546 | }, | |
547 | {"wait", (PyCFunction)Container_wait, METH_VARARGS | METH_KEYWORDS, | |
548 | "wait(state, timeout = -1) -> boolean\n" | |
549 | "\n" | |
550 | "Wait for the container to reach a given state or timeout." | |
551 | }, | |
552 | {NULL} /* Sentinel */ | |
553 | }; | |
554 | ||
555 | static PyTypeObject _lxc_ContainerType = { | |
556 | PyVarObject_HEAD_INIT(NULL, 0) | |
557 | "lxc.Container", /* tp_name */ | |
558 | sizeof(Container), /* tp_basicsize */ | |
559 | 0, /* tp_itemsize */ | |
560 | (destructor)Container_dealloc, /* tp_dealloc */ | |
561 | 0, /* tp_print */ | |
562 | 0, /* tp_getattr */ | |
563 | 0, /* tp_setattr */ | |
564 | 0, /* tp_reserved */ | |
565 | 0, /* tp_repr */ | |
566 | 0, /* tp_as_number */ | |
567 | 0, /* tp_as_sequence */ | |
568 | 0, /* tp_as_mapping */ | |
569 | 0, /* tp_hash */ | |
570 | 0, /* tp_call */ | |
571 | 0, /* tp_str */ | |
572 | 0, /* tp_getattro */ | |
573 | 0, /* tp_setattro */ | |
574 | 0, /* tp_as_buffer */ | |
575 | Py_TPFLAGS_DEFAULT | | |
576 | Py_TPFLAGS_BASETYPE, /* tp_flags */ | |
577 | "Container objects", /* tp_doc */ | |
578 | 0, /* tp_traverse */ | |
579 | 0, /* tp_clear */ | |
580 | 0, /* tp_richcompare */ | |
581 | 0, /* tp_weaklistoffset */ | |
582 | 0, /* tp_iter */ | |
583 | 0, /* tp_iternext */ | |
584 | Container_methods, /* tp_methods */ | |
585 | 0, /* tp_members */ | |
586 | Container_getseters, /* tp_getset */ | |
587 | 0, /* tp_base */ | |
588 | 0, /* tp_dict */ | |
589 | 0, /* tp_descr_get */ | |
590 | 0, /* tp_descr_set */ | |
591 | 0, /* tp_dictoffset */ | |
592 | (initproc)Container_init, /* tp_init */ | |
593 | 0, /* tp_alloc */ | |
594 | Container_new, /* tp_new */ | |
595 | }; | |
596 | ||
597 | static PyModuleDef _lxcmodule = { | |
598 | PyModuleDef_HEAD_INIT, | |
599 | "_lxc", | |
600 | "Binding for liblxc in python", | |
601 | -1, | |
602 | NULL, NULL, NULL, NULL, NULL | |
603 | }; | |
604 | ||
605 | PyMODINIT_FUNC | |
606 | PyInit__lxc(void) | |
607 | { | |
608 | PyObject* m; | |
609 | ||
610 | if (PyType_Ready(&_lxc_ContainerType) < 0) | |
611 | return NULL; | |
612 | ||
613 | m = PyModule_Create(&_lxcmodule); | |
614 | if (m == NULL) | |
615 | return NULL; | |
616 | ||
617 | Py_INCREF(&_lxc_ContainerType); | |
618 | PyModule_AddObject(m, "Container", (PyObject *)&_lxc_ContainerType); | |
619 | return m; | |
620 | } |