= Configuration = SAFplus component and application configuration can be used independently or together with the SAFplus [[Management]] add-on product. With the [[Management]] product, [[https://tools.ietf.org/html/rfc6241| NETCONF]] and [[https://www.ietf.org/rfc/rfc1157.txt | SNMP]] and the SAFplus [[CLI]] (Command Line Interface) "northbound" interfaces are available to access and modify configuration data. == Overview == Component and application configuration is accomplished by defining a schema that defines the required configuration data. This schema is defined in the YANG file format described in [[https://tools.ietf.org/html/rfc6020 | RFC6020 ]]. YANG is a simple data definition language with syntax familiar to C or Java programmers yet can be translated without loss of information into an XML format (called YIN). Definitions are organized into "modules" which can contain notifications (asynchronous messages from the server to the client), remote procedure calls, and a complex structured data feature-similar to SNMP MIB files, XML, or any other data definition language. Status, and statistics use this same infrastructure; the only difference is that these data are not stored persistently (to disk). Using the SAFplus Management add-on product, YANG files can be compiled into a C++ class structure. These classes represent the configuration, status, statistics, notifications and functions that were defined by the YANG information model. Application programmers can extend these classes by adding additional objects or creating derived classes to capture other related data. These C++ objects are automatically connected to and underlying persistent database and to the northbound protocols when the application "binds" the root object of the configuration tree to a location within the YANG defined information tree. During this "bind" initialization, child C++ objects are automatically created if they exist in the persistent database. Configuration can be defined by XML files is loaded and saved to the persistent database by OpenClovis tools. Offline, configuration can be manipulated by Python (or C) scripts and then exported as XML files or pushed into the persistent database. This architecture allows applications to access configuration via an intuitive object-oriented interface and avoids the need for every application to handle details of northbound interfaces, configuration/XML file parsing, CLI interfaces, and configuration database access. === SAFplus Cluster Modelling Tool Integration === OpenClovis YANG file extensions can be used to specify configuration information to be associated with instances of application components in the SAFplus IDE. The SAFplus IDE will automatically create data entry dialogs for the specified information and output the proper XML configuration files during code generation. A "leaf" (data item) is IDE enabled with the "safplus:ide" extension for example: {{{#!highlight cpp leaf timeout { type uint64; description "The maximum time this operation should take before completion. Specified in milliseconds."; safplus:ide "Maximum Time: "; } }}} This leaf would create a text entry box in the configuration dialog labelled "Maximum Time: ". Mouse-over help would display the description. == YANG Schema Extensions == SAFplus defines certain extensions to the basic YANG modelling language that help translate YANG to C++ and display configuration in the IDE and CLI. They are defined in the SAFplusTypes.yang file and can be included in your yang file like this: {{{#!highlight c import SAFplusTypes { prefix "safplus"; } }}} * c-type Overrides the generated type. For example: {{{#!highlight c leaf-list serviceUnits { safplus:c-type "SAFplusAmf::ServiceUnit*"; type instance-identifier; description "This component is the proxy for the components listed here."; } }}} A YANG instance-identifier is normally modelled as a string which contains the name-and-path of the instance. In the example above, the generated type will be overridden to a "SAFplusAmf::ServiceUnit*" pointer: {{{#!highlight c ClMgtProvList serviceUnits; }}} * c-existing Indicates that this type already exists "natively" in c++ so nothing should be generated. The argument is the name of the type. Example: {{{#!highlight c typedef SaTimeT { type uint64; c-existing "SaTimeT"; description "A representation of a time interval, specified in milliseconds"; } }}} * ui-prompt Supplies the prompt for the GUI or CLI when requesting this field. Example: {{{#!highlight c leaf duration { type safplus:SaTimeT; description "The time period (in milliseconds) involved."; safplus:ui-prompt "within time: "; } }}} The prompt will be: '''within time: ''' rather than: '''duration: ''' * ui-group Places this object in a visual or logical grouping for better organization. {{{#!highlight c container serviceUnitFailureEscalationPolicy { uses EscalationPolicy; description "The maximum Service Unit failure rate allowed before this node is faulted"; safplus:ui-group "Service Unit Failure Escalation Policy"; } }}} * ui-dialog This command triggers a sub-window (not necessarily a modal dialog box) that is created for these options. If the value is 'class ' or 'object ' this indicates that this subwindow should be instantiated as the custom code denoted by . All other values are the title of the sub-window. * atomic This indicates the minimum serialization granularity which has an effect on database reads/writes. The effect of the 'atomic' field is to cause all siblings and children to be written to the database as a single unit. This can dramatically reduce database size and improve database access times for those fields that are generally loaded or saved together. Database size is especially reduced for leaf-list objects. For example: {{{#!highlight c container AllTogether { safplus:atomic; leaf stringTest { type string; } leaf-list int64Test { type int64; } } }}} All the objects in "AllTogether" will be serialized as a single unit, including the entire int64Test array. {{{#!highlight c list AList { key "name"; atomic; leaf name { type string; } leaf data { type int64; } } }}} Each element in the AList array is serialized separately, but fields within the element (name and data) are stored together. {{{#!highlight c container atomicList { atomic; list AList { key "name"; leaf name { type string; } leaf data { type int64; } } } }}} This entire list is serialized atomically. == XML And Python == Configuration can be loaded and saved in XML format through a Python script. Convert XML to database: python dbalpy.py -x This command converts .xml into the configuration database format. == Object Serialization == There are two object serialization formats, text (XML) and binary (database and RPC calls). === Internals === Object serialization/deserialization must handle references (pointers). Therefore it must occur within the context of an object namespace. Binary serialization shall be called marshallization (marshal, demarshal), Text serialization shall be called XMLizaton (XMLize, deXMLize). All marshal operations occur via 4 overloaded APIs: {{{#!highlight cpp inline void demarshal(const std::string& input,ClMgtObject* context, & result) {...} inline marshal(const & input,ClMgtObject* context, const MarshalBuffer& output) {...} inline void deXMLize(const std::string& input,ClMgtObject* context, & result) {...} inline XMLize(const & input,ClMgtObject* context, const MarshalBuffer& output) {...} }}} Note, we need to find or create an efficient extensible buffer type "MarshalBuffer". This type should allow you to specify its size at creation time, for cases where the marshaled size can be pre-calculated. And it should be automatically and efficiently extensible for cases where its size cannot be pre-calculated. The act of "flattening" is converting a complex buffer (a linked list of small buffers, for example) into a single char* allocation of contiguous memory. Ideally, MarshalBuffer could translate directly into a scatter-gather list in IOC and DB so the buffer never needs to be flattened. "Freed" MarshalBuffers should be stored in linked lists of different sizes rather than actually calling "free()". This is both much more efficient and will make memory leak obvious. Individual buffer chunks should have beginning and end guard bytes to detect applications overwriting the buffer.