.. include:: ../../../router.1.0/srcDoc/vars.rst
.. _section-Rap:
Remote Administration Platform
******************************
This section describes details of the |RAP|, which represents the
foundation of the remote access capabilities available in |RTI_RS|,
|RTI| *Recording Service,* and |RTI| *Queuing Service.*
The |RAP| provides a common infrastructure that unifies and consolidates
the remote interface to all |RTI_SERVICE|\s.
.. Note::
Remote administration of |RTI_SERVICE|\s requires an understanding of
the `application resource model`. We recommend that you
read :ref:`section-Arm` (:ref:`section-Arm`) before
continuing with this section.
The |RAP| addresses two areas:
- **Resource Interface:** How to perform operations on a set of resource objects
that are available as part of the public interface of the remote service.
- **Communication:** How the remote service receives and sends information.
The combination of these two areas provides the general view of the |RAP|,
as shown in :numref:`FigureRapGeneralView`. The |RAP| is defined as a
request/reply architecture. In this architecture, the service is modeled as a
set of *resources* upon which the requester client can perform operations.
Resources represent objects that have both *state* and *behavior*.
.. figure:: ../../../router.1.0/srcDoc/static/RapGeneralView.svg
:figwidth: 80 %
:alt: Routing Service Adapter Overview
:name: FigureRapGeneralView
:align: center
General View of the |RAP| Architecture
Clients issue requests indicating the desired operation and receive replies
from the service with the result of the requests. If multiple clients issue
multiple requests to one or more services, the client will receive only replies
to its own requests.
.. _section-Rap-Interface:
Remote Interface
================
Services offer their available functionality through their set of resources.
The |RAP| defines a Representational State Transfer (REST)-like interface to
address service resources and perform operations on them. A resource operation
is determined by a REST request and the associated result by a REST reply.
.. list-table:: REST Interface
:name: TableRestInterface
:widths: 15 55
:header-rows: 1
* - Element
- Description
* - REST Request
- [method] + [resource_identifier] + [body]
- ``method``: Specifies the action to be performed on a service resource.
There is only a small subset of methods, known as *standard methods*
(see :ref:`section-Rap-Interface-StandardMethods`).
- ``resource_identifier``: Addresses a concrete service resource. Each
concrete service has its own set of resources (see
:ref:`section-Arm-ResourceId`).
- ``body``: Optional request data that contains necessary information to
complete the operation.
* - REST Reply
- [return code] + [body]
- ``return code``: Integer indicating the result of the operation.
- ``body``: Optional reply data that contains information associated with
the processing of the request.
.. _section-Rap-Interface-StandardMethods:
Standard Methods
----------------
The |RAP| defines the methods listed in :numref:`TableStdMethods`.
.. list-table:: Standard Methods
:name: TableStdMethods
:widths: 20 20 30 30
:header-rows: 1
* - Method
- URI
- Request Body
- Reply Body
* - ``CREATE``
- Parent collection resource identifier
- Resource representation
- N/A
* - ``GET``
- Resource identifier
- N/A
- Resource representation
* - ``UPDATE``
- Resource identifier
- Resource representation
- N/A
* - ``DELETE``
- Resource identifier
- Undefined
- N/A
.. _section-Rap-Interface-CustomMethods:
Custom Methods
--------------
There are certain cases in which an operation on a service resource cannot be
mapped intuitively to a standard method and resource identifier. *Custom
methods* address this limitation.
A custom method can be specified as part of the resource identifier, after the
resource path, separated by a ``:``.
.. list-table::
:header-rows: 0
* - ``UPDATE`` + [resource_identifier] ``:`` [custom_verb]
It is up to each service implementation to define which custom methods are
available and on what resources they apply. Custom methods follow these
conventions:
- They are invoked through the ``UPDATE`` standard method.
- They are named using lower snake_case.
- They may use the request body and reply body if necessary.
Example: Database Rollover
""""""""""""""""""""""""""
This example shows the REST request to perform a file rollover operation on
a file-based database:
.. code-block:: html
UPDATE /databases/MyDatabase:rollover
.. _section-Rap-Communication:
Communication
=============
The information exchange between client and server is based on the DDS
request-reply pattern, as shown in :numref:`FigureRapCommunication`. The
client maps to a |REQUESTER|, whereas the server maps to a |REPLIER|.
.. figure:: ../../../router.1.0/srcDoc/static/RapCommunication.svg
:figwidth: 80 %
:alt: Routing Service Adapter Overview
:name: FigureRapCommunication
:align: center
Communication in |RAP| is Based on DDS Request-Reply
The communication is performed over a single request-reply channel, composed of
two topics:
- **Command Request Topic:** Topic through which the client sends
the requests to the server.
- **Command Reply Topic:** Topic through which the server sends the
replies to the received requests.
The definition of these topics is shown in :numref:`TableRouterAdminTopics`:
.. list-table:: Remote Administration |TOPIC|\s
:name: TableRouterAdminTopics
:widths: 30 30 30
:header-rows: 1
* - Topic
- Name
- Top-level Type Name
* - |CREQ|
- rti/service/administration/command_request
- ``rti::service::admin::CommandRequest``
* - |CREP|
- rti/service/administration/command_reply
- ``rti::service::admin::CommandReply``
The definition for each |TOPIC| type is described below.
.. literalinclude:: /../../router.1.0/resource/idl/ServiceAdmin.idl
:caption: ``CommandRequest`` Type
:start-after: /* CommandRequest
:end-before: /* CommandReply
.. list-table:: ``CommandRequest``
:name: TableRequestTopicType
:widths: 20 80
:header-rows: 1
* - Field Name
- Description
* - ``instance_id``
- Associates a request with a given instance in the |CREQ|. |br|
This can be used if your requester application model wants to leverage
outstanding requests. In general, this member is always set to zero,
so all requests belong to the same |CREQ| instance.
* - ``application_name``
- Optional member that indicates the target service instance where the
request is sent. If ``NULL``, the request will be sent to all services. |br|
* - ``action``
- Indicates the resource operation.
* - ``resource_identifier``
- Addresses a service resource.
* - ``string_body``
- Contains content represented as a string.
* - ``octet_body``
- Contains content represented as binary.
.. literalinclude:: /../../router.1.0/resource/idl/ServiceAdmin.idl
:caption: ``CommandReply`` Type
:start-after: /* CommandReply
:end-before: // Module Admin
.. list-table:: ``CommandReply``
:name: TableReplyTopicType
:widths: 25 75
:header-rows: 1
* - Field Name
- Description
* - ``retcode``
- Indicates the result of the operation.
* - ``native_retcode``
- Provides extra information about the result of the operation.
* - ``string_body``
- Return value of the operation, represented as a string.
* - ``octet_body``
- Return value of the operation, represented as binary.
The type definitions for both the |CREQ| and |CREP| are in the file
``[NDDSHOME]/resource/idl/ServiceAdmin.idl``.
The definition of the request and reply topics is independent of any specific
service implementation. In fact, the topic names are fixed, unique, and shared
across all services that rely on the |RAP|. Clients can target specific
services through two mechanisms:
- Specifying a concrete service instance by providing its *application name*.
The application name is a service attribute and can be set at service
creation time.
- Specifying the configuration name loaded by the target services.
The target service configuration shall be present in the service resource
part of the ``resource_identifier``.
.. _section-Rap-Communication-ReplySequence:
Reply Sequence
--------------
Usually a request is expected to generate a single reply. Sometimes, however,
a request may trigger the *generation of multiple replies*, all associated with
the same request.
The |RAP| communication architecture allows services to respond to certain
requests with a *reply sequence*. All the samples in a reply sequence use the
the metadata ``SampleFlagBits`` to indicate whether it belongs to a reply
sequence and whether there are more replies pending.
The ``SampleFlagBits`` may contain different flags that indicate the status
of the reply procedure. For a given reply sequence, the associated sample flags
for each reply may contain:
- ``SEQUENTIAL_REPLY``: If present, this indicates that the sample is the first
reply of a reply sequence and there are more on the way.
- ``FINAL_REPLY``: If present, this indicates that the sample is the last one
belonging to a reply sequence. This flag is valid only if the
``SEQUENTIAL_REPLY`` is also set.
For more on SampleFlagBits, see documentation on
the DDS_SampleInfo structure in the Connext DDS API Reference HTML documentation.
.. _section-Rap-Communication-AccessingExample:
Example: Accessing from |CONNEXT_HEADING| Application
-----------------------------------------------------
This example shows a Modern C++ snippet on how to use |CONNEXT| Request-Reply
to disable a |RS| instance.
The first step is to generate code for administration types in
``[NDDSHOME]/resource/idl/ServiceAdmin.idl`` (which has a dependency on
``[NDDSHOME]/resource/idl/ServiceCommon.idl``):
.. code-block:: console
rtiddsgen -language C++11 -unboundedSupport -qualifiedEnumerator ServiceCommon.idl
rtiddsgen -language C++11 -unboundedSupport -qualifiedEnumerator ServiceAdmin.idl
Note that there are two important options in the generation command:
- ``-unboundedSupport`` is required to support unbounded length for the
request's and reply's octet and string bodies. This allows sending and receiving
any-size data in a single sample. Because of the unbounded support, you will
need to make sure that the requester's |DW| and |DR| are configured with
a QoS that sets the buffer pools' memory settings (in addition to any other
QoS). For example:
.. code-block:: xml
dds.data_writer.history.memory_manager.fast_pool.pool_buffer_max_size
16384
true
dds.data_reader.history.memory_manager.fast_pool.pool_buffer_max_size
16384
- ``-qualifiedEnumerator`` generates enum labels with the proper namespace based
on the IDL modules.
Then you create your requester application that handles the
requests and replies. For example:
.. code-block:: C++
#include "ServiceAdmin.hpp"
#include "ServiceCommon.hpp"
using namespace rti::request;
using namespace dds::core;
using namespace RTI::Service;
using namespace RTI::Service::Admin;
const unsigned int WAIT_TIMEOUT_SEC_MAX = 10;
const unsigned int ADMIN_DOMAIN_ID = 55;
int main(int, char *[]) {
try {
dds::domain::DomainParticipant participant(ADMIN_DOMAIN_ID);
// create requester params
rti::request::RequesterParams requester_params(participant);
requester_params.request_topic_name(COMMAND_REQUEST_TOPIC_NAME);
requester_params.reply_topic_name(COMMAND_REPLY_TOPIC_NAME);
// Wait for Routing Service Discovery
dds::core::status::PublicationMatchedStatus matched_status;
unsigned int wait_count = 0;
std::cout << "Waiting for a matching replier..." << std::endl;
while (matched_status.current_count() < 1
&& wait_count < WAIT_TIMEOUT_SEC_MAX) {
matched_status = requester.request_datawriter().publication_matched_status();
wait_count++;
rti::util::sleep(Duration(1));
}
if (matched_status.current_count() < 1) {
throw dds::core::Error("No matching replier found.");
}
/*
* Setup command
*/
CommandRequest request;
request.action(CommandActionKind::UPDATE_ACTION);
request.resource_identifier("/routing_services/MyRouter/state");
dds::topic::topic_type_support::to_cdr_buffer(
reinterpret_cast &>(request.octet_body()),
EntityState(EntityStateKind::DISABLED));
/*
* Send disable
*/
requester.send_request(request);
CommandReply reply = requester.receive_reply(
Duration(WAIT_TIMEOUT_SEC_MAX));
if (reply.retcode() == CommandReplyRetcode::OK_RETCODE) {
std::cout << "Command returned: " << reply.string_body() << std::endl;
} else {
std::cout << "Unsuccessful command returned value "
<< reply.retcode() << "." << std::endl;
throw dds::core::Error("Error in replier");
}
} catch (const std::exception& ex) {
std::cout << "Exception: " << ex.what() << std::endl;
return -1;
}
return 0;
}
You will need to compile your application code along with the generated files
and link with the standard |CONNEXT| libraries required for the Modern C++ API
in addition to the message API (``rticonnextmsgcpp2``).
In :link_community_examples_s:`RTI Community Examples Repository `
you can see a full example for an administration requester that shows how to send
different commands to *RTI Recording Service*.
.. _section-Rap-CommonOps:
Common Operations
=================
The set of services that use the |RAP| to implement remote administration
also share a base remote interface that consolidates and unifies the semantics
and behavior of certain common operations.
Services containing resources that implement the common operations conform to
the base remote interface, making sure that signatures, semantics, behavior,
and conditions are respected.
The following sections describe each of these common operations.
..
We will be referring to the sections below from each specific service API
reference.
..
.. _section-Rap-CommonOps-Create:
Create Resource
---------------
.. function:: CREATE [resource_identifier]
Creates a resource object from its configuration in XML representation.
This operation creates a resource object and its contained entities.
The created object becomes a child of the parent specified in the
``resource_identifier``.
After successful creation, the resource object is fully addressable for
additional remote access, and the associated object configuration is
inserted into the currently loaded full XML configuration.
**Request body**
- ``string_body``: XML representation of the resource object provided
as |SCHEME_FILE| or |SCHEME_STR|.
- Example |SCHEME_STR| request body:
.. code-block:: xml
str://"
...
"
- Example |SCHEME_FILE| request body:
.. code-block:: html
file:///home/rti/config/service_my_resource.xml
**Reply body**
- Empty.
**Return codes**
The operation may return a reply with error if:
- The specified resource identifier does not exist.
- The specified configuration is schematically invalid.
- There was an error creating the resource object.
.. _section-Rap-CommonOps-Get:
Get Resource
------------
.. function:: GET [resource_identifier]
Returns an equivalent XML string that represents the current state of the
resource object configuration, including any updates performed during its
lifecycle.
**Request body**
- Empty.
**Reply body**
- ``string_body``: XML representation of the resource object.
- Example reply body:
.. code-block:: xml
...
**Return codes**
The operation may return a reply with error if:
- The specified resource identifier does not exist.
.. _section-Rap-CommonOps-Update:
Update Resource
---------------
.. function:: UPDATE [resource_identifier]
Updates the specified resource object from its configuration in XML
representation.
This operation modifies the properties of the resource object, including
the associated configuration. Only the mutable properties of the resource
class can be updated while the object is enabled. To update immutable
properties, the resource object must be disabled first.
.. note::
Properties of a child resource cannot be updated as part of a parent
resource. Instead, child resources must be addressed and updated
independently.
Implementations may validate the received configuration against a scheme
(DTD or XSD) that defines the valid set of accepted parameters (for
example, only mutable elements).
The update content should only include only the properties to be updated or
changed. You are not required to provide the full representation of the object
being updated.
For example, consider the XML full representation of an object as follows:
.. code-block:: xml
initial_A
initial_B
initial_C
...
The update should only contain the content for the properties you want to
modify. For example, the following will only update ``nested_resource_B`` to a new
value, leaving the other nested resources unchanged:
.. code-block:: xml
updated_B
...
**Request body**
- ``string_body``: XML representation of the resource object provided
as |SCHEME_FILE| or |SCHEME_STR|.
- Example |SCHEME_STR| request body:
.. code-block:: xml
str://"
...
"
- Example |SCHEME_FILE| request body:
.. code-block:: html
file:///home/rti/config/service_update_my_resource.xml
**Reply body**
- Empty.
**Return codes**
The operation may return a reply with error if:
- The specified resource identifier does not exist.
- The specified configuration is schematically invalid.
- The specified configuration contains changes in immutable properties.
- There was an error updating the resource object.
.. _section-Rap-CommonOps-SetState:
Set Resource State
------------------
.. function:: UPDATE [resource_identifier]/state
Sends a state change request to the specified resource object.
This operation attempts to change the state of the specified resource
object and propagates the request to the resource object's contained entities.
The target state must be one of the resource class's valid accepted states.
**Request body**
- ``octet_body``: CDR representation of an entity state.
**Reply body**
- Empty.
**Return codes**
The operation may return a reply with error if:
- The specified resource identifier does not exist.
- The target request is invalid.
- The resource object reported an error while performing the state transition.
.. _section-Rap-CommonOps-GetState:
Get Resource State
------------------
.. function:: GET [resource_identifier]/state
Gets the current state of the specified resource object.
This operation attempts to fetch the state of the specified resource object.
The target's state is returned as a part of the reply.
**Request body**
- Empty
**Reply body**
- ``octet_body``: CDR representation of an entity's current state.
**Return codes**
The operation may return a reply with error if:
- The specified resource identifier does not exist.
- The target request is invalid.
- The resource object reported an error while fetching its current state.
.. _section-Rap-CommonOps-Delete:
Delete Resource
---------------
.. function:: DELETE [resource_identifier]
Deletes the specified resource object.
This operation deletes a resource object and its contained entities. The
deleted object is removed from its parent resource object.
The associated object configuration is removed from the currently loaded
full XML configuration.
After a successful deletion, the resource object is no longer addressable
for additional remote access.
**Request body**
- Empty.
**Reply body**
- Empty.
**Return codes**
The operation may return a reply with error if:
- The specified resource identifier does not exist.
- There was an error deleting the resource object.