Add support for guaranteed CPU shares
[util-vserver.git] / python / vserverimpl.c
1 /* Copyright 2005 Princeton University
2
3 Redistribution and use in source and binary forms, with or without
4 modification, are permitted provided that the following conditions
5 are met: 
6
7     * Redistributions of source code must retain the above copyright
8       notice, this list of conditions and the following disclaimer.
9       
10     * Redistributions in binary form must reproduce the above
11       copyright notice, this list of conditions and the following
12       disclaimer in the documentation and/or other materials provided
13       with the distribution.
14       
15     * Neither the name of the copyright holder nor the names of its
16       contributors may be used to endorse or promote products derived
17       from this software without specific prior written permission.
18       
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PRINCETON
23 UNIVERSITY OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26 OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27 AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
29 WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 POSSIBILITY OF SUCH DAMAGE. 
31
32 */
33
34 #include <Python.h>
35
36 #include <errno.h>
37 #include <stdint.h>
38 #include <unistd.h>
39
40 #include "config.h"
41 #include "pathconfig.h"
42 #include "planetlab.h"
43 #include "virtual.h"
44 #include "vserver.h"
45 #include "vserver-internal.h"
46
47 #define NONE  ({ Py_INCREF(Py_None); Py_None; })
48
49 static int
50 get_rspec(PyObject *resources, rspec_t *rspec)
51 {
52   int  result = -1;
53   PyObject  *cpu_share;
54   PyObject  *sched_flags = NULL;
55
56   if (!PyMapping_Check(resources))
57     {
58       PyErr_SetString(PyExc_TypeError, "invalid rspec");
59       return -1;
60     }
61
62   /* get CPU share */
63   if (!(cpu_share = PyMapping_GetItemString(resources, "nm_cpu_share")))
64     return -1;
65   if (!PyInt_Check(cpu_share))
66     {
67       PyErr_SetString(PyExc_TypeError, "nm_cpu_share not an integer");
68       goto out;
69     }
70   rspec->cpu_share = PyInt_AS_LONG(cpu_share);
71
72   /* check whether this share should be guaranteed */
73   rspec->cpu_sched_flags = VC_VXF_SCHED_FLAGS;
74   result = 0;
75   if ((sched_flags = PyMapping_GetItemString(resources, "nm_sched_flags")))
76     {
77       const char  *flagstr;
78
79       if (!(flagstr = PyString_AsString(sched_flags)))
80         result = -1;
81       else if (!strcmp(flagstr, "guaranteed"))
82         rspec->cpu_sched_flags &= ~VC_VXF_SCHED_SHARE;
83       Py_DECREF(sched_flags);
84     }
85   else
86     /* not an error if nm_sched_flags is missing */
87     PyErr_Clear();
88
89  out:
90   Py_DECREF(cpu_share);
91
92   return result;
93 }
94
95 /*
96  * context create
97  */
98 static PyObject *
99 vserver_chcontext(PyObject *self, PyObject *args)
100 {
101   xid_t  ctx;
102   uint32_t  flags = 0;
103   uint32_t  bcaps = ~vc_get_insecurebcaps();
104   rspec_t  rspec = { 32, VC_VXF_SCHED_FLAGS, -1, -1 };
105   PyObject  *resources;
106
107   if (!PyArg_ParseTuple(args, "IO|K", &ctx, &resources, &flags) ||
108       get_rspec(resources, &rspec))
109     return NULL;
110
111   if (pl_chcontext(ctx, flags, bcaps, &rspec))
112     return PyErr_SetFromErrno(PyExc_OSError);
113
114   return NONE;
115 }
116
117 static PyObject *
118 vserver_set_rlimit(PyObject *self, PyObject *args) {
119         struct vc_rlimit limits;
120         xid_t xid;
121         int resource;
122         PyObject *ret;
123
124         limits.min = VC_LIM_KEEP;
125         limits.soft = VC_LIM_KEEP;
126         limits.hard = VC_LIM_KEEP;
127
128         if (!PyArg_ParseTuple(args, "IiL", &xid, &resource, &limits.hard))
129                 return NULL;
130
131         if (vc_set_rlimit(xid, resource, &limits)) 
132                 ret = PyErr_SetFromErrno(PyExc_OSError);
133         else if (vc_get_rlimit(xid, resource, &limits)==-1)
134                 ret = PyErr_SetFromErrno(PyExc_OSError);
135         else
136                 ret = Py_BuildValue("L",limits.hard);
137
138         return ret;
139 }
140
141 static PyObject *
142 vserver_get_rlimit(PyObject *self, PyObject *args) {
143         struct vc_rlimit limits;
144         xid_t xid;
145         int resource;
146         PyObject *ret;
147
148         limits.min = VC_LIM_KEEP;
149         limits.soft = VC_LIM_KEEP;
150         limits.hard = VC_LIM_KEEP;
151
152         if (!PyArg_ParseTuple(args, "Ii", &xid, &resource))
153                 return NULL;
154
155         if (vc_get_rlimit(xid, resource, &limits)==-1)
156                 ret = PyErr_SetFromErrno(PyExc_OSError);
157         else
158                 ret = Py_BuildValue("L",limits.hard);
159
160         return ret;
161 }
162
163 /*
164  * setsched
165  */
166 static PyObject *
167 vserver_setsched(PyObject *self, PyObject *args)
168 {
169   xid_t  ctx;
170   rspec_t  rspec = { 32, VC_VXF_SCHED_FLAGS, -1, -1 };
171   PyObject  *resources;
172
173   if (!PyArg_ParseTuple(args, "IO", &ctx, &resources) ||
174       get_rspec(resources, &rspec))
175     return NULL;
176
177   /* ESRCH indicates that there are no processes in the context */
178   if (pl_setsched(ctx, rspec.cpu_share, rspec.cpu_sched_flags) &&
179       errno != ESRCH)
180     return PyErr_SetFromErrno(PyExc_OSError);
181
182   return NONE;
183 }
184
185 static PyObject *
186 vserver_get_dlimit(PyObject *self, PyObject *args)
187 {
188         PyObject *res;
189         char* path;
190         unsigned xid;
191         struct vcmd_ctx_dlimit_v0 data;
192         int r;
193
194         if (!PyArg_ParseTuple(args, "si", &path,&xid))
195                 return NULL;
196
197         memset(&data, 0, sizeof(data));
198         data.name = path;
199         data.flags = 0;
200         r = vserver(VCMD_get_dlimit, xid, &data);
201         if (r>=0) {
202                 res = Py_BuildValue("(i,i,i,i,i)",
203                                     data.space_used,
204                                     data.space_total,
205                                     data.inodes_used,
206                                     data.inodes_total,
207                                     data.reserved);
208         } else {
209                 res = PyErr_SetFromErrno(PyExc_OSError);
210         }
211
212         return res;
213 }
214
215
216 static PyObject *
217 vserver_set_dlimit(PyObject *self, PyObject *args)
218 {
219         char* path;
220         unsigned xid;
221         struct vcmd_ctx_dlimit_base_v0 init;
222         struct vcmd_ctx_dlimit_v0 data;
223
224         memset(&data,0,sizeof(data));
225         if (!PyArg_ParseTuple(args, "siiiiii", &path,
226                               &xid,
227                               &data.space_used,
228                               &data.space_total,
229                               &data.inodes_used,
230                               &data.inodes_total,
231                               &data.reserved))
232                 return NULL;
233
234         data.name = path;
235         data.flags = 0;
236
237         memset(&init, 0, sizeof(init));
238         init.name = path;
239         init.flags = 0;
240
241         if ((vserver(VCMD_add_dlimit, xid, &init) && errno != EEXIST) ||
242             vserver(VCMD_set_dlimit, xid, &data))
243           return PyErr_SetFromErrno(PyExc_OSError);
244
245         return NONE;    
246 }
247
248 static PyObject *
249 vserver_unset_dlimit(PyObject *self, PyObject *args)
250 {
251   char  *path;
252   unsigned  xid;
253   struct vcmd_ctx_dlimit_base_v0  init;
254
255   if (!PyArg_ParseTuple(args, "si", &path, &xid))
256     return NULL;
257
258   memset(&init, 0, sizeof(init));
259   init.name = path;
260   init.flags = 0;
261
262   if (vserver(VCMD_rem_dlimit, xid, &init) && errno != ESRCH)
263     return PyErr_SetFromErrno(PyExc_OSError);
264
265   return NONE;  
266 }
267
268 static PyObject *
269 vserver_killall(PyObject *self, PyObject *args)
270 {
271   xid_t  ctx;
272   int  sig;
273
274   if (!PyArg_ParseTuple(args, "Ii", &ctx, &sig))
275     return NULL;
276
277   if (vc_ctx_kill(ctx, 0, sig) && errno != ESRCH)
278     return PyErr_SetFromErrno(PyExc_OSError);
279
280   return NONE;
281 }
282
283 static PyMethodDef  methods[] = {
284   { "chcontext", vserver_chcontext, METH_VARARGS,
285     "chcontext to vserver with provided flags" },
286   { "setsched", vserver_setsched, METH_VARARGS,
287     "Change vserver scheduling attributes for given vserver context" },
288   { "setdlimit", vserver_set_dlimit, METH_VARARGS,
289     "Set disk limits for given vserver context" },
290   { "unsetdlimit", vserver_unset_dlimit, METH_VARARGS,
291     "Remove disk limits for given vserver context" },
292   { "getdlimit", vserver_get_dlimit, METH_VARARGS,
293     "Get disk limits for given vserver context" },
294   { "setrlimit", vserver_set_rlimit, METH_VARARGS,
295     "Set resource limits for given resource of a vserver context" },
296   { "getrlimit", vserver_get_rlimit, METH_VARARGS,
297     "Get resource limits for given resource of a vserver context" },
298   { "killall", vserver_killall, METH_VARARGS,
299     "Send signal to all processes in vserver context" },
300   { NULL, NULL, 0, NULL }
301 };
302
303 PyMODINIT_FUNC
304 initvserverimpl(void)
305 {
306   PyObject  *mod;
307
308   mod = Py_InitModule("vserverimpl", methods);
309
310   /* export the set of 'safe' capabilities */
311   PyModule_AddIntConstant(mod, "CAP_SAFE", ~vc_get_insecurebcaps());
312
313   /* export the default vserver directory */
314   PyModule_AddStringConstant(mod, "VSERVER_BASEDIR", DEFAULT_VSERVERDIR);
315
316   /* export limit-related constants */
317   PyModule_AddIntConstant(mod, "DLIMIT_KEEP", (int)CDLIM_KEEP);
318   PyModule_AddIntConstant(mod, "DLIMIT_INF", (int)CDLIM_INFINITY);
319 }