Message ID | 20190916114048.17699-1-wipawel@amazon.de (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | None | expand |
On 9/16/19 12:40 PM, Pawel Wieczorkiewicz wrote: > Extend the XC python bindings library to support also all common > livepatch operations and actions. > > Add the python bindings for the following operations: > - status (pyxc_livepatch_status): > Requires a payload name as an input. > Returns a status dict containing a state string and a return code > integer. > - action (pyxc_livepatch_action): > Requires a payload name and an action id as an input. Timeout and > flags are optional parameters. > Returns a return code integer. > - upload (pyxc_livepatch_upload): > Requires a payload name and a module's filename as an input. > Returns a return code integer. > - list (pyxc_livepatch_list): > Takes no parameters. > Returns a list of dicts containing each payload's: > * name as a string > * state as a string > * return code as an integer > * list of metadata key=value strings > > Each functions throws an exception error based on the errno value > received from its corresponding libxc function call. > > Signed-off-by: Pawel Wieczorkiewicz <wipawel@amazon.de> > Reviewed-by: Martin Mazein <amazein@amazon.de> > Reviewed-by: Andra-Irina Paraschiv <andraprs@amazon.com> > Reviewed-by: Leonard Foerster <foersleo@amazon.de> > Reviewed-by: Norbert Manthey <nmanthey@amazon.de> > Acked-by: Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com> This will be very useful, thanks! > --- > Changed since v1: > * changed PyList_Append() with PyList_SetItem() as requested by > Marek > > tools/python/xen/lowlevel/xc/xc.c | 273 ++++++++++++++++++++++++++++++++++++++ > 1 file changed, 273 insertions(+) > snip> +static PyObject *pyxc_livepatch_action(XcObject *self, > + PyObject *args, > + PyObject *kwds) > +{ > + int (*action_func)(xc_interface *xch, char *name, uint32_t timeout, uint64_t flags); > + char *name; > + unsigned int action; > + uint32_t timeout; > + uint64_t flags; > + int rc; > + > + static char *kwd_list[] = { "name", "action", "timeout", "flags", NULL }; > + > + if ( !PyArg_ParseTupleAndKeywords(args, kwds, "sI|Ik", kwd_list, > + &name, &action, &timeout, &flags) ) > + goto error; > + > + switch (action) > + { > + case LIVEPATCH_ACTION_UNLOAD: > + action_func = xc_livepatch_unload; > + break; > + case LIVEPATCH_ACTION_REVERT: > + action_func = xc_livepatch_revert; > + break; > + case LIVEPATCH_ACTION_APPLY: > + action_func = xc_livepatch_apply; > + break; > + case LIVEPATCH_ACTION_REPLACE: > + action_func = xc_livepatch_replace; > + break; > + default: > + goto error; > + } > + > + rc = action_func(self->xc_handle, name, timeout, flags); > + if ( rc ) > + goto error; > + > + return Py_BuildValue("i", rc); For this and all the other functions which return zero on success, IMO returning None would be more Pythonic. > +error: > + return pyxc_error_to_exception(self->xc_handle); > +} > + > +static PyObject *pyxc_livepatch_upload(XcObject *self, > + PyObject *args, > + PyObject *kwds) > +{ > + unsigned char *fbuf = MAP_FAILED; > + char *name, *filename; > + struct stat buf; > + int fd = 0, rc; > + ssize_t len; > + > + static char *kwd_list[] = { "name", "filename", NULL }; > + > + if ( !PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwd_list, > + &name, &filename)) > + goto error; > + > + fd = open(filename, O_RDONLY); > + if ( fd < 0 ) > + goto error; > + > + if ( stat(filename, &buf) != 0 ) > + goto error; I think it would be better to use fstat() to avoid a second path lookup potentially pointing to a different file. > + > + len = buf.st_size; > + fbuf = mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0); > + if ( fbuf == MAP_FAILED ) > + goto error; > + > + rc = xc_livepatch_upload(self->xc_handle, name, fbuf, len); > + if ( rc ) > + goto error; > + > + if ( munmap(fbuf, len) ) > + { > + fbuf = MAP_FAILED; > + goto error; > + } > + close(fd); > + > + return Py_BuildValue("i", rc);; Stray semicolon > +error: > + if ( fbuf != MAP_FAILED ) > + munmap(fbuf, len); > + if ( fd >= 0 ) > + close(fd); You should probably save & restore errno so you can return the original error. > + return pyxc_error_to_exception(self->xc_handle); Maybe you can have a conditional return to avoid duplicating the munmap() & close()? E.g. return rc ? pyxc_error_to_exception(self->xc_handle) : ... > +} > + > +static PyObject *pyxc_livepatch_list(XcObject *self) > +{ > + PyObject *list; > + unsigned int nr, done, left, i; > + xen_livepatch_status_t *info = NULL; > + char *name = NULL; > + char *metadata = NULL; > + uint32_t *len = NULL; > + uint32_t *metadata_len = NULL; > + uint64_t name_total_size, metadata_total_size; > + off_t name_off, metadata_off; > + int rc; > + > + rc = xc_livepatch_list_get_sizes(self->xc_handle, &nr, > + &name_total_size, &metadata_total_size); > + if ( rc ) > + goto error; > + > + if ( nr == 0 ) > + return PyList_New(0); > + > + rc = ENOMEM; > + info = malloc(nr * sizeof(*info)); > + if ( !info ) > + goto error; > + > + name = malloc(name_total_size * sizeof(*name)); > + if ( !name ) > + goto error; > + > + len = malloc(nr * sizeof(*len)); > + if ( !len ) > + goto error; > + > + metadata = malloc(metadata_total_size * sizeof(*metadata)); > + if ( !metadata ) > + goto error; > + > + metadata_len = malloc(nr * sizeof(*metadata_len)); > + if ( !metadata_len ) > + goto error; > + > + rc = xc_livepatch_list(self->xc_handle, nr, 0, info, > + name, len, name_total_size, > + metadata, metadata_len, metadata_total_size, > + &done, &left); > + if ( rc ) > + goto error; Should you also check done and left as is done in xen-livepatch.c? if ( rc || done != nr || left > 0) > + > + list = PyList_New(done); > + name_off = metadata_off = 0; > + for ( i = 0; i < done; i++ ) > + { > + PyObject *info_dict, *metadata_list; > + char *name_str, *metadata_str; > + > + name_str = name + name_off; > + metadata_str = metadata + metadata_off; > + > + metadata_list = PyList_New(0); > + for ( char *s = metadata_str; s < metadata_str + metadata_len[i]; s += strlen(s) + 1 ) > + { > + PyObject *field = Py_BuildValue("s", s); > + if ( field == NULL ) > + { > + Py_DECREF(list); > + Py_DECREF(metadata_list); > + rc = EFAULT; > + goto error; > + } > + > + PyList_Append(metadata_list, field); > + Py_DECREF(field); > + } > + > + info_dict = Py_BuildValue( > + "{s:s,s:i,s:i,s:N}", > + "name", name_str, > + "state", info[i].state, > + "rc", info[i].rc, > + "metadata", metadata_list); > + > + if ( info_dict == NULL ) > + { > + Py_DECREF(list); > + Py_DECREF(metadata_list); > + rc = EFAULT; > + goto error; > + } > + PyList_SetItem(list, i, info_dict); > + Py_DECREF(info_dict); You can use PyList_SET_ITEM() to avoid the need for PyDECREF. Thanks,
> On 25. Sep 2019, at 18:47, Ross Lagerwall <ross.lagerwall@citrix.com> wrote: > > On 9/16/19 12:40 PM, Pawel Wieczorkiewicz wrote: >> Extend the XC python bindings library to support also all common >> livepatch operations and actions. >> Add the python bindings for the following operations: >> - status (pyxc_livepatch_status): >> Requires a payload name as an input. >> Returns a status dict containing a state string and a return code >> integer. >> - action (pyxc_livepatch_action): >> Requires a payload name and an action id as an input. Timeout and >> flags are optional parameters. >> Returns a return code integer. >> - upload (pyxc_livepatch_upload): >> Requires a payload name and a module's filename as an input. >> Returns a return code integer. >> - list (pyxc_livepatch_list): >> Takes no parameters. >> Returns a list of dicts containing each payload's: >> * name as a string >> * state as a string >> * return code as an integer >> * list of metadata key=value strings >> Each functions throws an exception error based on the errno value >> received from its corresponding libxc function call. >> Signed-off-by: Pawel Wieczorkiewicz <wipawel@amazon.de> >> Reviewed-by: Martin Mazein <amazein@amazon.de> >> Reviewed-by: Andra-Irina Paraschiv <andraprs@amazon.com> >> Reviewed-by: Leonard Foerster <foersleo@amazon.de> >> Reviewed-by: Norbert Manthey <nmanthey@amazon.de> >> Acked-by: Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com> > > This will be very useful, thanks! > >> --- >> Changed since v1: >> * changed PyList_Append() with PyList_SetItem() as requested by >> Marek >> tools/python/xen/lowlevel/xc/xc.c | 273 ++++++++++++++++++++++++++++++++++++++ >> 1 file changed, 273 insertions(+) > snip> +static PyObject *pyxc_livepatch_action(XcObject *self, >> + PyObject *args, >> + PyObject *kwds) >> +{ >> + int (*action_func)(xc_interface *xch, char *name, uint32_t timeout, uint64_t flags); >> + char *name; >> + unsigned int action; >> + uint32_t timeout; >> + uint64_t flags; >> + int rc; >> + >> + static char *kwd_list[] = { "name", "action", "timeout", "flags", NULL }; >> + >> + if ( !PyArg_ParseTupleAndKeywords(args, kwds, "sI|Ik", kwd_list, >> + &name, &action, &timeout, &flags) ) >> + goto error; >> + >> + switch (action) >> + { >> + case LIVEPATCH_ACTION_UNLOAD: >> + action_func = xc_livepatch_unload; >> + break; >> + case LIVEPATCH_ACTION_REVERT: >> + action_func = xc_livepatch_revert; >> + break; >> + case LIVEPATCH_ACTION_APPLY: >> + action_func = xc_livepatch_apply; >> + break; >> + case LIVEPATCH_ACTION_REPLACE: >> + action_func = xc_livepatch_replace; >> + break; >> + default: >> + goto error; >> + } >> + >> + rc = action_func(self->xc_handle, name, timeout, flags); >> + if ( rc ) >> + goto error; >> + >> + return Py_BuildValue("i", rc); > > For this and all the other functions which return zero on success, IMO returning None would be more Pythonic. > OK, will change. >> +error: >> + return pyxc_error_to_exception(self->xc_handle); >> +} >> + >> +static PyObject *pyxc_livepatch_upload(XcObject *self, >> + PyObject *args, >> + PyObject *kwds) >> +{ >> + unsigned char *fbuf = MAP_FAILED; >> + char *name, *filename; >> + struct stat buf; >> + int fd = 0, rc; >> + ssize_t len; >> + >> + static char *kwd_list[] = { "name", "filename", NULL }; >> + >> + if ( !PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwd_list, >> + &name, &filename)) >> + goto error; >> + >> + fd = open(filename, O_RDONLY); >> + if ( fd < 0 ) >> + goto error; >> + >> + if ( stat(filename, &buf) != 0 ) >> + goto error; > > I think it would be better to use fstat() to avoid a second path lookup potentially pointing to a different file. > Ah, certainly! Will fix. >> + >> + len = buf.st_size; >> + fbuf = mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0); >> + if ( fbuf == MAP_FAILED ) >> + goto error; >> + >> + rc = xc_livepatch_upload(self->xc_handle, name, fbuf, len); >> + if ( rc ) >> + goto error; >> + >> + if ( munmap(fbuf, len) ) >> + { >> + fbuf = MAP_FAILED; >> + goto error; >> + } >> + close(fd); >> + >> + return Py_BuildValue("i", rc);; > > Stray semicolon ACK > >> +error: >> + if ( fbuf != MAP_FAILED ) >> + munmap(fbuf, len); >> + if ( fd >= 0 ) >> + close(fd); > > You should probably save & restore errno so you can return the original error. > Yes, that’s right. Will fix. >> + return pyxc_error_to_exception(self->xc_handle); > > Maybe you can have a conditional return to avoid duplicating the munmap() & close()? E.g. > > return rc ? pyxc_error_to_exception(self->xc_handle) : … > Oh, this indeed can work. Let me apply that. Thanks. >> +} >> + >> +static PyObject *pyxc_livepatch_list(XcObject *self) >> +{ >> + PyObject *list; >> + unsigned int nr, done, left, i; >> + xen_livepatch_status_t *info = NULL; >> + char *name = NULL; >> + char *metadata = NULL; >> + uint32_t *len = NULL; >> + uint32_t *metadata_len = NULL; >> + uint64_t name_total_size, metadata_total_size; >> + off_t name_off, metadata_off; >> + int rc; >> + >> + rc = xc_livepatch_list_get_sizes(self->xc_handle, &nr, >> + &name_total_size, &metadata_total_size); >> + if ( rc ) >> + goto error; >> + >> + if ( nr == 0 ) >> + return PyList_New(0); >> + >> + rc = ENOMEM; >> + info = malloc(nr * sizeof(*info)); >> + if ( !info ) >> + goto error; >> + >> + name = malloc(name_total_size * sizeof(*name)); >> + if ( !name ) >> + goto error; >> + >> + len = malloc(nr * sizeof(*len)); >> + if ( !len ) >> + goto error; >> + >> + metadata = malloc(metadata_total_size * sizeof(*metadata)); >> + if ( !metadata ) >> + goto error; >> + >> + metadata_len = malloc(nr * sizeof(*metadata_len)); >> + if ( !metadata_len ) >> + goto error; >> + >> + rc = xc_livepatch_list(self->xc_handle, nr, 0, info, >> + name, len, name_total_size, >> + metadata, metadata_len, metadata_total_size, >> + &done, &left); >> + if ( rc ) >> + goto error; > > Should you also check done and left as is done in xen-livepatch.c? > > if ( rc || done != nr || left > 0) > Yes, I will add that. >> + >> + list = PyList_New(done); >> + name_off = metadata_off = 0; >> + for ( i = 0; i < done; i++ ) >> + { >> + PyObject *info_dict, *metadata_list; >> + char *name_str, *metadata_str; >> + >> + name_str = name + name_off; >> + metadata_str = metadata + metadata_off; >> + >> + metadata_list = PyList_New(0); >> + for ( char *s = metadata_str; s < metadata_str + metadata_len[i]; s += strlen(s) + 1 ) >> + { >> + PyObject *field = Py_BuildValue("s", s); >> + if ( field == NULL ) >> + { >> + Py_DECREF(list); >> + Py_DECREF(metadata_list); >> + rc = EFAULT; >> + goto error; >> + } >> + >> + PyList_Append(metadata_list, field); >> + Py_DECREF(field); >> + } >> + >> + info_dict = Py_BuildValue( >> + "{s:s,s:i,s:i,s:N}", >> + "name", name_str, >> + "state", info[i].state, >> + "rc", info[i].rc, >> + "metadata", metadata_list); >> + >> + if ( info_dict == NULL ) >> + { >> + Py_DECREF(list); >> + Py_DECREF(metadata_list); >> + rc = EFAULT; >> + goto error; >> + } >> + PyList_SetItem(list, i, info_dict); >> + Py_DECREF(info_dict); > > You can use PyList_SET_ITEM() to avoid the need for PyDECREF. OK, will do. > > Thanks, > -- > Ross Lagerwall Best Regards, Pawel Wieczorkiewicz Amazon Development Center Germany GmbH Krausenstr. 38 10117 Berlin Geschaeftsfuehrung: Christian Schlaeger, Ralf Herbrich Eingetragen am Amtsgericht Charlottenburg unter HRB 149173 B Sitz: Berlin Ust-ID: DE 289 237 879
diff --git a/tools/python/xen/lowlevel/xc/xc.c b/tools/python/xen/lowlevel/xc/xc.c index 9d53c4cf37..2a821e46d2 100644 --- a/tools/python/xen/lowlevel/xc/xc.c +++ b/tools/python/xen/lowlevel/xc/xc.c @@ -1979,6 +1979,230 @@ static PyObject *pyflask_access(PyObject *self, PyObject *args, return Py_BuildValue("i",ret); } +static PyObject *pyxc_livepatch_status(XcObject *self, + PyObject *args, + PyObject *kwds) +{ + xen_livepatch_status_t status; + PyObject *info_dict = NULL; + char *name; + int rc; + + static char *kwd_list[] = { "name", NULL }; + + if ( !PyArg_ParseTupleAndKeywords(args, kwds, "s", kwd_list, &name) ) + goto error; + + rc = xc_livepatch_get(self->xc_handle, name, &status); + if ( rc ) + goto error; + + info_dict = Py_BuildValue( + "{s:i,s:i}", + "state", status.state, + "rc", status.rc); + +error: + return info_dict ?: pyxc_error_to_exception(self->xc_handle); +} + +static PyObject *pyxc_livepatch_action(XcObject *self, + PyObject *args, + PyObject *kwds) +{ + int (*action_func)(xc_interface *xch, char *name, uint32_t timeout, uint64_t flags); + char *name; + unsigned int action; + uint32_t timeout; + uint64_t flags; + int rc; + + static char *kwd_list[] = { "name", "action", "timeout", "flags", NULL }; + + if ( !PyArg_ParseTupleAndKeywords(args, kwds, "sI|Ik", kwd_list, + &name, &action, &timeout, &flags) ) + goto error; + + switch (action) + { + case LIVEPATCH_ACTION_UNLOAD: + action_func = xc_livepatch_unload; + break; + case LIVEPATCH_ACTION_REVERT: + action_func = xc_livepatch_revert; + break; + case LIVEPATCH_ACTION_APPLY: + action_func = xc_livepatch_apply; + break; + case LIVEPATCH_ACTION_REPLACE: + action_func = xc_livepatch_replace; + break; + default: + goto error; + } + + rc = action_func(self->xc_handle, name, timeout, flags); + if ( rc ) + goto error; + + return Py_BuildValue("i", rc); +error: + return pyxc_error_to_exception(self->xc_handle); +} + +static PyObject *pyxc_livepatch_upload(XcObject *self, + PyObject *args, + PyObject *kwds) +{ + unsigned char *fbuf = MAP_FAILED; + char *name, *filename; + struct stat buf; + int fd = 0, rc; + ssize_t len; + + static char *kwd_list[] = { "name", "filename", NULL }; + + if ( !PyArg_ParseTupleAndKeywords(args, kwds, "ss", kwd_list, + &name, &filename)) + goto error; + + fd = open(filename, O_RDONLY); + if ( fd < 0 ) + goto error; + + if ( stat(filename, &buf) != 0 ) + goto error; + + len = buf.st_size; + fbuf = mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0); + if ( fbuf == MAP_FAILED ) + goto error; + + rc = xc_livepatch_upload(self->xc_handle, name, fbuf, len); + if ( rc ) + goto error; + + if ( munmap(fbuf, len) ) + { + fbuf = MAP_FAILED; + goto error; + } + close(fd); + + return Py_BuildValue("i", rc);; +error: + if ( fbuf != MAP_FAILED ) + munmap(fbuf, len); + if ( fd >= 0 ) + close(fd); + return pyxc_error_to_exception(self->xc_handle); +} + +static PyObject *pyxc_livepatch_list(XcObject *self) +{ + PyObject *list; + unsigned int nr, done, left, i; + xen_livepatch_status_t *info = NULL; + char *name = NULL; + char *metadata = NULL; + uint32_t *len = NULL; + uint32_t *metadata_len = NULL; + uint64_t name_total_size, metadata_total_size; + off_t name_off, metadata_off; + int rc; + + rc = xc_livepatch_list_get_sizes(self->xc_handle, &nr, + &name_total_size, &metadata_total_size); + if ( rc ) + goto error; + + if ( nr == 0 ) + return PyList_New(0); + + rc = ENOMEM; + info = malloc(nr * sizeof(*info)); + if ( !info ) + goto error; + + name = malloc(name_total_size * sizeof(*name)); + if ( !name ) + goto error; + + len = malloc(nr * sizeof(*len)); + if ( !len ) + goto error; + + metadata = malloc(metadata_total_size * sizeof(*metadata)); + if ( !metadata ) + goto error; + + metadata_len = malloc(nr * sizeof(*metadata_len)); + if ( !metadata_len ) + goto error; + + rc = xc_livepatch_list(self->xc_handle, nr, 0, info, + name, len, name_total_size, + metadata, metadata_len, metadata_total_size, + &done, &left); + if ( rc ) + goto error; + + list = PyList_New(done); + name_off = metadata_off = 0; + for ( i = 0; i < done; i++ ) + { + PyObject *info_dict, *metadata_list; + char *name_str, *metadata_str; + + name_str = name + name_off; + metadata_str = metadata + metadata_off; + + metadata_list = PyList_New(0); + for ( char *s = metadata_str; s < metadata_str + metadata_len[i]; s += strlen(s) + 1 ) + { + PyObject *field = Py_BuildValue("s", s); + if ( field == NULL ) + { + Py_DECREF(list); + Py_DECREF(metadata_list); + rc = EFAULT; + goto error; + } + + PyList_Append(metadata_list, field); + Py_DECREF(field); + } + + info_dict = Py_BuildValue( + "{s:s,s:i,s:i,s:N}", + "name", name_str, + "state", info[i].state, + "rc", info[i].rc, + "metadata", metadata_list); + + if ( info_dict == NULL ) + { + Py_DECREF(list); + Py_DECREF(metadata_list); + rc = EFAULT; + goto error; + } + PyList_SetItem(list, i, info_dict); + Py_DECREF(info_dict); + + name_off += len[i]; + metadata_off += metadata_len[i]; + } + +error: + free(info); + free(name); + free(len); + free(metadata); + free(metadata_len); + return rc ? pyxc_error_to_exception(self->xc_handle) : list; +} + static PyMethodDef pyxc_methods[] = { { "domain_create", (PyCFunction)pyxc_domain_create, @@ -2542,6 +2766,44 @@ static PyMethodDef pyxc_methods[] = { "Returns: [int]: 0 on all permission granted; -1 if any permissions are \ denied\n" }, + { "livepatch_status", + (PyCFunction)pyxc_livepatch_status, + METH_KEYWORDS, "\n" + "Gets current state and return code for a specified module.\n" + " name [str]: Module name to be used\n" + "Returns: [dict] on success; throwing an exception on error\n" + " state [int]: Module current state: CHECKED or APPLIED\n" + " rc [int]: Return code of last module's operation\n" }, + + { "livepatch_upload", + (PyCFunction)pyxc_livepatch_upload, + METH_KEYWORDS, "\n" + "Uploads a module with specified name from filename.\n" + " name [str]: Module name to be used\n" + " filename [str]: Filename of a module to be uploaded\n" + "Returns: [int] 0 on success; throwing an exception on error\n" }, + + { "livepatch_action", + (PyCFunction)pyxc_livepatch_action, + METH_KEYWORDS, "\n" + "Performs an action (unload, revert, apply or replace) on a specified \ + module.\n" + " name [str]: Module name to be used\n" + " action [uint]: Action enum id\n" + " timeout [uint]: Action scheduled execution timeout\n" + " flags [ulong]: Flags specifying action's extra parameters\n" + "Returns: [int] 0 on success; throwing an exception on error\n" }, + + { "livepatch_list", + (PyCFunction)pyxc_livepatch_list, + METH_NOARGS, "\n" + "List all uploaded livepatch modules with their current state and metadata.\n" + "Returns: [list of dicts] on success; throwing an exception on error\n" + " name [str]: Module name\n" + " state [int]: Module current state: CHECKED or APPLIED\n" + " rc [int]: Return code of last module's operation\n" + " metadata [list]: List of module's metadata 'key=value' strings\n" }, + { NULL, NULL, 0, NULL } }; @@ -2653,6 +2915,17 @@ PyMODINIT_FUNC initxc(void) PyModule_AddIntConstant(m, "XEN_SCHEDULER_CREDIT", XEN_SCHEDULER_CREDIT); PyModule_AddIntConstant(m, "XEN_SCHEDULER_CREDIT2", XEN_SCHEDULER_CREDIT2); + /* Expose livepatch constants to Python */ + PyModule_AddIntConstant(m, "LIVEPATCH_ACTION_UNLOAD", LIVEPATCH_ACTION_UNLOAD); + PyModule_AddIntConstant(m, "LIVEPATCH_ACTION_REVERT", LIVEPATCH_ACTION_REVERT); + PyModule_AddIntConstant(m, "LIVEPATCH_ACTION_APPLY", LIVEPATCH_ACTION_APPLY); + PyModule_AddIntConstant(m, "LIVEPATCH_ACTION_REPLACE", LIVEPATCH_ACTION_REPLACE); + + PyModule_AddIntConstant(m, "LIVEPATCH_ACTION_APPLY_NODEPS", LIVEPATCH_ACTION_APPLY_NODEPS); + + PyModule_AddIntConstant(m, "LIVEPATCH_STATE_APPLIED", LIVEPATCH_STATE_APPLIED); + PyModule_AddIntConstant(m, "LIVEPATCH_STATE_CHECKED", LIVEPATCH_STATE_CHECKED); + #if PY_MAJOR_VERSION >= 3 return m; #endif