Fix coverity warnings in VOM and VAPI
[vpp.git] / src / vpp-api / vom / om.hpp
1 /*
2  * Copyright (c) 2017 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #ifndef __VOM_OM_H__
17 #define __VOM_OM_H__
18
19 #include <memory>
20 #include <set>
21
22 #include "vom/client_db.hpp"
23 #include "vom/hw.hpp"
24
25 /**
26
27 The VPP Object Model (VOM) library.
28
29 Before we begin, a glossary of terms:
30    - Agent or client: A user mode process that links to and uses the VOM library
31      to programme VPP
32    - VPP: A running instance of VPP
33    - High Availability (HA): Scenarios where the client and/or VPP restart with
34      minimal service interruption.
35    - CReate, Update, Delete (CRUD): An API style where the producer issues
36      notifications to changes to objects
37
38 The VOM is a C++ library that models entities in VPP as C++ classes. The
39  relationships between VOM objects and VPP entities is not always 1:1. Some
40  effort has been made to construct a higher level, more abstract API to VPP
41  programming*.
42 The client programming model is simple (or at least I intended it to be..). The
43 client deals in ‘desired’ state, that is, it expresses the objects it wants to
44 exists (in VPP) and the properties that the object should have, i.e**;
45     Interface af1(“my-af-packet-1”, AFPACKET, admin::UP);
46 Then the client ‘writes’ this object into the ‘model’
47     OM::write(“clients-thing-1”, af1);
48
49 “clients-thing-1” is a description of the entity within the client’s domain that
50 ‘owns’ (or has locked or has a reference to) the VOM object. There can be many
51 owners of each VOM object. It will be the last owner’s update that will be
52 programmed in VPP. This model means that the client is not burdened with
53 maintaining which of its objects have created which VOM objects. If the client
54 is itself driven by a CRUD API, then create notifications are implemented as
55  above. Update notifications add two extra statements;
56     OM::mark(“clients-thing-1”);
57     … do writes ….
58     OM::sweep(“clients-thing-1”);
59 These ‘mark’ and ‘sweep’ statements are indications to OM that firstly, indicate
60 that all the objects owned by “clients-thing-1” are now stale, i.e that the
61 client may no longer need them. If one of the subsequent writes should update a
62 stale object, then it is no longer stale. The sweep statement will ‘remove’ all
63 the remaining stale objects. In this model, the client does not need to maintain
64 the mapping of VOM objects to its own objects – it can simply express what it
65 needs now.
66 The delete notification is simply:
67      OM::remove(“clients-thing-1”);
68 Which will remove all the objects in VOM that are owned by “clients-thing-1”.
69 Where ‘remove’ in this sense means unlock and unreference, the VOM object, and
70 VPP state, will only be truly removed once there are no more owners. This is
71 equivalent to a mark & sweep with no intermediate writes.
72
73 To provide this client side model the VOM is a stateful library, meaning that
74 for each entity it creates in VPP, VOM maintains its own representation of that
75 object. VOM can therefore be memory hungry. The desired state is expressed by
76 the client, the ‘actual’ state is maintained by VOM. VOM will consolidate the
77 two states when the client writes to the OM and thus issue VPP only the changes
78 required.
79
80 The concepts of ownership and statefulness also allow the support for HA
81 scenarios.
82 VPP restart: When VPP restarts, VOM will reconnect and ‘replay’ its state, in
83 dependency order, to VPP. The client does not need to regenerate its desired
84 state.
85 Client restart: when the client restarts, VOM will read/dump the current state
86 of all VPP objects and store them in the OM owned by the special owner “boot”.
87 As the client reprogrammes its desired state, objects will become owned by both
88 the boot process and the client. At the point in time, as determined by the
89 client, all stale state, that owned only by boot, can be purged. Hence the
90 system reaches the correct final state, with no interruption to VPP forwarding.
91
92
93 Basic Design:
94
95 Each object in VOM (i.e. an interface, route, bridge-domain, etc) is stored in a
96 per-type object database, with an object-type specific key. This ‘singular’ DB
97 has a value-type of a weak pointer to the object. I use the term ‘singular’ to
98 refer to the instance of the object stored in these databases, to be distinct
99 from the instances the client constructs to represent desired state.
100 The ‘client’ DB maintains the mapping of owner to object. The value type of the
101 client DB is a shared pointer to the singular instance of the owned object.
102 Once all the owners are gone, and all the shared pointers are destroyed, the
103 singular instance is also destroyed.
104
105 Each VOM object has some basic behaviour:
106   update: issue to VPP an update to this object’s state. This could include the
107           create
108   sweep: delete the VPP entity – called when the object is destroyed.
109   replay: issue to VPP all the commands needed to re-programme (VPP restart HA
110           scenario)
111   populate: read state from VPP and add it to the OM (client restart HA
112 scenario)
113
114 The object code is boiler-plate, in some cases (like the ACLs) even template.
115 The objects are purposefully left as simple, functionality free as possible.
116
117 Communication with VPP is through a ‘queue’ of ‘commands’. A command is
118 essentially an object wrapper around a VPP binary API call (although we do use
119 the VAPI C++ bindings too). Commands come in three flavours:
120   RPC: do this; done.
121   DUMP: give me all of these things; here you go
122   EVENT; tell me about these events; here’s one …. Here’s one…. Oh here’s
123          another….. etc.
124
125 RPC and DUMP commands are handled synchronously. Therefore on return from
126 OM::write(…) VPP has been issued with the request and responded. EVENTs are
127 asynchronous and will be delivered to the listeners in a different thread – so
128 beware!!
129
130 * As such VOM provides some level of insulation to the changes to the VPP
131   binary API.
132 ** some of the type names are shorten for brevity’s sake.
133
134 */
135 namespace VOM {
136 /**
137  * The interface to writing objects into VPP OM.
138  */
139 class OM
140 {
141 public:
142   /**
143    * A class providing the RAII pattern for mark and sweep
144    */
145   class mark_n_sweep
146   {
147   public:
148     /**
149      * Constructor - will call mark on the key
150      */
151     mark_n_sweep(const client_db::key_t& key);
152
153     /**
154      * Destructor - will call sweep on the key
155      */
156     ~mark_n_sweep();
157
158   private:
159     /**
160      * no copies
161      */
162     mark_n_sweep(const mark_n_sweep& ms) = delete;
163
164     /**
165      * The client whose state we are guarding.
166      */
167     client_db::key_t m_key;
168   };
169
170   /**
171    * Init
172    */
173   static void init();
174
175   /**
176    * populate the OM with state read from HW.
177    */
178   static void populate(const client_db::key_t& key);
179
180   /**
181    * Mark all state owned by this key as stale
182    */
183   static void mark(const client_db::key_t& key);
184
185   /**
186    * Sweep all the key's objects that are stale
187    */
188   static void sweep(const client_db::key_t& key);
189
190   /**
191    * Replay all of the objects to HW.
192    */
193   static void replay(void);
194
195   /**
196    * Make the State in VPP reflect the expressed desired state.
197    *  But don't call the HW - use this whilst processing dumped
198    *  data from HW
199    */
200   template <typename OBJ>
201   static rc_t commit(const client_db::key_t& key, const OBJ& obj)
202   {
203     rc_t rc = rc_t::OK;
204
205     HW::disable();
206     rc = OM::write(key, obj);
207     HW::enable();
208
209     return (rc);
210   }
211
212   /**
213    * Make the State in VPP reflect the expressed desired state.
214    *  After processing all the objects in the queue, in FIFO order,
215    *  any remaining state owned by the client_db::key_t is purged.
216    * This is a template function so the object's update() function is
217    * always called with the derived type.
218    */
219   template <typename OBJ>
220   static rc_t write(const client_db::key_t& key, const OBJ& obj)
221   {
222     rc_t rc = rc_t::OK;
223
224     /*
225      * Find the singular instance another owner may have created.
226      * this always returns something.
227      */
228     std::shared_ptr<OBJ> inst = obj.singular();
229
230     /*
231      * Update the existing object with the new desired state
232      */
233     inst->update(obj);
234
235     /*
236      * Find if the object already stored on behalf of this key.
237      * and mark them stale
238      */
239     object_ref_list& objs = m_db->find(key);
240
241     /*
242      * Iterate through this list to find a matchin' object
243      * to the one requested.
244      */
245     auto match_ptr = [inst](const object_ref& oref) {
246       return (inst == oref.obj());
247     };
248     auto it = std::find_if(objs.begin(), objs.end(), match_ptr);
249
250     if (it != objs.end()) {
251       /*
252        * yes, this key already owns this object.
253        */
254       it->clear();
255     } else {
256       /*
257        * Add the singular instance to the owners list
258        */
259       objs.insert(object_ref(inst));
260     }
261
262     return (HW::write());
263   }
264
265   /**
266    * Remove all object in the OM referenced by the key
267    */
268   static void remove(const client_db::key_t& key);
269
270   /**
271    * Print each of the object in the DB into the stream provided
272    */
273   static void dump(const client_db::key_t& key, std::ostream& os);
274
275   /**
276    * Print each of the KEYS
277    */
278   static void dump(std::ostream& os);
279
280   /**
281    * Class definition for listeners to OM events
282    */
283   class listener
284   {
285   public:
286     listener() = default;
287     virtual ~listener() = default;
288
289     /**
290      * Handle a populate event
291      */
292     virtual void handle_populate(const client_db::key_t& key) = 0;
293
294     /**
295      * Handle a replay event
296      */
297     virtual void handle_replay() = 0;
298
299     /**
300      * Get the sortable Id of the listener
301      */
302     virtual dependency_t order() const = 0;
303
304     /**
305      * less than operator for set sorting
306      */
307     bool operator<(const listener& listener) const
308     {
309       return (order() < listener.order());
310     }
311   };
312
313   /**
314    * Register a listener of events
315    */
316   static bool register_listener(listener* listener);
317
318 private:
319   /**
320    * Database of object state created for each key
321    */
322   static client_db* m_db;
323
324   /**
325    * Comparator to keep the pointers to listeners in sorted order
326    */
327   struct listener_comparator_t
328   {
329     bool operator()(const listener* l1, const listener* l2) const
330     {
331       return (l1->order() < l2->order());
332     }
333   };
334
335   /**
336    * convenient typedef for the sorted set of listeners
337    */
338   typedef std::multiset<listener*, listener_comparator_t> listener_list;
339
340   /**
341    * The listeners for events
342    */
343   static std::unique_ptr<listener_list> m_listeners;
344 };
345 }
346
347 /*
348  * fd.io coding-style-patch-verification: ON
349  *
350  * Local Variables:
351  * eval: (c-set-style "mozilla")
352  * End:
353  */
354
355 #endif