Skip to content

Commit

Permalink
Merge branch 'feature/implement_table_support' into development
Browse files Browse the repository at this point in the history
  • Loading branch information
tanjeff committed Nov 30, 2015
2 parents 058a334 + 68d19bb commit 5d86eea
Show file tree
Hide file tree
Showing 81 changed files with 4,588 additions and 2,397 deletions.
4 changes: 2 additions & 2 deletions doc/api.doxyfile
Expand Up @@ -223,7 +223,7 @@ TAB_SIZE = 8
# will result in a user-defined paragraph with heading "Side Effects:".
# You can put \n's in the value part of an alias to insert newlines.

ALIASES =
ALIASES = agentxcpp{1}="\ref agentxcpp::\1 \"\1\""

# This tag can be used to specify a number of word-keyword mappings (TCL only).
# A mapping has the form "name=value". For example adding
Expand Down Expand Up @@ -899,7 +899,7 @@ VERBATIM_HEADERS = NO
# of all compounds will be generated. Enable this if the project
# contains a lot of classes, structs, unions or interfaces.

ALPHABETICAL_INDEX = NO
ALPHABETICAL_INDEX = YES

# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
Expand Down
30 changes: 16 additions & 14 deletions doc/documentation.dox
Expand Up @@ -19,7 +19,7 @@

/*!

\internal
\cond INTERNAL

\page documentation About the Documentation

Expand All @@ -30,31 +30,33 @@ documentation while the latter is used for the internals documentation.

The source of documentation is (nearly) identical for both documentations.
Classes, members, functions, etc. are documented directly in the C++ source
files, using the <tt>\\internal</tt> keyword mark internal-only content. The
<tt>doc/</tt> directory contains the mainpages (<tt>api.mainpage</tt> and
<tt>%internals.mainpage</tt>), which are different for the API and the
files, using the <tt>\\internal</tt> keyword or a pair of <tt>\\cond
INTERNAL</tt> and <tt>\\endcond</tt> to mark internal-only content.
Documentation blocks marked in this way are included only in the internals
documentation, because the <tt>INTERNAL_DOCS</tt> configuration item is set to
<tt>YES</tt> for the internals documentation, but to <tt>NO</tt> for the API
documentation, and the <tt>ENABLED_SECTIONS</tt> configuration item includes
the section name "INTERNAL" for the internals documentation (hence
<tt>\\cond</tt> can test for "INTERNAL").

The <tt>doc/</tt> directory also contains the mainpages (<tt>api.mainpage</tt>
and <tt>%internals.mainpage</tt>), which are different for the API and the
internals documentation. The <tt>*.dox</tt> files provide subpages explaining
the concepts used in the library (e.g. the source of this page is
<tt>doc/documentation.dox</tt>).

Documentation blocks marked with the <tt>\\internal</tt> keyword are included
only in the internals documentation (the <tt>INTERNAL_DOCS</tt> configuration
item is set to <tt>YES</tt> for the internals documentation, but to <tt>NO</tt>
for the API documentation). The API documentation contains only documentation
blocks which are not marked with <tt>\\internal</tt>. This is also true for the
codeblocks in <tt>*.dox</tt> files (e.g. this page is not included in the API
documentation).
<tt>%doc/documentation.dox</tt>).

If you are developing for AgentXcpp, consider the following conventions:
- Use <tt>\\internal</tt> to mark documentation blocks which should
<em>not</em> appear in the API documentation. If only a part of the
documentation block should be excluded, surround the part to be excluded with
<tt>\\internal</tt> and <tt>\\endinternal</tt>.
<tt>\\internal</tt> and <tt>\\endinternal</tt>. You may also use <tt>\\cond
INTERNAL</tt> and <tt>\\endcond</tt>.
- Document each and every item, such as classes, class members, functions,
global variables, etc.
- Include a <tt>\\brief</tt> command for every documented item.
- Include a subpage (i.e. a <tt>*.dox</tt> file) for each abstract concept.
Add links to that pages to the appropriate mainpage(s).

\endcond

*/
1 change: 1 addition & 0 deletions doc/getting_started.dox
Expand Up @@ -29,6 +29,7 @@ sections:
-# \subpage how_to_add_rw_support
"Learn how to implement writeable SNMP variables."
-# \subpage how_to_send_notifications "Learn how to send traps and notifications with agentXcpp."
-# \subpage how_to_implement_tables "Learn how to implement SNMP tables with agentXcpp."

Finally, start writing your own subagents. Happy hacking!

Expand Down
132 changes: 61 additions & 71 deletions doc/how_to_add_rw_support.dox
Expand Up @@ -7,7 +7,7 @@
* it under the terms of the AgentXcpp library license, version 1, which
* consists of the GNU General Public License and some additional
* permissions.
*
snmpget -v2c -c rw localhost SIMPLE-MIB::simpleCounter.0 *
* AgentXcpp is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Expand Down Expand Up @@ -56,146 +56,137 @@ This is the same as before, but with "ACCESS read-write".
A single SNMP Set request may set multiple variables at once (possibly
distributed over multiple subagents), and SNMP guarantees an atomic operation
in that case. This means that, should any variable fail to perform the Set
operation, no other variable is allowed to perform the set operation either.
Therefore, the Set operation is carried out in multiple distinct steps:
operation, no other variable is allowed to perform the Set operation either.
Therefore, the SET operation is carried out in multiple distinct steps:

-# \b TestSet checks whether the variable could be set at all. This step is
performed for all variables (in all subagents) affected by the single SNMP
performed for all variables (in all subagents) affected by the single SNMP Set
request. If any variable is not able to accept its new value, the Set
operation is aborted (and no variable gets updated). During this step
subagents shall allocate the resources they need to carry out the Set
operation later.
operation is aborted, and the next step "CommitSet" is skipped (and no
variable gets updated). During this step subagents shall allocate the
resources they need to carry out the Set operation later, but they shall not
update their value yet.
-# \b CommitSet actually sets the new value. This step may fail for a variable,
even if the TestSet step announced that the CommitSet would work. If that
even if the TestSet step announced that the CommitSet would work. If that
happens, all other variables which already performed the CommitSet must undo
their action. It is strongly recommended to implement variables in such a
way that CommitSet does not fail, when possible.
their action (which is done in the UndoSet step). It is strongly recommended
to implement variables in such a way that CommitSet does not fail, when
possible.
-# The third step is one of the following:
- \b CleanupSet is usually the last step, performed after CommitSet was
successful for all variables. In the CleanupSet step, the subagent shall
release any resources which where allocated in the TestSet step.
- \b UndoSet is performed if CommitSet failed for any variable. UndoSet is
then called for all variables which already performed CommitSet and
- \b UndoSet may be performed if CommitSet failed for any variable. UndoSet
is then called for all variables which already performed CommitSet and
therefore must undo their action. For variables which didn't perform
CommitSet yet, CleanupSet is called instead. Like CleanupSet, UndoSet
shall also releases all resources which were allocated in the TestSet
step.
CommitSet yet, CleanupSet is called instead. UndoSet shall undo the
actions performed by CommitSet and release all resources which were
allocated in the TestSet step.


To implement these actions, the \ref agentxcpp::Variable
"Variable" class provides the methods \ref
agentxcpp::Variable::undoset "testset()", \ref
agentxcpp::Variable::undoset "commitset()", \ref
agentxcpp::Variable::undoset "cleanupset()" and \ref
agentxcpp::Variable::undoset "undoset()" which can be overridden by a
concrete implementation.
To implement these actions, the Variable classes provides the methods
<tt>perform_testset()</tt>, <tt>perform_commitset()</tt>,
<tt>perform_cleanupset()</tt> and <tt>perform_undoset()</tt>, which can be
overridden by a concrete implementation.

\section extend_simplecounter Extending the SimpleCounter class

To implement write support for the SimpleCounter class, we implement all four
methods. The \ref agentxcpp::Variable::undoset "testset()"
implementation allocates an \ref agentxcpp::IntegerValue "IntegerValue" value, which is deallocated by \ref
agentxcpp::Variable::undoset "cleanupset()" respectively \ref
agentxcpp::Variable::undoset "undoset()". This is not very realistic,
but it demonstrates the usage of the individual steps.
methods. The <tt>perform_testset()</tt> implementation allocates an quint32 value, which is
deallocated by <tt>perform_cleanupset()</tt> respectively <tt>perform_undoset()</tt>. This is not
very realistic, but it demonstrates the usage of the individual steps. The
<tt>perform_commitset()</tt> method stores the old value (in case an undo needs to be carried out by
<tt>perform_undoset()</tt>) and sets the new value.




\subsection TestSet

The \ref agentxcpp::Variable::undoset "testset()" function must be
overridden by any variable which should be writeable. If it is not overridden,
agentXcpp assumes that the variable is read-only (which we exploited in the
first, read-only version). Hence, we implement it for our SimpleCounter class
by adding the following code to it:
The <tt>perform_testset()</tt> method must be overridden by any variable which
should be writeable. If it is not overridden, agentXcpp assumes that the
variable is read-only (which we exploited in the first, read-only version).
This also means that an e.g. IntegerVariable object is read-only by default and
needs to be subclassed to add write support. Hence, we implement it for our
SimpleCounter class by adding the following code to it:

\code
private:
IntegerValue* old_value;
quint32* old_value;

public:
virtual testset_result_t testset(QSharedPointer<IntegerValue> new_value)
virtual testset_result_t perform_testset(qint32 new_value)
{
try
{
// Allocate space to store the old value
old_value = new IntegerValue;
old_value = new quint32;
}
catch(std::bad_alloc)
{
// Resource allocation failed.
return resourceUnavailable;
}

// setting the variable will succeed
return noError;
}
\endcode

The \ref agentxcpp::Variable::testset "testset()" method receives the
value and can check whether setting the variable to this value would work.
Here, we don't check the value, but we allocate an \ref agentxcpp::IntegerValue
"IntegerValue" to store the old value, which is needed for \ref agentxcpp::Variable::undoset
"undoset()". If allocation fails, we return the error
\ref agentxcpp::AbstractVariable::resourceUnavailable "resourceUnavailable".
The \ref agentxcpp::IntegerVariable::perform_testset "perform_testset()" method
receives the value and can check whether setting the variable to this value
would work. Here, we don't check the value, but we allocate an quint32 to store the old value,
which is needed for <tt>perform_undoset()</tt>. If allocation fails, we return the error \ref
agentxcpp::AbstractVariable::resourceUnavailable "resourceUnavailable".


\subsection CommitSet

The \ref agentxcpp::Variable::commitset "commitset()" method must
also be implemented for writeable variables. In the SimpleCounter class, this
method sets the internal value and stores the old one, in case \ref
agentxcpp::Variable::undoset "undoset()" is called:
The <tt>perform_commitset()</tt> method must also be implemented for writeable
variables. In the SimpleCounter class, this method stores the old value and
sets the internal value:
\code
virtual bool commitset(QSharedPointer<IntegerValue> new_value)
virtual bool perform_commitset(qint32 new_value)
{
// Remember old value for rollback
*old_value = counter;

(*old_value) = v;
// Set new value
counter = *new_value;

v = new_value;
// Operation succeeded
return true;
}
\endcode

\subsection CleanupSet

The \ref agentxcpp::Variable::cleanupset "cleanupset()" method
releases old_value again:
The <tt>perform_cleanupset()</tt> method releases old_value again:

\code
virtual void cleanupset(QSharedPointer<IntegerValue> new_value)
virtual void perform_cleanupset(qint32 new_value)
{
// Release old value
delete old_value;
}
\endcode

If old_value were an IntegerValue (instead of a pointer to IntegerValue) no allocation
and deallocation would have been necessary. The \ref
agentxcpp::Variable::cleanupset "cleanupset()" method would not be
implemented then. In fact, this method is the only one which is optional for
writeable variables, while the other methods are mandatory.
If old_value were an quint32 (instead of a pointer to quint32), no allocation and deallocation would
have been necessary. The <tt>perform_cleanupset()</tt> method would not be implemented then. In
fact, this method is the only one which is optional for writeable variables, while the other methods
are mandatory.

\subsection UndoSet

The \ref agentxcpp::Variable::undoset "undoset()" method must be
implemented by each variable and perform a rollback. It is technically possible
to omit this method; the compiler has no way to detect a missing
implementation. However, it is an error to omit it, and an error will be
reported to the master agent if it is missing. For simpleCounter implementation
is easy:
The <tt>perform_undoset()</tt> method must be implemented by each writeable variable and shall
perform a rollback. It is technically possible to omit this method; the compiler has no way to
detect a missing implementation. However, it is an error to omit it, and an error will be reported
to the master agent if it is missing. For simpleCounter implementation is easy:

\code
virtual bool undoset(QSharedPointer<IntegerValue> new_value)
virtual bool perform_undoset(qint32 new_value)
{
// Restore old value
counter = *old_value;

v = *old_value;
// Release old value
delete old_value;

// Rollback succeeded
return true;
}
Expand All @@ -209,8 +200,7 @@ Again, we compile and run the subagent. This is exactly the same as in the
first tutorial (it is assumed that your system is already setup accordingly):

\verbatim
g++ simpleagent.cpp -o simpleagent -lagentxcpp \
`pkg-config --cflags --libs QtNetwork QtCore`
g++ simpleagent.cpp -o simpleagent -lagentxcpp `pkg-config --cflags --libs QtNetwork QtCore`
\endverbatim

Next, we start the subagent again:
Expand Down

0 comments on commit 5d86eea

Please sign in to comment.