use libc;
use std;
use std::collections::BTreeSet;
use std::path::PathBuf;
use entity::server::ServerId;
use {Error, ErrorKind, Result};
pub type DeviceId = String;
pub type DeviceNo = u32;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeviceSummary {
pub id: DeviceId,
#[serde(default)]
pub server: Option<ServerId>,
#[serde(rename = "type")]
pub kind: DeviceKind,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum DeviceKind {
Virtual,
Memory,
File,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Device {
Virtual(VirtualDevice),
Memory(MemoryDevice),
File(FileDevice),
}
impl Default for Device {
fn default() -> Self {
Device::Memory(MemoryDevice {
id: String::new(),
capacity: 0,
seqno: 0,
weight: Default::default(),
server: String::new(),
})
}
}
impl Device {
pub fn to_summary(&self) -> DeviceSummary {
DeviceSummary {
id: self.id().to_owned(),
server: self.server().cloned(),
kind: self.kind(),
}
}
pub fn kind(&self) -> DeviceKind {
match *self {
Device::Virtual(_) => DeviceKind::Virtual,
Device::Memory(_) => DeviceKind::Memory,
Device::File(_) => DeviceKind::File,
}
}
pub fn is_virtual(&self) -> bool {
if let Device::Virtual(_) = *self {
true
} else {
false
}
}
pub fn server(&self) -> Option<&ServerId> {
match *self {
Device::Memory(ref d) => Some(&d.server),
Device::File(ref d) => Some(&d.server),
_ => None,
}
}
pub fn id(&self) -> &DeviceId {
match *self {
Device::Virtual(ref d) => &d.id,
Device::Memory(ref d) => &d.id,
Device::File(ref d) => &d.id,
}
}
pub fn set_seqno(&mut self, seqno: u32) {
match *self {
Device::Virtual(ref mut d) => d.seqno = seqno,
Device::Memory(ref mut d) => d.seqno = seqno,
Device::File(ref mut d) => d.seqno = seqno,
}
}
pub fn seqno(&self) -> u32 {
match *self {
Device::Virtual(ref d) => d.seqno,
Device::Memory(ref d) => d.seqno,
Device::File(ref d) => d.seqno,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VirtualDevice {
pub id: DeviceId,
#[serde(default)]
pub seqno: u32,
#[serde(default)]
pub weight: Weight,
pub children: BTreeSet<DeviceId>,
#[serde(default)]
pub policy: SegmentAllocationPolicy,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryDevice {
pub id: DeviceId,
#[serde(default)]
pub seqno: u32,
#[serde(default)]
pub weight: Weight,
pub server: ServerId,
pub capacity: u64,
}
impl MemoryDevice {
pub fn weight(&self) -> u64 {
self.weight.calculate(self.capacity)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileDevice {
pub id: DeviceId,
#[serde(default)]
pub seqno: u32,
#[serde(default)]
pub weight: Weight,
pub server: ServerId,
#[serde(default)]
pub capacity: u64,
pub filepath: PathBuf,
}
impl FileDevice {
pub fn weight(&self) -> u64 {
self.weight.calculate(self.capacity)
}
pub fn capacity(&self) -> Result<u64> {
if self.capacity == 0 {
let dir = track_assert_some!(self.filepath.parent(), ErrorKind::InvalidInput);
track!(std::fs::create_dir_all(dir).map_err(Error::from))?;
let path = track_try_unwrap!(
std::ffi::CString::new(dir.to_string_lossy().to_string()).map_err(Error::from)
);
let available_space: u64 = track!(calc_available_space(&path))?;
Ok((available_space / 100) * 99)
} else {
Ok(self.capacity)
}
}
}
#[cfg(target_os = "macos")]
fn calc_available_space(path: &std::ffi::CString) -> Result<u64> {
let mut s: libc::statfs = unsafe { std::mem::zeroed() };
let result = unsafe { libc::statfs(path.as_ptr(), (&mut s) as _) };
if result == 0 {
Ok(u64::from(s.f_bsize) * s.f_bavail)
} else {
track!(Err(Error::from(std::io::Error::last_os_error())))
}
}
#[cfg(not(target_os = "macos"))]
fn calc_available_space(path: &std::ffi::CString) -> Result<u64> {
let mut s: libc::statvfs = unsafe { std::mem::zeroed() };
let result = unsafe { libc::statvfs(path.as_ptr(), (&mut s) as _) };
if result == 0 {
Ok(s.f_bsize * s.f_bavail)
} else {
track!(Err(Error::from(std::io::Error::last_os_error())))
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Weight {
Auto,
Absolute(u64),
Relative(f64),
}
impl Weight {
pub fn calculate(&self, base: u64) -> u64 {
match *self {
Weight::Auto => base,
Weight::Absolute(v) => v,
Weight::Relative(r) => (base as f64 * r) as u64,
}
}
}
impl Default for Weight {
fn default() -> Self {
Weight::Auto
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SegmentAllocationPolicy {
#[serde(rename = "SCATTER_IF_POSSIBLE")]
ScatterIfPossible = 0,
#[serde(rename = "SCATTER")]
Scatter = 1,
#[serde(rename = "NEUTRAL")]
Neutral = 2,
#[serde(rename = "GATHER")]
Gather = 3,
#[serde(rename = "AS_EVEN_AS_POSSIBLE")]
AsEvenAsPossible = 4,
}
impl Default for SegmentAllocationPolicy {
fn default() -> Self {
SegmentAllocationPolicy::AsEvenAsPossible
}
}