blob: d0f61d5182f2410861fbfb2554843a5a3db8d0eb [file] [log] [blame]
/*
* Copyright 2023 Google LLC
*
* 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 THIRD_PARTY_ECCLESIA_LIB_REDFISH_EVENT_SERVER_SUBSCRIPTION_H_
#define THIRD_PARTY_ECCLESIA_LIB_REDFISH_EVENT_SERVER_SUBSCRIPTION_H_
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <unordered_set>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "absl/types/span.h"
#include "nlohmann/json.hpp"
namespace ecclesia {
// Represents an event source identifier
struct EventSourceId {
// Enum representing the type of event source
enum class Type : uint8_t {
// D-Bus objects as event sources
kDbusObjects = 0,
// Socket IO as event sources
kSocketIO = 1,
// File IO as event sources
kFileIO = 2
};
EventSourceId(absl::string_view key_in, Type type_in)
: key(key_in), type(type_in) {}
template <typename H>
friend H AbslHashValue(H h, const EventSourceId &n) {
return H::combine(std::move(h), n.key);
}
bool operator==(const EventSourceId &other) const {
return key == other.key && type == other.type;
}
bool operator!=(const EventSourceId &other) const {
return !(*this == other);
}
// Converts EventSourceId to JSON format
nlohmann::json ToJSON() const;
// Converts EventSourceId to string format
std::string ToString() const;
// Unique identifier for the event source
std::string key;
// Type of event source, represented by the Type enum
Type type;
};
// Uniquely identifies subscription.
struct SubscriptionId {
explicit SubscriptionId(size_t subscription_id_in)
: subscription_id(subscription_id_in) {}
template <typename H>
friend H AbslHashValue(H h, const SubscriptionId &n) {
return H::combine(std::move(h), n.subscription_id);
}
bool operator==(const SubscriptionId &other) const {
return subscription_id == other.subscription_id;
}
bool operator!=(const SubscriptionId &other) const {
return !(*this == other);
}
size_t Id() const { return subscription_id; }
size_t subscription_id;
};
// EventId uniquely identifies an event at any given point in time from any
// event source.
//
// EventId satisfies the contract of uniqueness through `redfish_event_id` and
// monotonicity through `timestamp`. `EventId` has an association with
// `SubscriptionId` to allow subscription service to group events for a
// subscriber, useful to satisfy lossless-events contract.
struct EventId {
EventId(const SubscriptionId &subscription_id_in,
const EventSourceId &source_id_in, absl::Time timestamp_in);
// Converts EventId to string format
std::string ToString() const;
// Converts EventId to JSON format
nlohmann::json ToJSON() const;
template <typename H>
friend H AbslHashValue(H h, const EventId &n) {
return H::combine(std::move(h), n.redfish_event_id);
}
bool operator==(EventId const& event_id) const {
return ((event_id.source_id == this->source_id) &&
(event_id.timestamp == this->timestamp) &&
(event_id.subscription_id == this->subscription_id) &&
(event_id.redfish_event_id == this->redfish_event_id));
}
bool operator!=(EventId const& event_id) const {
return !(*this == event_id);
}
EventSourceId source_id;
absl::Time timestamp;
SubscriptionId subscription_id;
size_t redfish_event_id;
};
// Structure representing a Trigger, which defines event triggering conditions
struct Trigger {
using EventSourceToUri =
absl::flat_hash_map<EventSourceId, std::vector<std::string>>;
// Static method to create a Trigger object from raw data
static absl::StatusOr<Trigger> Create(const nlohmann::json &trigger_json);
// Constructor for Trigger
explicit Trigger(absl::string_view id_in,
absl::flat_hash_set<std::string> origin_resources_in,
absl::string_view predicate_in = "", bool mask_in = false);
// Trigger id
std::string id;
// Converts Trigger to JSON format
nlohmann::json ToJSON() const;
// Converts Trigger to string format
std::string ToString() const;
// List of origin resources associated with the Trigger
absl::flat_hash_set<std::string> origin_resources;
// Predicate expression for determining when to trigger the event
std::string predicate;
// Flag indicating whether to mask event source.
bool mask;
};
// Structure representing a subscription
struct SubscriptionContext {
SubscriptionContext(
const SubscriptionId &subscription_id_in,
const absl::flat_hash_map<EventSourceId, absl::flat_hash_set<std::string>>
&event_source_to_uris_in,
absl::flat_hash_map<std::string, Trigger> id_to_triggers_in,
std::function<void(const nlohmann::json &)> &&on_event_callback_in);
// Unique identifier for the subscription
SubscriptionId subscription_id;
// Map of event source IDs to corresponding URIs.
// This map is used to process incoming events by looking up the URI an event
// is associated with and then the URI is used to create an internal query
// that builds `OriginOfCondtion`.
absl::flat_hash_map<EventSourceId, absl::flat_hash_set<std::string>>
event_source_to_uri;
// Map of trigger IDs to corresponding Trigger objects
// This map associates each trigger ID with the corresponding Trigger
// object, enabling efficient lookup and management of triggers for a given
// subscription.
absl::flat_hash_map<std::string, Trigger> id_to_triggers;
// Event callback to be invoked for events related to this subscription
// This callback function is defined for each subscription and will be
// called whenever an event occurs that matches the criteria defined by the
// subscription's triggers. The callback function receives the event data as
// its argument.
std::function<void(const nlohmann::json &)> on_event_callback;
// Peer's authenticated Redfish privileges
std::unordered_set<std::string> privileges;
};
// Interface for an event store.
// EventStore stores events in an overwriting circular buffer and allows
// looking up the events queued since a specific event_id to honor the
// lossless eventing contract of subscription service.
//
// The event store shall be thread safe.
class EventStore {
public:
virtual ~EventStore() = default;
// Adds a new event to the overwriting circular buffer.
virtual void AddNewEvent(const EventId &event_id,
const nlohmann::json &event) = 0;
// Retrieves all events that have been added since (but not including) the
// given redfish event id in a chronological order determined by
// `event_id.timestamp`.
// If `redfish_event_id` is std::nullopt value, all events shall be returned
// in chronological order.
virtual std::vector<nlohmann::json> GetEventsSince(
std::optional<size_t> redfish_event_id) = 0;
// Retrieves the event with the given event id.
virtual nlohmann::json GetEvent(const EventId &event_id) = 0;
virtual std::vector<nlohmann::json> GetEventsBySubscriptionId(
size_t subscription_id) = 0;
// Report all events in the JSON format.
virtual nlohmann::json ToJSON() = 0;
// Report all events in the string format.
virtual std::string ToString() = 0;
// Cleat all events from the store.
virtual void Clear() = 0;
};
// Interface for a subscription store
class SubscriptionStore {
public:
virtual ~SubscriptionStore() = default;
// Adds a new subscription with the given subscription ID and event source IDs
virtual absl::Status AddNewSubscription(
std::unique_ptr<SubscriptionContext> subscription_context) = 0;
// Deletes the subscription with the given subscription ID
virtual void DeleteSubscription(const SubscriptionId &subscription_id) = 0;
virtual absl::StatusOr<const SubscriptionContext *> GetSubscription(
const SubscriptionId &subscription_id) = 0;
// Retrieves the subscriptions associated with the given event source ID
virtual absl::StatusOr<absl::Span<const ecclesia::SubscriptionContext *const>>
GetSubscriptionsByEventSourceId(const EventSourceId &source_id) = 0;
// Converts SubscriptionStore to JSON format
virtual nlohmann::json ToJSON() = 0;
// Converts SubscriptionStore to string format
virtual std::string ToString() = 0;
};
// Interface for a SubscriptionService backend.
// This ensures that SubscriptionService interoperates with different Redfish
// backends by standardizing the interface to subscribe and query Redfish
// resources.
class SubscriptionBackend {
public:
// Callback type for Redfish queries.
using QueryCallback =
std::function<void(const absl::Status & /*Status Code*/,
const nlohmann::json & /*Redfish Resource json_str*/)>;
// Callback type for Subscribe requests to SubscriptionBackend.
// This callback shall be invoked with collection of EventSourceId objects on
// a successful completion or an error in the status code on failed
// subscription.
using SubscribeCallback =
std::function<void(const absl::Status & /*Status Code*/,
const std::vector<EventSourceId> & /*EventSources*/)>;
// Destructor for SubscriptionBackend
virtual ~SubscriptionBackend() = default;
// Performs a Redfish query at the given URL and invokes the provided callback
// with the query result.
// `peer_privileges` is the peer's Redfish privileges that will be used to
// authorize the queried resources.
virtual absl::Status Query(
absl::string_view url, QueryCallback &&query_callback,
const std::unordered_set<std::string> &peer_privileges) = 0;
// Subscribes to Redfish events for the given URL and invokes the callback
// post subscription.
// `peer_privileges` is the peer's Redfish privileges that will be used to
// authorize the subscribed resources.
virtual absl::Status Subscribe(
absl::string_view url, SubscribeCallback &&subscribe_callback,
const std::unordered_set<std::string> &peer_privileges) = 0;
};
// Interface for a subscription service
class SubscriptionService {
public:
virtual ~SubscriptionService() = default;
// Creates a new subscription and returns the subscription ID of the newly
// created subscription.
// Note: the implementation must guarantee that `on_event_callback` will only
// be called once at a time. No parallel call will be allowed!
virtual void CreateSubscription(
const nlohmann::json &request,
const std::unordered_set<std::string> &peer_privileges,
std::function<void(const absl::StatusOr<SubscriptionId> &)>
on_subscribe_callback,
std::function<void(const nlohmann::json &)> on_event_callback) = 0;
// Deletes the subscription with the given subscription ID
virtual void DeleteSubscription(const SubscriptionId &subscription_id) = 0;
// Retrieves all subscriptions managed by the service.
virtual absl::Span<const SubscriptionContext> GetAllSubscriptions() = 0;
// Returns all subscriptions in a JSON format.
virtual nlohmann::json GetSubscriptionsToJSON() = 0;
// Returns all subscriptions in a string format.
virtual std::string GetSubscriptionsToString() = 0;
// Returns all subscriptions in a JSON format.
virtual nlohmann::json GetEventsToJSON() = 0;
// Returns all subscriptions in a string format.
virtual std::string GetEventsToString() = 0;
// clear/flush all events in the event store.
virtual void ClearEventStore() = 0;
virtual nlohmann::json GetEventsBySubscriptionIdToJSON(
size_t subscriber_id) = 0;
// Invoked by an EventSource to notify SubscriptionService about an event
// occurrence.
// Returns error if notification cannot be processed, typically used to
// indicate event source that subscription is deleted and the source should
// disable event listener.
virtual absl::Status Notify(EventSourceId event_source_id,
const absl::Status &status) = 0;
// Invoked by an EventSource to notify SubscriptionService about an event
// occurrence along with providing data associated with the event.
// This method is preferred when the event source is capable of constructing
// entire OriginOfCondition for event.
virtual absl::Status NotifyWithData(EventSourceId key, absl::Status status,
const nlohmann::json &data) = 0;
};
} // namespace ecclesia
#endif // THIRD_PARTY_ECCLESIA_LIB_REDFISH_EVENT_SERVER_SUBSCRIPTION_H_