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