yp-client API Examples

NotificationStack Class to Receive Notifications

The NotificationStack class is found in some of the applications, such as toaster. This class logs incoming NETCONF notifications from the server as text messages.

// This class will receive notifications for the session, through its operator()
// function. It also holds them and manages access to them from multiple
// threads, necessary because they're usually added from a background thread.
//
// To the outside world, this class will act like std::stack<xstring>.
class NotificationStack {
    public:
    NotificationStack(): mData(std::make_shared<data_t>()) { }

    // Functions to emulate std::stack<xstring>.
    bool empty() const { lock_t lock(mData->mutex); return mData->notes.empty(); }
    size_t size() const { lock_t lock(mData->mutex); return mData->notes.size(); }
    const xstring& top() const { lock_t lock(mData->mutex); return mData->notes.front(); }
    void push(const xstring &n) { lock_t lock(mData->mutex); mData->notes.push_front(n); }
    void pop() { lock_t lock(mData->mutex); mData->notes.pop_front(); }

    // This function blocks until a notification is received or the provided
    // wait time runs out.
    bool waitForNotification(const std::chrono::duration<clock_t> &maxWaitTime) {
        // Start waiting. The condition variable eliminates the need for
        // wasteful polling.
        lock_t lock(mData->mutex);
        return mData->waiter.wait_until(lock, clock_t::now() + maxWaitTime,
            [=]() { return !mData->notes.empty(); });
    }

    // This function turns objects created from this class into "functors" --
    // objects that act like a function, but can hold a non-global state as
    // well. We're exploiting that capability to create an object that we can
    // use as a notification function.
    bool operator()(const xstring &newNotification) {
        cout << "Received notification:" << endl << newNotification << endl
            << endl;

        // Must lock the mutex before all accesses to mData->notes, because this
        // function will usually be called from a background thread.
        {
            lock_t lock(mData->mutex);
            mData->notes.push_back(newNotification);
        }
        mData->waiter.notify_one();

        // We could return false here, if we were only looking for a particular
        // notification and had found it. That would remove this notification
        // function from the Session's list and prevent it from receiving any
        // later notifications. Since we want to continue receiving them, we'll
        // return true instead.
        return true;
    }

    private:
    typedef std::mutex mutex_t;
    typedef std::unique_lock<mutex_t> lock_t;
    typedef std::chrono::steady_clock clock_t;

    struct data_t {
        mutable mutex_t mutex;
        std::deque<xstring> notes;
        std::condition_variable waiter;
    };

    // Since we're using this class as a functor, it has to be copyable, and
    // neither std::mutex nor std::condition_variable is. This works around that
    // problem.
    std::shared_ptr<data_t> mData;
};

Convenience function: sendCommand

// A convenience function for sending a command and reporting its results.
static bool sendCommand(const xstring &command) {
    try {
        cout << " Sending: " << command << endl;

        Response response = session.command(command);
        if (response.error) {
            cout << "Received error:" << endl << response.result << endl << endl;
            return false;
        }

        if (response.result.empty())
            cout << "Received: OK" << endl << endl;
        else
            cout << "Received:" << endl << response.result << endl << endl;
        return true;
    } catch (StatusFailure &e) {
        cout << "StatusFailure exception: " << e.what() << endl << endl;
        return false;
    } catch (std::exception &e) {
        cout << "other exception: " << e.what() << endl << endl;
        return false;
    }
}

Convenience function: fetchPassword

// A function to request a password from the user, showing only asterisks on the
// screen.
xstring fetchPassword(const xstring &prompt, bool showAsterisk = true) {
    const char cReturn = 10, cBackspace = 127;

    std::cout << prompt << ' ';

    xstring r;
    while (true) {
        struct termios t_old;
        tcgetattr(STDIN_FILENO, &t_old);
        struct termios t_new = t_old;
        t_new.c_lflag &= ~(ICANON | ECHO);
        tcsetattr(STDIN_FILENO, TCSANOW, &t_new);
        xmlChar ch = getchar();
        tcsetattr(STDIN_FILENO, TCSANOW, &t_old);

        if (ch == cReturn) {
            break;
        } else if (ch == cBackspace) {
            if (r.length() != 0) {
                if (showAsterisk)
                    std::cout << "\b \b";
                r = r.substr(0, r.length() - 1);
            }
        } else {
            r += ch;
            if (showAsterisk)
                cout << '*';
        }
    }

    std::cout << std::endl;
    return r;
}

Useful Server Information and Configuration

Some yp-client API examples are shown here.

Get the Session Number

session.serverSessionId()

Get the Message Number

Response response = session.command(command);
xstring id = (response.internalRequestId &&
              !response.internalRequestId.empty() ?
              " (" + response.internalRequestId + ")" : "");

Set Display Mdmode to XML

api.displayMode(displaymode::xml) ;

With api.displayMode(displaymode::xml); then sending the command: sget /netconf-state/statistics generates a response similar to:

<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
    <data>
        <netconf-state xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring">
            <statistics>
                <netconf-start-time>2019-12-14T00:38:22Z</netconf-start-time>
                <in-bad-hellos>0</in-bad-hellos>
                <in-sessions>3</in-sessions>
                <dropped-sessions>0</dropped-sessions>
                <in-rpcs>16</in-rpcs>
                <in-bad-rpcs>0</in-bad-rpcs>
                <out-rpc-errors>0</out-rpc-errors>
                <out-notifications>5</out-notifications>
            </statistics>
        </netconf-state>
    </data>
</rpc-reply>

Set Display Mdmode to JSON

api.displayMode(displaymode::json) ;

With api.displayMode(displaymode::json); then sending the command: sget /netconf-state/statistics generates a response similar to:

{
    "yuma-netconf:rpc-reply": {
        "yuma-netconf:data": {
            "ietf-netconf-monitoring:netconf-state": {
                "statistics": {
                    "netconf-start-time":"2019-12-14T00:38:22Z",
                    "in-bad-hellos":0,
                    "in-sessions":4,
                    "dropped-sessions":0,
                    "in-rpcs":19,
                    "in-bad-rpcs":0,
                    "out-rpc-errors":0,
                    "out-notifications":5
                }
            }
        }
    }
}