#pragma once #include #include #include #include #include #include #include "common/models.h" // ───────────────────────────────────────────────────────────────────────────── // Database // Thread-safe, file-backed inventory store. // // File format (text, one record per line): // # device-inventory database // META|next_server_id|next_part_type_id|next_part_id // S|id|name|hostname|location|description // PT|id|name|description // MS|part_id|server_id|serial_or_NULL|last_updated|speed_mhz|size_mb|manufacturer|channel_config|other_info // MSLOT|part_id|server_id|serial_or_NULL|last_updated|allowed_speed_mhz|allowed_size_mb|installed_stick_id_or_NULL // CPU|part_id|server_id|serial_or_NULL|last_updated|name|manufacturer|speed_ghz|cores|threads // CPUSLOT|part_id|server_id|serial_or_NULL|last_updated|form_factor|installed_cpu_id_or_NULL // DISK|part_id|server_id|serial_or_NULL|last_updated|manufacturer|model|generation|conn_type|conn_speed|disk_speed|disk_size|disk_type|age_years|partition_ids_LS|partition_sizes_LS|vm_hostnames_LS|vm_server_ids_LS // NIC|part_id|server_id|serial_or_NULL|last_updated|manufacturer|model|age_years|conn_type|conn_speed|mac|ips_LS|dhcp // // Lists within a field use LS ('\x02'). Pipe and backslash in text fields are // escaped via escape()/unescape(). // ───────────────────────────────────────────────────────────────────────────── class Database { public: explicit Database(std::string path); // Persist current inventory to disk (acquires lock) bool save(); // Load (or reload) inventory from disk bool load(); // ── Servers ─────────────────────────────────────────────────────────────── Server add_server(std::string name, std::string hostname, std::string location, std::string description); std::vector list_servers() const; std::optional get_server(uint32_t id) const; bool edit_server(uint32_t id, const std::string& field, const std::string& value); bool remove_server(uint32_t id); // ── Part types (labels) ─────────────────────────────────────────────────── PartType add_part_type(std::string name, std::string description); std::vector list_part_types() const; std::optional get_part_type(uint32_t id) const; bool edit_part_type(uint32_t id, const std::string& field, const std::string& value); bool remove_part_type(uint32_t id); // ── Typed parts ─────────────────────────────────────────────────────────── // kv is a map of field-key → field-value strings (K_* constants from protocol.h) uint32_t add_part(const std::string& type_name, uint32_t server_id, const std::map& kv); // Insert or update: for nic match by mac, for others by serial uint32_t upsert_part(const std::string& type_name, uint32_t server_id, const std::map& kv); bool edit_part(uint32_t part_id, const std::map& kv); bool remove_part(uint32_t part_id); // Wire-row format: type_namepart_idserver_idserial_or_NULLlast_updatedk1v1... std::vector list_parts(uint32_t server_id, const std::string& type_filter = "") const; std::string get_part_row(uint32_t part_id) const; // empty = not found // Returns all NetworkCard records belonging to the given server. std::vector get_nics_for_server(uint32_t server_id) const; private: std::string path_; Inventory inv_; mutable std::mutex mu_; // Internal save without acquiring lock (caller must hold mu_) bool save_nolock(); // Internal add_part without acquiring lock uint32_t add_part_nolock(const std::string& type_name, uint32_t server_id, const std::map& kv); uint32_t alloc_part_id(); // call while holding mu_ static std::string escape(const std::string& s); static std::string unescape(const std::string& s); static void apply_base(PartBase& p, const std::map& kv); // Per-type wire-row serializers std::string serialize_memory_stick(const MemoryStick& m) const; std::string serialize_memory_slot (const MemorySlot& m) const; std::string serialize_cpu (const CPU& c) const; std::string serialize_cpu_slot (const CPUSlot& c) const; std::string serialize_disk (const Disk& d) const; std::string serialize_nic (const NetworkCard& n) const; };