root/usr/src/lib/pylibbe/common/libbe_py.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 * Copyright 2012 OmniTI Computer Consulting, Inc.  All rights reserved.
 * Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
 */

#include <Python.h>
#include <sys/varargs.h>
#include <stdio.h>
#include <libnvpair.h>

#include <libbe.h>
#include <libbe_priv.h>

enum {
        BE_PY_SUCCESS = 0,
        BE_PY_ERR_APPEND = 6000,
        BE_PY_ERR_DICT,
        BE_PY_ERR_LIST,
        BE_PY_ERR_NVLIST,
        BE_PY_ERR_PARSETUPLE,
        BE_PY_ERR_PRINT_ERR,
        BE_PY_ERR_VAR_CONV,
} bePyErr;

/*
 * public libbe functions
 */

PyObject *beCreateSnapshot(PyObject *, PyObject *);
PyObject *beCopy(PyObject *, PyObject *);
PyObject *beList(PyObject *, PyObject *, PyObject *);
PyObject *beActivate(PyObject *, PyObject *, PyObject *);
PyObject *beDestroy(PyObject *, PyObject *);
PyObject *beDestroySnapshot(PyObject *, PyObject *);
PyObject *beRename(PyObject *, PyObject *);
PyObject *beMount(PyObject *, PyObject *);
PyObject *beUnmount(PyObject *, PyObject *);
PyObject *bePrintErrors(PyObject *, PyObject *);
PyObject *beGetErrDesc(PyObject *, PyObject *);
char *beMapLibbePyErrorToString(int);

static boolean_t convertBEInfoToDictionary(be_node_list_t *be,
    PyObject **listDict);
static boolean_t convertDatasetInfoToDictionary(be_dataset_list_t *ds,
    PyObject **listDict);
static boolean_t convertSnapshotInfoToDictionary(be_snapshot_list_t *ss,
    PyObject **listDict);
static boolean_t convertPyArgsToNvlist(nvlist_t **nvList, int numArgs, ...);


/* ~~~~~~~~~~~~~~~ */
/* Public Funtions */
/* ~~~~~~~~~~~~~~~ */

/*
 * Function:    beCreateSnapshot
 * Description: Convert Python args to nvlist pairs and
 *              call libbe:be_create_snapshot to create a
 *              snapshot of all the datasets within a BE
 * Parameters:
 *   args -          pointer to a python object containing:
 *        beName -   The name of the BE to create a snapshot of
 *        snapName - The name of the snapshot to create (optional)
 *
 *        The following public attribute values. defined by libbe.h,
 *        are used by this function:
 *
 * Returns a pointer to a python object and an optional snapshot name:
 *      0, [snapName] - Success
 *      1, [snapName] - Failure
 * Scope:
 *      Public
 */
PyObject *
beCreateSnapshot(PyObject *self, PyObject *args)
{
        char    *beName = NULL;
        char    *snapName = NULL;
        int     ret = BE_PY_SUCCESS;
        nvlist_t        *beAttrs = NULL;
        PyObject        *retVals = NULL;

        if (!PyArg_ParseTuple(args, "z|z", &beName, &snapName)) {
                return (Py_BuildValue("[is]", BE_PY_ERR_PARSETUPLE, NULL));
        }

        if (!convertPyArgsToNvlist(&beAttrs, 4,
            BE_ATTR_ORIG_BE_NAME, beName,
            BE_ATTR_SNAP_NAME, snapName)) {
                nvlist_free(beAttrs);
                return (Py_BuildValue("[is]", BE_PY_ERR_NVLIST, NULL));
        }

        if (beAttrs == NULL) {
                return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
        }

        if ((ret = be_create_snapshot(beAttrs)) != 0) {
                nvlist_free(beAttrs);
                return (Py_BuildValue("[is]", ret, NULL));
        }
        if (snapName == NULL) {
                if (nvlist_lookup_pairs(beAttrs, NV_FLAG_NOENTOK,
                    BE_ATTR_SNAP_NAME, DATA_TYPE_STRING, &snapName,
                    NULL) != 0) {
                        nvlist_free(beAttrs);
                        return (Py_BuildValue("[is]",
                            BE_PY_ERR_NVLIST, NULL));
                }
                retVals = Py_BuildValue("[is]", ret, snapName);
                nvlist_free(beAttrs);
                return (retVals);
        }
        nvlist_free(beAttrs);

        return (Py_BuildValue("[is]", ret, NULL));
}

/*
 * Function:    beCopy
 * Description: Convert Python args to nvlist pairs and call libbe:be_copy
 *              to create a Boot Environment
 * Parameters:
 *   args -     pointer to a python object containing:
 *     trgtBeName - The name of the BE to create
 *     srcBeName - The name of the BE used to create trgtBeName (optional)
 *     rpool - The pool to create the new BE in (optional)
 *     srcSnapName - The snapshot name (optional)
 *     beNameProperties - The properties to use when creating
 *                        the BE (optional)
 *
 * Returns a pointer to a python object. That Python object will consist of
 * the return code and optional attributes, trgtBeName and snapshotName
 *      BE_SUCCESS, [trgtBeName], [trgtSnapName] - Success
 *      1, [trgtBeName], [trgtSnapName] - Failure
 * Scope:
 *      Public
 */
PyObject *
beCopy(PyObject *self, PyObject *args)
{
        char    *trgtBeName = NULL;
        char    *srcBeName = NULL;
        char    *srcSnapName = NULL;
        char    *trgtSnapName = NULL;
        char    *rpool = NULL;
        char    *beDescription = NULL;
        Py_ssize_t      pos = 0;
        int             ret = BE_PY_SUCCESS;
        nvlist_t        *beAttrs = NULL;
        nvlist_t        *beProps = NULL;
        PyObject        *beNameProperties = NULL;
        PyObject        *pkey = NULL;
        PyObject        *pvalue = NULL;
        PyObject        *retVals = NULL;

        if (!PyArg_ParseTuple(args, "|zzzzOz", &trgtBeName, &srcBeName,
            &srcSnapName, &rpool, &beNameProperties, &beDescription)) {
                return (Py_BuildValue("[iss]", BE_PY_ERR_PARSETUPLE,
                    NULL, NULL));
        }

        if (!convertPyArgsToNvlist(&beAttrs, 10,
            BE_ATTR_NEW_BE_NAME, trgtBeName,
            BE_ATTR_ORIG_BE_NAME, srcBeName,
            BE_ATTR_SNAP_NAME, srcSnapName,
            BE_ATTR_NEW_BE_POOL, rpool,
            BE_ATTR_NEW_BE_DESC, beDescription)) {
                nvlist_free(beAttrs);
                return (Py_BuildValue("[iss]", BE_PY_ERR_NVLIST, NULL, NULL));
        }

        if (beNameProperties != NULL) {
                if (nvlist_alloc(&beProps, NV_UNIQUE_NAME, 0) != 0) {
                        (void) printf("nvlist_alloc failed.\n");
                        nvlist_free(beAttrs);
                        return (Py_BuildValue("[iss]", BE_PY_ERR_NVLIST,
                            NULL, NULL));
                }
                while (PyDict_Next(beNameProperties, &pos, &pkey, &pvalue)) {
#if PY_MAJOR_VERSION >= 3
                        if (!convertPyArgsToNvlist(&beProps, 2,
                            PyUnicode_AsUTF8(pkey),
                            PyUnicode_AsUTF8(pvalue))) {
#else
                        if (!convertPyArgsToNvlist(&beProps, 2,
                            PyString_AsString(pkey),
                            PyString_AsString(pvalue))) {
#endif
                                nvlist_free(beProps);
                                nvlist_free(beAttrs);
                                return (Py_BuildValue("[iss]", BE_PY_ERR_NVLIST,
                                    NULL, NULL));
                        }
                }
        }

        if (beProps != NULL && beAttrs != NULL &&
            nvlist_add_nvlist(beAttrs, BE_ATTR_ZFS_PROPERTIES,
            beProps) != 0) {
                nvlist_free(beProps);
                nvlist_free(beAttrs);
                return (Py_BuildValue("[iss]", BE_PY_ERR_NVLIST,
                    NULL, NULL));
        }

        nvlist_free(beProps);

        if (trgtBeName == NULL) {
                /*
                 * Caller wants to get back the BE_ATTR_NEW_BE_NAME and
                 * BE_ATTR_SNAP_NAME
                 */
                if ((ret = be_copy(beAttrs)) != BE_SUCCESS) {
                        nvlist_free(beAttrs);
                        return (Py_BuildValue("[iss]", ret, NULL, NULL));
                }

                /*
                 * When no trgtBeName is passed to be_copy, be_copy
                 * returns an auto generated beName and snapshot name.
                 */
                if (nvlist_lookup_string(beAttrs, BE_ATTR_NEW_BE_NAME,
                    &trgtBeName) != 0) {
                        nvlist_free(beAttrs);
                        return (Py_BuildValue("[iss]", BE_PY_ERR_NVLIST,
                            NULL, NULL));
                }
                if (nvlist_lookup_string(beAttrs, BE_ATTR_SNAP_NAME,
                    &trgtSnapName) != 0) {
                        nvlist_free(beAttrs);
                        return (Py_BuildValue("[iss]", BE_PY_ERR_NVLIST,
                            NULL, NULL));
                }

                retVals = Py_BuildValue("[iss]", BE_PY_SUCCESS,
                    trgtBeName, trgtSnapName);
                nvlist_free(beAttrs);
                return (retVals);

        } else {
                ret = be_copy(beAttrs);
                nvlist_free(beAttrs);
                return (Py_BuildValue("[iss]", ret, NULL, NULL));
        }
}

/*
 * Function:    beList
 * Description: Convert Python args to nvlist pairs and call libbe:be_list
 *              to gather information about Boot Environments
 * Parameters:
 *   args -     pointer to a python object containing:
 *     bename  - The name of the BE to list (optional)
 *     nosnaps - boolean indicating whether to exclude snapshots (optional)
 *
 * Returns a pointer to a python object. That Python object will consist of
 * the return code and a list of Dicts or NULL.
 *      BE_PY_SUCCESS, listOfDicts - Success
 *      bePyErr or be_errno_t, NULL - Failure
 * Scope:
 *      Public
 */
PyObject *
beList(PyObject *self, PyObject *args, PyObject *keywds)
{
        char    *beName = NULL;
        int     noSnaps = 0;
        int     ret = BE_PY_SUCCESS;
        be_node_list_t *list = NULL;
        be_node_list_t *be = NULL;
        PyObject *dict = NULL;
        PyObject *listOfDicts = NULL;
        uint64_t listopts = BE_LIST_SNAPSHOTS;

        static char *kwlist[] = {"bename", "nosnaps", NULL};

        if ((listOfDicts = PyList_New(0)) == NULL) {
                ret = BE_PY_ERR_DICT;
                listOfDicts = Py_None;
                goto done;
        }

        if (!PyArg_ParseTupleAndKeywords(args, keywds, "|zi",
            kwlist, &beName, &noSnaps)) {
                ret = BE_PY_ERR_PARSETUPLE;
                goto done;
        }

        if (noSnaps)
                listopts &= ~BE_LIST_SNAPSHOTS;

        if ((ret = be_list(beName, &list, listopts)) != BE_SUCCESS) {
                goto done;
        }

        for (be = list; be != NULL; be = be->be_next_node) {
                be_dataset_list_t *ds = be->be_node_datasets;
                be_snapshot_list_t *ss = be->be_node_snapshots;

                if ((dict = PyDict_New()) == NULL) {
                        ret = BE_PY_ERR_DICT;
                        goto done;
                }

                if (!convertBEInfoToDictionary(be, &dict)) {
                        /* LINTED */
                        Py_DECREF(dict);
                        ret = BE_PY_ERR_VAR_CONV;
                        goto done;
                }

                if (PyList_Append(listOfDicts, dict) != 0) {
                        /* LINTED */
                        Py_DECREF(dict);
                        ret = BE_PY_ERR_APPEND;
                        goto done;
                }

                /* LINTED */
                Py_DECREF(dict);

                while (ds != NULL) {
                        if ((dict = PyDict_New()) == NULL) {
                                ret = BE_PY_ERR_DICT;
                                goto done;
                        }

                        if (!convertDatasetInfoToDictionary(ds, &dict)) {
                                /* LINTED */
                                Py_DECREF(dict);
                                ret = BE_PY_ERR_VAR_CONV;
                                goto done;
                        }

                        if (PyList_Append(listOfDicts, dict) != 0) {
                                /* LINTED */
                                Py_DECREF(dict);
                                ret = BE_PY_ERR_APPEND;
                                goto done;
                        }

                        ds = ds->be_next_dataset;

                        /* LINTED */
                        Py_DECREF(dict);
                }


                while (ss != NULL) {
                        if ((dict = PyDict_New()) == NULL) {
                                /* LINTED */
                                Py_DECREF(dict);
                                ret = BE_PY_ERR_DICT;
                                goto done;
                        }

                        if (!convertSnapshotInfoToDictionary(ss, &dict)) {
                                /* LINTED */
                                Py_DECREF(dict);
                                ret = BE_PY_ERR_VAR_CONV;
                                goto done;
                        }

                        if (PyList_Append(listOfDicts, dict) != 0) {
                                /* LINTED */
                                Py_DECREF(dict);
                                ret = BE_PY_ERR_APPEND;
                                goto done;
                        }

                        ss = ss->be_next_snapshot;

                        /* LINTED */
                        Py_DECREF(dict);
                }
        }

done:
        if (list != NULL)
                be_free_list(list);
        return (Py_BuildValue("[iO]", ret, listOfDicts));
}

/*
 * Function:    beActivate
 * Description: Convert Python args to nvlist pairs and call libbe:be_activate
 *              to activate a Boot Environment
 * Parameters:
 *   args -     pointer to a python object containing:
 *     bename    - The name of the BE to activate
 *     temporary - If True, perform a temporary BE activation
 *                 If False, remove a temporary BE activation
 *                 If not present, do nothing in regard to temporary activation
 *
 * Returns a pointer to a python object:
 *      BE_SUCCESS - Success
 *      bePyErr or be_errno_t - Failure
 * Scope:
 *      Public
 */
PyObject *
beActivate(PyObject *self, PyObject *args, PyObject *keywds)
{
        char            *beName = NULL;
        int             ret = BE_PY_SUCCESS;
        nvlist_t        *beAttrs = NULL;
        int             temp = -1;

        static char *kwlist[] = {"bename", "temporary", NULL};

        if (!PyArg_ParseTupleAndKeywords(args, keywds, "z|i",
            kwlist, &beName, &temp)) {
                ret = BE_PY_ERR_PARSETUPLE;
                goto done;
        }

        if (!convertPyArgsToNvlist(&beAttrs, 2, BE_ATTR_ORIG_BE_NAME, beName) ||
            beAttrs == NULL) {
                ret = BE_PY_ERR_NVLIST;
                goto done;
        }

        if (temp != -1 && nvlist_add_boolean_value(beAttrs,
            BE_ATTR_ACTIVE_NEXTBOOT, temp == 0 ? B_FALSE : B_TRUE) != 0) {
                ret = BE_PY_ERR_NVLIST;
                goto done;
        }

        ret = be_activate(beAttrs);
done:
        nvlist_free(beAttrs);
        return (Py_BuildValue("i", ret));
}

/*
 * Function:    beDestroy
 * Description: Convert Python args to nvlist pairs and call libbe:be_destroy
 *              to destroy a Boot Environment
 * Parameters:
 *   args -     pointer to a python object containing:
 *     beName - The name of the BE to destroy
 *
 * Returns a pointer to a python object:
 *      BE_SUCCESS - Success
 *      bePyErr or be_errno_t - Failure
 * Scope:
 *      Public
 */
PyObject *
beDestroy(PyObject *self, PyObject *args)
{
        char            *beName = NULL;
        int             destroy_snaps = 0;
        int             force_unmount = 0;
        int             destroy_flags = 0;
        int             ret = BE_PY_SUCCESS;
        nvlist_t        *beAttrs = NULL;

        if (!PyArg_ParseTuple(args, "z|ii", &beName, &destroy_snaps,
            &force_unmount)) {
                return (Py_BuildValue("i", BE_PY_ERR_PARSETUPLE));
        }

        if (destroy_snaps == 1)
                destroy_flags |= BE_DESTROY_FLAG_SNAPSHOTS;

        if (force_unmount == 1)
                destroy_flags |= BE_DESTROY_FLAG_FORCE_UNMOUNT;

        if (!convertPyArgsToNvlist(&beAttrs, 2, BE_ATTR_ORIG_BE_NAME, beName)) {
                nvlist_free(beAttrs);
                return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
        }

        if (nvlist_add_uint16(beAttrs, BE_ATTR_DESTROY_FLAGS, destroy_flags)
            != 0) {
                (void) printf("nvlist_add_uint16 failed for "
                    "BE_ATTR_DESTROY_FLAGS (%d).\n", destroy_flags);
                nvlist_free(beAttrs);
                return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
        }

        if (beAttrs == NULL) {
                return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
        }

        ret = be_destroy(beAttrs);
        nvlist_free(beAttrs);
        return (Py_BuildValue("i", ret));
}

/*
 * Function:    beDestroySnapshot
 * Description: Convert Python args to nvlist pairs and call libbe:be_destroy
 *              to destroy a snapshot of a Boot Environment
 * Parameters:
 *   args -     pointer to a python object containing:
 *     beName - The name of the BE to destroy
 *     snapName - The name of the snapshot to destroy
 *
 * Returns a pointer to a python object:
 *      BE_SUCCESS - Success
 *      bePyErr or be_errno_t - Failure
 * Scope:
 *      Public
 */
PyObject *
beDestroySnapshot(PyObject *self, PyObject *args)
{
        char            *beName = NULL;
        char            *snapName = NULL;
        int             ret = BE_PY_SUCCESS;
        nvlist_t        *beAttrs = NULL;

        if (!PyArg_ParseTuple(args, "zz", &beName, &snapName)) {
                return (Py_BuildValue("i", BE_PY_ERR_PARSETUPLE));
        }

        if (!convertPyArgsToNvlist(&beAttrs, 4,
            BE_ATTR_ORIG_BE_NAME, beName,
            BE_ATTR_SNAP_NAME, snapName)) {
                nvlist_free(beAttrs);
                return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
        }

        if (beAttrs == NULL) {
                return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
        }

        ret = be_destroy_snapshot(beAttrs);
        nvlist_free(beAttrs);
        return (Py_BuildValue("i", ret));
}

/*
 * Function:    beRename
 * Description: Convert Python args to nvlist pairs and call libbe:be_rename
 *              to rename a Boot Environment
 * Parameters:
 *   args -     pointer to a python object containing:
 *     oldBeName - The name of the old Boot Environment
 *     newBeName - The name of the new Boot Environment
 *
 * Returns a pointer to a python object:
 *      BE_SUCCESS - Success
 *      bePyErr or be_errno_t - Failure
 * Scope:
 *      Public
 */
PyObject *
beRename(PyObject *self, PyObject *args)
{
        char            *oldBeName = NULL;
        char            *newBeName = NULL;
        int             ret = BE_PY_SUCCESS;
        nvlist_t        *beAttrs = NULL;

        if (!PyArg_ParseTuple(args, "zz", &oldBeName, &newBeName)) {
                return (Py_BuildValue("i", BE_PY_ERR_PARSETUPLE));
        }

        if (!convertPyArgsToNvlist(&beAttrs, 4,
            BE_ATTR_ORIG_BE_NAME, oldBeName,
            BE_ATTR_NEW_BE_NAME, newBeName)) {
                nvlist_free(beAttrs);
                return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
        }

        if (beAttrs == NULL) {
                return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
        }

        ret = be_rename(beAttrs);
        nvlist_free(beAttrs);
        return (Py_BuildValue("i", ret));
}

/*
 * Function:    beMount
 * Description: Convert Python args to nvlist pairs and call libbe:be_mount
 *              to mount a Boot Environment
 * Parameters:
 *   args -     pointer to a python object containing:
 *     beName - The name of the Boot Environment to mount
 *     mountpoint - The path of the mountpoint to mount the
 *                  Boot Environment on (optional)
 *
 * Returns a pointer to a python object:
 *      BE_SUCCESS - Success
 *      bePyErr or be_errno_t - Failure
 * Scope:
 *      Public
 */
PyObject *
beMount(PyObject *self, PyObject *args)
{
        char            *beName = NULL;
        char            *mountpoint = NULL;
        int             ret = BE_PY_SUCCESS;
        nvlist_t        *beAttrs = NULL;

        if (!PyArg_ParseTuple(args, "zz", &beName, &mountpoint)) {
                return (Py_BuildValue("i", BE_PY_ERR_PARSETUPLE));
        }

        if (!convertPyArgsToNvlist(&beAttrs, 4,
            BE_ATTR_ORIG_BE_NAME, beName,
            BE_ATTR_MOUNTPOINT, mountpoint)) {
                nvlist_free(beAttrs);
                return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
        }

        if (beAttrs == NULL) {
                return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
        }

        ret = be_mount(beAttrs);
        nvlist_free(beAttrs);
        return (Py_BuildValue("i", ret));
}

/*
 * Function:    beUnmount
 * Description: Convert Python args to nvlist pairs and call libbe:be_unmount
 *              to unmount a Boot Environment
 * Parameters:
 *   args -     pointer to a python object containing:
 *     beName - The name of the Boot Environment to unmount
 *
 * Returns a pointer to a python object:
 *      BE_SUCCESS - Success
 *      bePyErr or be_errno_t - Failure
 * Scope:
 *      Public
 */
PyObject *
beUnmount(PyObject *self, PyObject *args)
{
        char            *beName = NULL;
        int             force_unmount = 0;
        int             unmount_flags = 0;
        int             ret = BE_PY_SUCCESS;
        nvlist_t        *beAttrs = NULL;

        if (!PyArg_ParseTuple(args, "z|i", &beName, &force_unmount)) {
                return (Py_BuildValue("i", BE_PY_ERR_PARSETUPLE));
        }

        if (force_unmount == 1)
                unmount_flags |= BE_UNMOUNT_FLAG_FORCE;

        if (!convertPyArgsToNvlist(&beAttrs, 2,
            BE_ATTR_ORIG_BE_NAME, beName)) {
                nvlist_free(beAttrs);
                return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
        }

        if (nvlist_add_uint16(beAttrs, BE_ATTR_UNMOUNT_FLAGS, unmount_flags)
            != 0) {
                (void) printf("nvlist_add_uint16 failed for "
                    "BE_ATTR_UNMOUNT_FLAGS (%d).\n", unmount_flags);
                nvlist_free(beAttrs);
                return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
        }

        if (beAttrs == NULL) {
                return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
        }

        ret = be_unmount(beAttrs);
        nvlist_free(beAttrs);
        return (Py_BuildValue("i", ret));
}

/*
 * Function:    beRollback
 * Description: Convert Python args to nvlist pairs and call libbe:be_rollback
 *              to rollback a Boot Environment to a previously taken
 *               snapshot.
 * Parameters:
 *   args -     pointer to a python object containing:
 *     beName - The name of the Boot Environment to unmount
 *
 * Returns a pointer to a python object:
 *      BE_SUCCESS - Success
 *      bePyErr or be_errno_t - Failure
 * Scope:
 *      Public
 */
PyObject *
beRollback(PyObject *self, PyObject *args)
{
        char            *beName = NULL;
        char            *snapName = NULL;
        int             ret = BE_PY_SUCCESS;
        nvlist_t        *beAttrs = NULL;

        if (!PyArg_ParseTuple(args, "zz", &beName, &snapName)) {
                return (Py_BuildValue("i", BE_PY_ERR_PARSETUPLE));
        }

        if (!convertPyArgsToNvlist(&beAttrs, 4,
            BE_ATTR_ORIG_BE_NAME, beName,
            BE_ATTR_SNAP_NAME, snapName)) {
                nvlist_free(beAttrs);
                return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
        }

        if (beAttrs == NULL) {
                return (Py_BuildValue("i", BE_PY_ERR_NVLIST));
        }

        ret = be_rollback(beAttrs);
        nvlist_free(beAttrs);
        return (Py_BuildValue("i", ret));
}

/*
 * Function:    bePrintErrors
 * Description: Convert Python args to boolean and call libbe_print_errors to
 *                      turn on/off error output for the library.
 * Parameter:
 *   args -     pointer to a python object containing:
 *              print_errors - Boolean that turns library error
 *                             printing on or off.
 * Parameters:
 *   args -     pointer to a python object containing:
 *     0 - do not print errors - Python boolean "False"
 *     1 - print errors - Python boolean "True"
 *
 * Returns 1 on missing or invalid argument, 0 otherwise
 * Scope:
 *      Public
 */
PyObject *
bePrintErrors(PyObject *self, PyObject *args)
{
        int             print_errors;

        if (!PyArg_ParseTuple(args, "i", &print_errors) ||
            (print_errors != 1 && print_errors != 0))
                return (Py_BuildValue("i", BE_PY_ERR_PRINT_ERR));
        libbe_print_errors(print_errors == 1);
        return (Py_BuildValue("i", BE_PY_SUCCESS));
}

/*
 * Function:    beGetErrDesc
 * Description: Convert Python args to an int and call be_err_to_str to
 *                      map an error code to an error string.
 * Parameter:
 *   args -     pointer to a python object containing:
 *              errCode - value to map to an error string.
 *
 * Returns: error string or NULL
 * Scope:
 *      Public
 */
PyObject *
beGetErrDesc(PyObject *self, PyObject *args)
{
        int     errCode = 0;
        char    *beErrStr = NULL;

        if (!PyArg_ParseTuple(args, "i", &errCode)) {
                return (Py_BuildValue("s", NULL));
        }

        /*
         * First check libbe_py errors. If NULL is returned check error codes
         * in libbe.
         */

        if ((beErrStr = beMapLibbePyErrorToString(errCode)) == NULL) {
                beErrStr = be_err_to_str(errCode);
        }

        return (Py_BuildValue("s", beErrStr));
}

/*
 * Function:    beVerifyBEName
 * Description: Call be_valid_be_name() to verify the BE name.
 * Parameter:
 *   args -     pointer to a python object containing:
 *              string - value to map to a string.
 *
 * Returns:  0 for success or 1 for failure
 * Scope:
 *      Public
 */
PyObject *
beVerifyBEName(PyObject *self, PyObject *args)
{
        char    *string = NULL;

        if (!PyArg_ParseTuple(args, "s", &string)) {
                return (Py_BuildValue("i", 1));
        }

        if (be_valid_be_name(string)) {
                return (Py_BuildValue("i", 0));
        } else {
                return (Py_BuildValue("i", 1));
        }
}

/* ~~~~~~~~~~~~~~~~~ */
/* Private Functions */
/* ~~~~~~~~~~~~~~~~~ */

static boolean_t
convertBEInfoToDictionary(be_node_list_t *be, PyObject **listDict)
{
        if (be->be_node_name != NULL) {
                if (PyDict_SetItemString(*listDict, BE_ATTR_ORIG_BE_NAME,
                    PyUnicode_FromString(be->be_node_name)) != 0) {
                        return (B_FALSE);
                }
        }

        if (be->be_rpool != NULL) {
                if (PyDict_SetItemString(*listDict, BE_ATTR_ORIG_BE_POOL,
                    PyUnicode_FromString(be->be_rpool)) != 0) {
                        return (B_FALSE);
                }
        }

        if (be->be_mntpt != NULL) {
                if (PyDict_SetItemString(*listDict, BE_ATTR_MOUNTPOINT,
                    PyUnicode_FromString(be->be_mntpt)) != 0) {
                        return (B_FALSE);
                }
        }

        if (PyDict_SetItemString(*listDict, BE_ATTR_MOUNTED,
            (be->be_mounted ? Py_True : Py_False)) != 0) {
                return (B_FALSE);
        }

        if (PyDict_SetItemString(*listDict, BE_ATTR_ACTIVE,
            (be->be_active ? Py_True : Py_False)) != 0) {
                return (B_FALSE);
        }

        if (PyDict_SetItemString(*listDict, BE_ATTR_ACTIVE_ON_BOOT,
            (be->be_active_on_boot ? Py_True : Py_False)) != 0) {
                return (B_FALSE);
        }

        if (PyDict_SetItemString(*listDict, BE_ATTR_ACTIVE_NEXTBOOT,
            (be->be_active_next ? Py_True : Py_False)) != 0) {
                return (B_FALSE);
        }

        if (PyDict_SetItemString(*listDict, BE_ATTR_GLOBAL_ACTIVE,
            (be->be_global_active ? Py_True : Py_False)) != 0) {
                return (B_FALSE);
        }

        if (be->be_space_used != 0) {
                if (PyDict_SetItemString(*listDict, BE_ATTR_SPACE,
                    PyLong_FromUnsignedLongLong(be->be_space_used)) != 0) {
                        return (B_FALSE);
                }
        }

        if (be->be_root_ds != NULL) {
                if (PyDict_SetItemString(*listDict, BE_ATTR_ROOT_DS,
                    PyUnicode_FromString(be->be_root_ds)) != 0) {
                        return (B_FALSE);
                }
        }

        if (be->be_node_creation != 0) {
                if (PyDict_SetItemString(*listDict, BE_ATTR_DATE,
                    PyLong_FromLong(be->be_node_creation)) != 0) {
                        return (B_FALSE);
                }
        }

        if (be->be_policy_type != NULL) {
                if (PyDict_SetItemString(*listDict, BE_ATTR_POLICY,
                    PyUnicode_FromString(be->be_policy_type)) != 0) {
                        return (B_FALSE);
                }
        }

        if (be->be_uuid_str != NULL) {
                if (PyDict_SetItemString(*listDict, BE_ATTR_UUID_STR,
                    PyUnicode_FromString(be->be_uuid_str)) != 0) {
                        return (B_FALSE);
                }
        }

        return (B_TRUE);
}

static boolean_t
convertDatasetInfoToDictionary(be_dataset_list_t *ds, PyObject **listDict)
{
        if (ds->be_dataset_name != NULL) {
                if (PyDict_SetItemString(*listDict, BE_ATTR_DATASET,
                    PyUnicode_FromString(ds->be_dataset_name)) != 0) {
                        return (B_FALSE);
                }
        }

        if (PyDict_SetItemString(*listDict, BE_ATTR_STATUS,
            (ds->be_ds_mounted ? Py_True : Py_False)) != 0) {
                        return (B_FALSE);
        }

        if (ds->be_ds_mntpt != NULL) {
                if (PyDict_SetItemString(*listDict, BE_ATTR_MOUNTPOINT,
                    PyUnicode_FromString(ds->be_ds_mntpt)) != 0) {
                        return (B_FALSE);
                }
        }

        if (PyDict_SetItemString(*listDict, BE_ATTR_MOUNTED,
            (ds->be_ds_mounted ? Py_True : Py_False)) != 0) {
                return (B_FALSE);
        }

        if (ds->be_ds_space_used != 0) {
                if (PyDict_SetItemString(*listDict, BE_ATTR_SPACE,
                    PyLong_FromUnsignedLongLong(ds->be_ds_space_used))
                    != 0) {
                        return (B_FALSE);
                }
        }

        if (ds->be_dataset_name != 0) {
                if (PyDict_SetItemString(*listDict, BE_ATTR_DATASET,
                    PyUnicode_FromString(ds->be_dataset_name)) != 0) {
                        return (B_FALSE);
                }
        }

        if (ds->be_ds_plcy_type != NULL) {
                if (PyDict_SetItemString(*listDict, BE_ATTR_POLICY,
                    PyUnicode_FromString(ds->be_ds_plcy_type)) != 0) {
                        return (B_FALSE);
                }
        }

        if (ds->be_ds_creation != 0) {
                if (PyDict_SetItemString(*listDict, BE_ATTR_DATE,
                    PyLong_FromLong(ds->be_ds_creation)) != 0) {
                        return (B_FALSE);
                }
        }

        return (B_TRUE);
}

static boolean_t
convertSnapshotInfoToDictionary(be_snapshot_list_t *ss, PyObject **listDict)
{
        if (ss->be_snapshot_name != NULL) {
                if (PyDict_SetItemString(*listDict, BE_ATTR_SNAP_NAME,
                    PyUnicode_FromString(ss->be_snapshot_name)) != 0) {
                        return (B_FALSE);
                }
        }

        if (ss->be_snapshot_creation != 0) {
                if (PyDict_SetItemString(*listDict, BE_ATTR_DATE,
                    PyLong_FromLong(ss->be_snapshot_creation)) != 0) {
                        return (B_FALSE);
                }
        }

        if (ss->be_snapshot_type != NULL) {
                if (PyDict_SetItemString(*listDict, BE_ATTR_POLICY,
                    PyUnicode_FromString(ss->be_snapshot_type)) != 0) {
                        return (B_FALSE);
                }
        }

        if (ss->be_snapshot_space_used != 0) {
                if (PyDict_SetItemString(*listDict, BE_ATTR_SPACE,
                    PyLong_FromUnsignedLongLong(ss->be_snapshot_space_used))
                    != 0) {
                        return (B_FALSE);
                }
        }

        return (B_TRUE);
}

/*
 * Convert string arguments to nvlist attributes
 */

static boolean_t
convertPyArgsToNvlist(nvlist_t **nvList, int numArgs, ...)
{
        char *pt, *pt2;
        va_list ap;
        int i;

        if (*nvList == NULL) {
                if (nvlist_alloc(nvList, NV_UNIQUE_NAME, 0) != 0) {
                        (void) printf("nvlist_alloc failed.\n");
                        return (B_FALSE);
                }
        }

        va_start(ap, numArgs);

        for (i = 0; i < numArgs; i += 2) {
                if ((pt = va_arg(ap, char *)) == NULL ||
                    (pt2 = va_arg(ap, char *)) == NULL) {
                        continue;
                }
                if (nvlist_add_string(*nvList, pt, pt2) != 0) {
                        (void) printf("nvlist_add_string failed for %s (%s).\n",
                            pt, pt2);
                        nvlist_free(*nvList);
                        *nvList = NULL;
                        return (B_FALSE);
                }
        }

        va_end(ap);

        return (B_TRUE);
}

/*
 * Function:    beMapLibbePyErrorToString
 * Description: Convert Python args to an int and map an error code to an
 *                      error string.
 * Parameter:
 *              errCode - value to map to an error string.
 *
 * Returns error string or NULL
 * Scope:
 *      Public
 */

char *
beMapLibbePyErrorToString(int errCode)
{
        switch (errCode) {
        case BE_PY_ERR_APPEND:
                return ("Unable to append a dictionary to a list "
                    "of dictionaries.");
        case BE_PY_ERR_DICT:
                return ("Creation of a Python dictionary failed.");
        case BE_PY_ERR_LIST:
                return ("beList() failed.");
        case BE_PY_ERR_NVLIST:
                return ("An nvlist operation failed.");
        case BE_PY_ERR_PARSETUPLE:
                return ("PyArg_ParseTuple() failed to convert variable to C.");
        case BE_PY_ERR_PRINT_ERR:
                return ("bePrintErrors() failed.");
        case BE_PY_ERR_VAR_CONV:
                return ("Unable to add variables to a Python dictionary.");
        default:
                return (NULL);
        }
}

/* Private python initialization structure */

static struct PyMethodDef libbeMethods[] = {
        {"beCopy", beCopy, METH_VARARGS, "Create/Copy a BE."},
        {"beCreateSnapshot", beCreateSnapshot, METH_VARARGS,
            "Create a snapshot."},
        {"beDestroy", beDestroy, METH_VARARGS, "Destroy a BE."},
        {"beDestroySnapshot", beDestroySnapshot, METH_VARARGS,
            "Destroy a snapshot."},
        {"beMount", beMount, METH_VARARGS, "Mount a BE."},
        {"beUnmount", beUnmount, METH_VARARGS, "Unmount a BE."},
        {"beList", (PyCFunction)(uintptr_t)beList, METH_VARARGS | METH_KEYWORDS,
            "List BE info."},
        {"beRename", beRename, METH_VARARGS, "Rename a BE."},
        {"beActivate", (PyCFunction)(uintptr_t)beActivate,
            METH_VARARGS | METH_KEYWORDS, "Activate a BE."},
        {"beRollback", beRollback, METH_VARARGS, "Rollback a BE."},
        {"bePrintErrors", bePrintErrors, METH_VARARGS,
            "Enable/disable error printing."},
        {"beGetErrDesc", beGetErrDesc, METH_VARARGS,
            "Map Error codes to strings."},
        {"beVerifyBEName", beVerifyBEName, METH_VARARGS,
            "Verify BE name."},
        {NULL, NULL, 0, NULL}
};

#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef libbe_module = {
        PyModuleDef_HEAD_INIT,
        "libbe_py",
        NULL,
        -1,
        libbeMethods
};
#endif

static PyObject *
moduleinit()
{
        /* PyMODINIT_FUNC; */
#if PY_MAJOR_VERSION >= 3
        return (PyModule_Create(&libbe_module));
#else
        /*
         * Python2 module initialisation functions are void and may not return
         * a value. However, they will set an exception if appropriate.
         */
        (void) Py_InitModule("libbe_py", libbeMethods);
        return (NULL);
#endif
}

#if PY_MAJOR_VERSION >= 3
PyMODINIT_FUNC
PyInit_libbe_py(void)
{
        return (moduleinit());
}
#else
PyMODINIT_FUNC
initlibbe_py(void)
{
        (void) moduleinit();
}
#endif