Merge "Doc how to find MR projects from POM or taglist"
[docs.git] / docs / developer-guide / opflex-libopflex-developer-guide.rst
1 .. _opflex-libopflex-dev-guide:
2
3 OpFlex libopflex Developer Guide
4 ================================
5
6 Overview
7 --------
8
9 The OpFlex framework allows you to develop agents that can communicate
10 using the OpFlex protocol and act as a policy element in an OpFlex-based
11 distributed control system. The OpFlex architecture provides a
12 distributed control system based on a declarative policy information
13 model. The policies are defined at a logically centralized policy
14 repository and enforced within a set of distributed policy elements. The
15 policy repository communicates with the subordinate policy elements
16 using the OpFlex control protocol. This protocol allows for
17 bidirectional communication of policy, events, statistics, and faults.
18
19 Rather than simply providing access to the OpFlex protocol, this
20 framework allows you to directly manipulate a management information
21 tree containing a hierarchy of managed objects. This tree is kept in
22 sync as needed with other policy elements in the system, and you are
23 automatically notified when important changes to the model occur.
24 Additionally, we can ensure that only those managed objects that are
25 important to the local policy element are synchronized locally.
26
27 Object Model
28 ~~~~~~~~~~~~
29
30 Interactions with the OpFlex framework happen through the management
31 information tree. This is a tree of managed objects defined by an object
32 model specific to your application. There are a few important major
33 category of objects that can appear in the model.
34
35 -  First, there is the policy object. A policy object represents some
36    data related to a policy that describes a user intent for how the
37    system should behave. A policy object is stored in the policy
38    repository which is the source of "truth" for this object.
39
40 -  Second, there is an endpoint object. A endpoint represents an entity
41    in the system to which we want to apply policy, which could be a
42    network interface, a storage array, or other relevent policy
43    endpoint. Endpoints are discovered and reported by policy elements
44    locally, and are synchronized into the endpoint repository. The
45    originating policy element is the source of truth for the endpoints
46    it discovers. Policy elements can retrieve information about
47    endpoints discovered by other policy elements by resolving endpoints
48    from the endpoint repository.
49
50 -  Third, there is the observable object. An observable object
51    represents some state related to the operational status or health of
52    the policy element. Observable objects will be reported to the
53    observer.
54
55 -  Finally, there is the local-only object. This is the simplest object
56    because it exists only local to a particular policy element. These
57    objects can be used to store state specific to that policy element,
58    or as helpers to resolve other objects. Read on to learn more.
59
60 You can use the genie tool that is included with the framework to
61 produce your application model along with a set of generated accessor
62 classes that can work with this framework library. You should refer to
63 the documentation that accompanies the genie tool for information on how
64 to use to to generate your object model. Later in this guide, we’ll go
65 through examples of how to use the generated managed object accessor
66 classes.
67
68 Programming by Side Effect
69 ~~~~~~~~~~~~~~~~~~~~~~~~~~
70
71 When developing software on the OpFlex framework, you’ll need to think
72 in a slightly different way. Rather than calling an API function that
73 would perform some specific action, you’ll need to write a managed
74 object to the managed object database. Writing that object to the store
75 will trigger the side effect of performing the action that you want.
76
77 For example, a policy element will need to have a component responsible
78 for discovering policy endpoints. When it discovers a policy endpoint,
79 it would write an endpoint object into the managed object database. That
80 endpoint object will contain a reference to policy that is relevant to
81 the endpoint object. This will trigger a whole cascade of events. First,
82 the framework will notice that an endpoint object has been created and
83 it will write it to the endpoint repository. Second, the framework to
84 will attempt to resolve the unresolved reference to the relevent policy
85 object. There might be a whole chain of policy resolutions that will be
86 automatically performed to download all the relevent policy until there
87 are no longer any dangling references.
88
89 As long as there is a locally-created object in the system with a
90 reference to that policy, the framework will continually ensure that the
91 policy and any transitive policies are kept up to date. The policy
92 element can subscribe to updates to these policy classes that will be
93 invoked either the first time the policy is resolved or any time the
94 policy changes.
95
96 A consequence of this design is that the managed object database can be
97 temporarily in an inconsistent state with unresolved dangling
98 references. Eventually, however, the inconsistency will be fully
99 resolved. The policy element must be able to cleanly handle
100 partially-resolved or inconsistent state and eventually reach the
101 correct state as it receives these update notifications. Note that, in
102 the OpFlex architecture, when there is no policy that specifically
103 allows a particular action, that action must be prevented.
104
105 Let’s cover one slightly more complex example. If a policy element needs
106 to discover information about an endpoint that is not local to that
107 policy element, it will need to retrieve that information from the
108 endpoint repository. However, just as there is no API call to retrieve a
109 policy object from the policy repository, there is no API call to
110 retrieve an endpoint from the endpoint repository.
111
112 To get this information, the policy element needs to create a local-only
113 object that references the endpoint. Once it creates this local-only
114 object, if the endpoint is not already resolved, the framework will
115 notice the dangling reference and automatically resolve the endpoint
116 from the endpoint respository. When the endpoint resolution completes,
117 the framework deliver an update notification to the policy element. The
118 policy element will continue to receive any updates related to that
119 endpoint until the policy element remove the local-only reference to the
120 object. Once this occurs, the framework can garbage-collect any
121 unreferenced objects.
122
123 Threading and Ownership
124 ~~~~~~~~~~~~~~~~~~~~~~~
125
126 The OpFlex framework uses a somewhat unique threading model. Each
127 managed object in the system belongs to a particular owner. An owner
128 would typically be a single thread that is reponsible for all updates to
129 objects with that owner. Though anything can read the state of a managed
130 object, only the owner of a managed object is permitted to write to it.
131 Though this is not strictly required for correctness, the performance of
132 the system wil be best if you ensure that only one thread at a time is
133 writing to objects with a particular owner.
134
135 Change notifications are delivered in a serialized fashion by a single
136 thread. Blocking this thread from a notification callback will stall
137 delivery of all notifications. It is therefore best practice to ensure
138 that you do not block or perform long-running operations from a
139 notification callback.
140
141 Key APIs and Interfaces
142 -----------------------
143
144 Basic Usage and Initialization
145 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
146
147 The primary interface point into the framework is
148 opflex::ofcore::OFFramework. You can choose to instantiate your own copy
149 of the framework, or you can use the static default instance.
150
151 Before you can use the framework, you must initialize it by installing
152 your model metadata. The model metadata is accessible through the
153 generated model library. In this case, it assumes your model is called
154 "mymodel":
155
156 .. code:: cpp
157
158     #include <opflex/ofcore/OFFramework.h>
159     #include <mymodel/metadata/metadata.hpp>
160     // ...
161     using opflex::ofcore::OFFramework;
162     OFFramework::defaultInstance().setModel(mymodel::getMetadata());
163
164 The other critical piece of information required for initialization is
165 the OpFlex identity information. The identity information is required in
166 order to successfully connect to OpFlex peers. In OpFlex, each component
167 has a unique name within its policy domain, and each policy domain is
168 identified by a globally unique domain name. You can set this identity
169 information by calling:
170
171 .. code:: cpp
172
173     OFFramework::defaultInstance()
174         .setOpflexIdentity("[component name]", "[unique domain]");
175
176 You can then start the framework simply by calling:
177
178 .. code:: cpp
179
180     OFFramework::defaultInstance().start();
181
182 Finally, you can add peers after the framework is started by calling the
183 ``opflex::ofcore::OFFramework::addPeer`` method:
184
185 .. code:: cpp
186
187     OFFramework::defaultInstance().addPeer("192.168.1.5", 1234);
188
189 When connecting to the peer, that peer may provide an additional list of
190 peers to connect to, which will be automatically added as peers. If the
191 peer does not include itself in the list, then the framework will
192 disconnect from that peer and add the peers in the list. In this way, it
193 is possible to automatically bootstrap the correct set of peers using a
194 known hostname or IP address or a known, fixed anycast IP address.
195
196 To cleanly shut down, you can call:
197
198 .. code:: cpp
199
200     OFFramework::defaultInstance().stop();
201
202 Working with Data in the Tree
203 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
204
205 Reading from the Tree
206 ^^^^^^^^^^^^^^^^^^^^^
207
208 You can access data in the managed tree using the generated accessor
209 classes. The details of exactly which classes you’ll use will depend on
210 the model you’re using, but let’s assume that we have a simple model
211 called "simple" with the following classes:
212
213 -  root - The root node. The URI for the root node is "/"
214
215 -  foo - A policy object, and a child of root, with a scalar string
216    property called "bar", and a unsigned 64-bit integer property called
217    baz. The bar property is the naming property for foo. The URI for a
218    foo object would be "/foo/[value of bar]/"
219
220 -  fooref - A local-only child of root, with a reference to a foo, and a
221    scalar string property called "bar". The bar property is the naming
222    property for foo. The URI for a fooref object would be
223    "/fooref/[value of bar]/"
224
225 In this example, we’ll have a generated class for each of the objects.
226 There are two main ways to get access to an object in the tree.
227
228 First, we can get instantiate an accessor class to any node in the tree
229 by calling one of its static resolve functions. The resolve functions
230 can take either an already-built URI that represents the object, or you
231 can call the version that will locate the object by its naming
232 properties.
233
234 Second, we can access the object also from its parent object using the
235 appropriate child resolver member functions.
236
237 However we read it, the object we get back is an immutable view into the
238 object it references. The properties set locally on that object will not
239 change even though the underlying object may have been updated in the
240 store. Note, however, that its children can change between when you
241 first retrieve the object and when you resolve any children.
242
243 Another thing that is critical to note again is that when you attempt to
244 resolve an object, you can get back nothing, even if the object actually
245 does exist on another OpFlex node. You must ensure that some object in
246 the managed object database references the remote managed object you
247 want before it will be visible to you.
248
249 To get access to the root node using the default framework instance, we
250 can simply call:
251
252 .. code:: cpp
253
254     using boost::shared_ptr;
255     using boost::optional;
256     optional<shared_ptr<simple::root> > r(simple::root::resolve());
257
258 Note that whenever we can a resolve function, we get back our data in
259 the form of an optional shared pointer to the object instance. If the
260 node does not exist, then the optional will be set to boost::none. Note
261 that if you dereference an optional that has not been set, you’ll
262 trigger an assert, so you must check the return as follows:
263
264 .. code:: cpp
265
266     if (!r) {
267        // handle missing object
268     }
269
270 Now let’s get a child node of the root in three different ways:
271
272 .. code:: cpp
273
274     // Get foo1 by constructing its URI from the root
275     optional<shared_ptr<simple::foo> > foo1(simple::foo::resolve("test"));
276     // get foo1 by constructing its URI relative to its parent
277     foo1 = r.get()->resolveFoo("test");
278     // get foo1 by manually building its URI
279     foo1 = simple::foo::resolve(opflex::modb::URIBuilder()
280                                    .addElement("foo")
281                                    .addElement("test")
282                                    .build());
283
284 All three of these calls will give us the same object, which is the
285 "foo" object located at "/foo/test/".
286
287 The foo class has a single string property called "bar". We can easily
288 access it as follows:
289
290 .. code:: cpp
291
292     const std::string& barv = foo1.getBar();
293
294 Writing to the Tree
295 ^^^^^^^^^^^^^^^^^^^
296
297 Writing to the tree is nearly as easy as reading from it. The key
298 concept to understand is the mutator object. If you want to make changes
299 to the tree, you must allocate a mutator object. The mutator will
300 register itself in some thread-local storage in the framework instance
301 you’re using. The mutator is specific to a single "owner" for the data,
302 so you can only make changes to data associated with that owner.
303
304 Whenever you modify one of the accessor classes, the change is actually
305 forwarded to the currently-active mutator. You won’t see any of the
306 changes you make until you call the commit member function on the
307 mutator. When you do that, all the changes you made are written into the
308 store.
309
310 Once the changes are written into the store, you will need to call the
311 appropriate resolve function again to see the changes.
312
313 Allocating a mutator is simple. To create a mutator for the default
314 framework instance associated with the owner "owner1", just allocate the
315 mutator on the stack. Be sure to call commit() before it goes out of
316 scope or you’ll lose your changes.
317
318 .. code:: cpp
319
320     {
321         opflex::modb::Mutator mutator("owner1");
322         // make changes here
323         mutator.commit();
324     }
325
326 Note that if an exception is thrown while making changes but before
327 committing, the mutator will go out of scope and the changes will be
328 discarded.
329
330 To create a new node, you must call the appropriate add[Child] member
331 function on its parent. This function takes parameters for each of the
332 naming properties for the object:
333
334 .. code:: cpp
335
336     shared_ptr<simple::foo> newfoo(root->addFoo("test"));
337
338 This will return a shared pointer to a new foo object that has been
339 registered in the active mutator but not yet committed. The "bar" naming
340 property will be set automatically, but if you want to set the "baz"
341 property now, you can do so by calling:
342
343 .. code:: cpp
344
345     newfoo->setBaz(42);
346
347 Note that creating the root node requires a call to the special static
348 class method createRootElement:
349
350 .. code:: cpp
351
352     shared_ptr<simple::root> newroot(simple::root::createRootElement());
353
354 Here’s a complete example that ties this all together:
355
356 .. code:: cpp
357
358     {
359         opflex::modb::Mutator mutator("owner1");
360         shared_ptr<simple::root> newroot(simple::root::createRootElement());
361         shared_ptr<simple::root> newfoo(newroot->addFoo("test"));
362         newfoo->setBaz(42);
363
364         mutator.commit();
365     }
366
367 Update Notifications
368 ~~~~~~~~~~~~~~~~~~~~
369
370 When using the OpFlex framework, you’re likely to find that most of your
371 time is spend responding to changes in the managed object database. To
372 get these notifications, you’re going to need to register some number of
373 listeners.
374
375 You can register an object listener to see all changes related to a
376 particular class by calling a static function for that class. You’ll
377 then get notifications whenever any object in that class is added,
378 updated, or deleted. The listener should queue a task to read the new
379 state and perform appropriate processing. If this function blocks or
380 peforms a long-running operation, then the dispatching of update
381 notifications will be stalled, but there will not be any other
382 deleterious effects.
383
384 If multiple changes happen to the same URI, then at least one
385 notification will be delivered but some events may be consolidated.
386
387 The update you get will tell you the URI and the Class ID of the changed
388 object. The class ID is a unique ID for each class. When you get the
389 update, you’ll need to call the appropriate resolve function to retrieve
390 the new value.
391
392 You’ll need to create your own object listener derived from
393 opflex::modb::ObjectListener:
394
395 .. code:: cpp
396
397     class MyListener : public ObjectListener {
398     public:
399         MyListener() { }
400         virtual void objectUpdated(class_id_t class_id, const URI& uri) {
401             // Your handler here
402         }
403     };
404
405 To register your listener with the default framework instance, just call
406 the appropriate class static method:
407
408 .. code:: cpp
409
410     MyListener listener;
411     simple::foo::registerListener(&listener);
412     // main loop
413     simple::foo::unregisterListener(&listener);
414
415 The listener will now recieve notifications whenever any foo or any
416 children of any foo object changes.
417
418 Note that you must ensure that you unregister your listeners before
419 deallocating them.
420
421 API Reference Documentation
422 ---------------------------
423
424 Complete API documentation can be found by in doc/html/index.html in
425 any build.