summaryrefslogtreecommitdiffstats
path: root/src/vnet/lldp/lldp_doc.md
AgeCommit message (Expand)AuthorFilesLines
2017-07-05Add API support for LLDP config/interface setSteve Shin1-4/+6
2016-12-28Reorganize source tree to use single autotools instanceDamjan Marion1-0/+84
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
/*
 * Copyright (c) 2017 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef __VOM_OM_H__
#define __VOM_OM_H__

#include <algorithm>
#include <memory>
#include <set>

#include "vom/client_db.hpp"
#include "vom/hw.hpp"

/**

The VPP Object Model (VOM) library.

Before we begin, a glossary of terms:
   - Agent or client: A user mode process that links to and uses the VOM library
     to programme VPP
   - VPP: A running instance of VPP
   - High Availability (HA): Scenarios where the client and/or VPP restart with
     minimal service interruption.
   - CReate, Update, Delete (CRUD): An API style where the producer issues
     notifications to changes to objects

The VOM is a C++ library that models entities in VPP as C++ classes. The
 relationships between VOM objects and VPP entities is not always 1:1. Some
 effort has been made to construct a higher level, more abstract API to VPP
 programming*.
The client programming model is simple (or at least I intended it to be..). The
client deals in ‘desired’ state, that is, it expresses the objects it wants to
exists (in VPP) and the properties that the object should have, i.e**;
    Interface af1(“my-af-packet-1”, AFPACKET, admin::UP);
Then the client ‘writes’ this object into the ‘model’
    OM::write(“clients-thing-1”, af1);

“clients-thing-1” is a description of the entity within the client’s domain that
‘owns’ (or has locked or has a reference to) the VOM object. There can be many
owners of each VOM object. It will be the last owner’s update that will be
programmed in VPP. This model means that the client is not burdened with
maintaining which of its objects have created which VOM objects. If the client
is itself driven by a CRUD API, then create notifications are implemented as
 above. Update notifications add two extra statements;
    OM::mark(“clients-thing-1”);
    … do writes ….
    OM::sweep(“clients-thing-1”);
These ‘mark’ and ‘sweep’ statements are indications to OM that firstly, indicate
that all the objects owned by “clients-thing-1” are now stale, i.e that the
client may no longer need them. If one of the subsequent writes should update a
stale object, then it is no longer stale. The sweep statement will ‘remove’ all
the remaining stale objects. In this model, the client does not need to maintain
the mapping of VOM objects to its own objects – it can simply express what it
needs now.
The delete notification is simply:
     OM::remove(“clients-thing-1”);
Which will remove all the objects in VOM that are owned by “clients-thing-1”.
Where ‘remove’ in this sense means unlock and unreference, the VOM object, and
VPP state, will only be truly removed once there are no more owners. This is
equivalent to a mark & sweep with no intermediate writes.

To provide this client side model the VOM is a stateful library, meaning that
for each entity it creates in VPP, VOM maintains its own representation of that
object. VOM can therefore be memory hungry. The desired state is expressed by
the client, the ‘actual’ state is maintained by VOM. VOM will consolidate the
two states when the client writes to the OM and thus issue VPP only the changes
required.

The concepts of ownership and statefulness also allow the support for HA
scenarios.
VPP restart: When VPP restarts, VOM will reconnect and ‘replay’ its state, in
dependency order, to VPP. The client does not need to regenerate its desired
state.
Client restart: when the client restarts, VOM will read/dump the current state
of all VPP objects and store them in the OM owned by the special owner “boot”.
As the client reprogrammes its desired state, objects will become owned by both
the boot process and the client. At the point in time, as determined by the
client, all stale state, that owned only by boot, can be purged. Hence the
system reaches the correct final state, with no interruption to VPP forwarding.


Basic Design:

Each object in VOM (i.e. an interface, route, bridge-domain, etc) is stored in a
per-type object database, with an object-type specific key. This ‘singular’ DB
has a value-type of a weak pointer to the object. I use the term ‘singular’ to
refer to the instance of the object stored in these databases, to be distinct
from the instances the client constructs to represent desired state.
The ‘client’ DB maintains the mapping of owner to object. The value type of the
client DB is a shared pointer to the singular instance of the owned object.
Once all the owners are gone, and all the shared pointers are destroyed, the
singular instance is also destroyed.

Each VOM object has some basic behaviour:
  update: issue to VPP an update to this object’s state. This could include the
          create
  sweep: delete the VPP entity – called when the object is destroyed.
  replay: issue to VPP all the commands needed to re-programme (VPP restart HA
          scenario)
  populate: read state from VPP and add it to the OM (client restart HA
scenario)

The object code is boiler-plate, in some cases (like the ACLs) even template.
The objects are purposefully left as simple, functionality free as possible.

Communication with VPP is through a ‘queue’ of ‘commands’. A command is
essentially an object wrapper around a VPP binary API call (although we do use
the VAPI C++ bindings too). Commands come in three flavours:
  RPC: do this; done.
  DUMP: give me all of these things; here you go
  EVENT; tell me about these events; here’s one …. Here’s one…. Oh here’s
         another….. etc.

RPC and DUMP commands are handled synchronously. Therefore on return from
OM::write(…) VPP has been issued with the request and responded. EVENTs are
asynchronous and will be delivered to the listeners in a different thread – so
beware!!

* As such VOM provides some level of insulation to the changes to the VPP
  binary API.
** some of the type names are shorten for brevity’s sake.

*/
namespace VOM {
/**
 * The interface to writing objects into VPP OM.
 */
class OM
{
public:
  /**
   * A class providing the RAII pattern for mark and sweep
   */
  class mark_n_sweep
  {
  public:
    /**
     * Constructor - will call mark on the key
     */
    mark_n_sweep(const client_db::key_t& key);

    /**
     * Destructor - will call sweep on the key
     */
    ~mark_n_sweep();

  private:
    /**
     * no copies
     */
    mark_n_sweep(const mark_n_sweep& ms) = delete;

    /**
     * The client whose state we are guarding.
     */
    client_db::key_t m_key;
  };

  /**
   * Init
   */
  static void init();

  /**
   * populate the OM with state read from HW.
   */
  static void populate(const client_db::key_t& key);

  /**
   * Mark all state owned by this key as stale
   */
  static void mark(const client_db::key_t& key);

  /**
   * Sweep all the key's objects that are stale
   */
  static void sweep(const client_db::key_t& key);

  /**
   * Replay all of the objects to HW.
   */
  static void replay(void);

  /**
   * Make the State in VPP reflect the expressed desired state.
   *  But don't call the HW - use this whilst processing dumped
   *  data from HW
   */
  template <typename OBJ>
  static rc_t commit(const client_db::key_t& key, const OBJ& obj)
  {
    rc_t rc = rc_t::OK;

    HW::disable();
    rc = OM::write(key, obj);
    HW::enable();

    return (rc);
  }

  /**
   * Make the State in VPP reflect the expressed desired state.
   *  After processing all the objects in the queue, in FIFO order,
   *  any remaining state owned by the client_db::key_t is purged.
   * This is a template function so the object's update() function is
   * always called with the derived type.
   */
  template <typename OBJ>
  static rc_t write(const client_db::key_t& key, const OBJ& obj)
  {
    rc_t rc = rc_t::OK;

    /*
     * Find the singular instance another owner may have created.
     * this always returns something.
     */
    std::shared_ptr<OBJ> inst = obj.singular();

    /*
     * Update the existing object with the new desired state
     */
    inst->update(obj);

    /*
     * Find if the object already stored on behalf of this key.
     * and mark them stale
     */
    object_ref_list& objs = m_db->find(key);

    /*
     * Iterate through this list to find a matchin' object
     * to the one requested.
     */
    auto match_ptr = [inst](const object_ref& oref) {
      return (inst == oref.obj());
    };
    auto it = std::find_if(objs.begin(), objs.end(), match_ptr);

    if (it != objs.end()) {
      /*
       * yes, this key already owns this object.
       */
      it->clear();
    } else {
      /*
       * Add the singular instance to the owners list
       */
      objs.insert(object_ref(inst));
    }

    return (HW::write());
  }

  /**
   * Remove all object in the OM referenced by the key
   */
  static void remove(const client_db::key_t& key);

  /**
   * Print each of the object in the DB into the stream provided
   */
  static void dump(const client_db::key_t& key, std::ostream& os);

  /**
   * Print each of the KEYS
   */
  static void dump(std::ostream& os);

  /**
   * Class definition for listeners to OM events
   */
  class listener
  {
  public:
    listener() = default;
    virtual ~listener() = default;

    /**
     * Handle a populate event
     */
    virtual void handle_populate(const client_db::key_t& key) = 0;

    /**
     * Handle a replay event
     */
    virtual void handle_replay() = 0;

    /**
     * Get the sortable Id of the listener
     */
    virtual dependency_t order() const = 0;

    /**
     * less than operator for set sorting
     */
    bool operator<(const listener& listener) const
    {
      return (order() < listener.order());
    }
  };

  /**
   * Register a listener of events
   */
  static bool register_listener(listener* listener);

private:
  /**
   * Database of object state created for each key
   */
  static client_db* m_db;

  /**
   * Comparator to keep the pointers to listeners in sorted order
   */
  struct listener_comparator_t
  {
    bool operator()(const listener* l1, const listener* l2) const
    {
      return (l1->order() < l2->order());
    }
  };

  /**
   * convenient typedef for the sorted set of listeners
   */
  typedef std::multiset<listener*, listener_comparator_t> listener_list;

  /**
   * The listeners for events
   */
  static std::unique_ptr<listener_list> m_listeners;
};
}

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "mozilla")
 * End:
 */

#endif