use super::*;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CoreRole {
GeneralPurpose,
Control,
Worker,
Storage,
Compute,
Custom(String),
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct ResourceProfile {
cpu_cores: Option<u16>,
memory_bytes: Option<u64>,
gpu_count: u16,
}
impl ResourceProfile {
pub fn new(cpu_cores: Option<u16>, memory_bytes: Option<u64>, gpu_count: u16) -> Self {
Self {
cpu_cores,
memory_bytes,
gpu_count,
}
}
pub fn cpu_cores(&self) -> Option<u16> {
self.cpu_cores
}
pub fn memory_bytes(&self) -> Option<u64> {
self.memory_bytes
}
pub fn gpu_count(&self) -> u16 {
self.gpu_count
}
fn validate(&self) -> ContractResult<()> {
if self.cpu_cores == Some(0) || self.memory_bytes == Some(0) {
return Err(ContractError::InvalidValue {
field: "resources",
reason: "known CPU and memory values must be greater than zero",
});
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum WorkloadClass {
#[default]
General,
Interactive,
Batch,
Compute,
Io,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SchedulingProfile {
weight: u16,
priority: i16,
max_concurrency: u32,
available: bool,
workload: WorkloadClass,
affinity: BTreeSet<String>,
}
impl SchedulingProfile {
pub fn new(
weight: u16,
priority: i16,
max_concurrency: u32,
workload: WorkloadClass,
) -> ContractResult<Self> {
if weight == 0 || max_concurrency == 0 {
return Err(ContractError::InvalidValue {
field: "scheduling",
reason: "weight and max concurrency must be greater than zero",
});
}
Ok(Self {
weight,
priority,
max_concurrency,
available: true,
workload,
affinity: BTreeSet::new(),
})
}
pub fn with_availability(mut self, available: bool) -> Self {
self.available = available;
self
}
pub fn with_affinity(mut self, affinity: impl Into<String>) -> ContractResult<Self> {
let affinity = affinity.into();
validate_text("scheduling.affinity", &affinity, 128)?;
self.affinity.insert(affinity);
Ok(self)
}
pub fn weight(&self) -> u16 {
self.weight
}
pub fn priority(&self) -> i16 {
self.priority
}
pub fn max_concurrency(&self) -> u32 {
self.max_concurrency
}
pub fn is_available(&self) -> bool {
self.available
}
pub fn workload(&self) -> WorkloadClass {
self.workload
}
pub fn affinity(&self) -> &BTreeSet<String> {
&self.affinity
}
pub(crate) fn validate(&self) -> ContractResult<()> {
if self.weight == 0 || self.max_concurrency == 0 {
return Err(ContractError::InvalidValue {
field: "scheduling",
reason: "weight and max concurrency must be greater than zero",
});
}
for affinity in &self.affinity {
validate_text("scheduling.affinity", affinity, 128)?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CoreProfile {
role: CoreRole,
service_id: ServiceId,
capabilities: BTreeSet<CapabilityId>,
leadership: LeadershipRequirement,
resources: ResourceProfile,
scheduling: SchedulingProfile,
}
impl CoreProfile {
pub fn new(
role: CoreRole,
service_id: ServiceId,
capabilities: impl IntoIterator<Item = CapabilityId>,
leadership: LeadershipRequirement,
resources: ResourceProfile,
scheduling: SchedulingProfile,
) -> ContractResult<Self> {
if leadership.service_id() != &service_id {
return Err(ContractError::InvalidValue {
field: "core_profile.leadership",
reason: "leadership must be scoped to the profile service",
});
}
let profile = Self {
role,
service_id,
capabilities: capabilities.into_iter().collect(),
leadership,
resources,
scheduling,
};
profile.validate()?;
Ok(profile)
}
pub fn role(&self) -> &CoreRole {
&self.role
}
pub fn service_id(&self) -> &ServiceId {
&self.service_id
}
pub fn capabilities(&self) -> &BTreeSet<CapabilityId> {
&self.capabilities
}
pub fn leadership(&self) -> &LeadershipRequirement {
&self.leadership
}
pub fn resources(&self) -> &ResourceProfile {
&self.resources
}
pub fn scheduling(&self) -> &SchedulingProfile {
&self.scheduling
}
pub(crate) fn validate(&self) -> ContractResult<()> {
if let CoreRole::Custom(role) = &self.role {
validate_text("core_profile.role", role, 128)?;
}
self.leadership.validate()?;
self.resources.validate()?;
self.scheduling.validate()?;
if self.leadership.service_id() != &self.service_id {
return Err(ContractError::InvalidValue {
field: "core_profile.leadership",
reason: "leadership must be scoped to the profile service",
});
}
Ok(())
}
}