<commit_before>/// Figure 8.22 The system function, without signal handling and
/// Figure 8.23 Calling the system function
///
/// $ f23-system 2>&1 | grep -v "[0-9]:" # remove all lines with times
/// normal termination, exit status = 0
/// sh: nosuchcommand: command not found
/// normal termination, exit status = 127
/// normal termination, exit status = 44

extern crate libc;
extern crate apue;
extern crate errno;

use apue::{err_sys, pr_exit, system};

fn main() {
    for cmd in ["date", "nosuchcommand", "who; exit 44"].into_iter() {
        if let Some(status) = unsafe { system(cmd) } {
            pr_exit(status);
        } else {
            err_sys(&format!("{} error", cmd));
        }
    }
}
<commit_msg>fix f23-system for linux<commit_after>/// Figure 8.22 The system function, without signal handling and
/// Figure 8.23 Calling the system function
///
/// mac only:
/// $ f23-system 2>&1 | grep -Ev "[0-9]{2}:" # remove all lines with times
/// normal termination, exit status = 0
/// sh: nosuchcommand: command not found
/// normal termination, exit status = 127
/// normal termination, exit status = 44
///
/// linux only:
/// $ f23-system 2>&1 | grep -Ev "[0-9]{2}:" # remove all lines with times
/// normal termination, exit status = 0
/// sh: 1: nosuchcommand: not found
/// normal termination, exit status = 127
/// normal termination, exit status = 44

extern crate libc;
extern crate apue;
extern crate errno;

use apue::{err_sys, pr_exit, system};

fn main() {
    for cmd in ["date", "nosuchcommand", "who; exit 44"].into_iter() {
        if let Some(status) = unsafe { system(cmd) } {
            pr_exit(status);
        } else {
            err_sys(&format!("{} error", cmd));
        }
    }
}
<|endoftext|>


<commit_before>use drawing_ctx::RsvgDrawingCtx;
use handle::*;
use node::*;
use property_bag::RsvgPropertyBag;
use state::RsvgState;

use std::rc::*;

type CNodeSetAtts = unsafe extern "C" fn (node: *const RsvgNode, node_impl: *const RsvgCNodeImpl, handle: *const RsvgHandle, pbag: *const RsvgPropertyBag);
type CNodeDraw = unsafe extern "C" fn (node: *const RsvgNode, node_impl: *const RsvgCNodeImpl, draw_ctx: *const RsvgDrawingCtx, dominate: i32);
type CNodeFree = unsafe extern "C" fn (node_impl: *const RsvgCNodeImpl);

struct CNode {
    c_node_impl: *const RsvgCNodeImpl,

    set_atts_fn: CNodeSetAtts,
    draw_fn:     CNodeDraw,
    free_fn:     CNodeFree,
}

impl NodeTrait for CNode {
    fn set_atts (&self, node: &RsvgNode, handle: *const RsvgHandle, pbag: *const RsvgPropertyBag) {
        unsafe { (self.set_atts_fn) (node as *const RsvgNode, self.c_node_impl, handle, pbag); }
    }

    fn draw (&self, node: &RsvgNode, draw_ctx: *const RsvgDrawingCtx, dominate: i32) {
        unsafe { (self.draw_fn) (node as *const RsvgNode, self.c_node_impl, draw_ctx, dominate); }
    }

    fn get_c_impl (&self) -> *const RsvgCNodeImpl {
        self.c_node_impl
    }
}

impl Drop for CNode {
    fn drop (&mut self) {
        unsafe { (self.free_fn) (self.c_node_impl); }
    }
}

#[no_mangle]
pub extern fn rsvg_rust_cnode_new (node_type:   NodeType,
                                   raw_parent:  *const RsvgNode,
                                   state:       *mut RsvgState,
                                   c_node_impl: *const RsvgCNodeImpl,
                                   set_atts_fn: CNodeSetAtts,
                                   draw_fn:     CNodeDraw,
                                   free_fn:     CNodeFree) -> *const RsvgNode {
    assert! (!state.is_null ());
    assert! (!c_node_impl.is_null ());

    let parent: Option<Weak<Node>> =
        if raw_parent.is_null () {
            None
        } else {
            let p: &RsvgNode = unsafe { & *raw_parent };
            Some (Rc::downgrade (&p.clone ()))
        };

    let cnode = CNode {
        c_node_impl: c_node_impl,
        set_atts_fn: set_atts_fn,
        draw_fn:     draw_fn,
        free_fn:     free_fn
    };

    box_node (Rc::new (Node::new (node_type,
                                  parent,
                                  state,
                                  Box::new (cnode))))
}

#[no_mangle]
pub extern fn rsvg_rust_cnode_get_impl (raw_node: *const RsvgNode) -> *const RsvgCNodeImpl {
    assert! (!raw_node.is_null ());
    let node: &RsvgNode = unsafe { & *raw_node };

    node.get_c_impl ()
}
<commit_msg>cnode.rs: Factor out function to convert a *const RsvgNode into an Option<Weak<Node>><commit_after>use drawing_ctx::RsvgDrawingCtx;
use handle::*;
use node::*;
use property_bag::RsvgPropertyBag;
use state::RsvgState;

use std::rc::*;

type CNodeSetAtts = unsafe extern "C" fn (node: *const RsvgNode, node_impl: *const RsvgCNodeImpl, handle: *const RsvgHandle, pbag: *const RsvgPropertyBag);
type CNodeDraw = unsafe extern "C" fn (node: *const RsvgNode, node_impl: *const RsvgCNodeImpl, draw_ctx: *const RsvgDrawingCtx, dominate: i32);
type CNodeFree = unsafe extern "C" fn (node_impl: *const RsvgCNodeImpl);

struct CNode {
    c_node_impl: *const RsvgCNodeImpl,

    set_atts_fn: CNodeSetAtts,
    draw_fn:     CNodeDraw,
    free_fn:     CNodeFree,
}

impl NodeTrait for CNode {
    fn set_atts (&self, node: &RsvgNode, handle: *const RsvgHandle, pbag: *const RsvgPropertyBag) {
        unsafe { (self.set_atts_fn) (node as *const RsvgNode, self.c_node_impl, handle, pbag); }
    }

    fn draw (&self, node: &RsvgNode, draw_ctx: *const RsvgDrawingCtx, dominate: i32) {
        unsafe { (self.draw_fn) (node as *const RsvgNode, self.c_node_impl, draw_ctx, dominate); }
    }

    fn get_c_impl (&self) -> *const RsvgCNodeImpl {
        self.c_node_impl
    }
}

impl Drop for CNode {
    fn drop (&mut self) {
        unsafe { (self.free_fn) (self.c_node_impl); }
    }
}

pub fn parent_ptr_to_weak (raw_parent: *const RsvgNode) -> Option<Weak<Node>> {
    if raw_parent.is_null () {
        None
    } else {
        let p: &RsvgNode = unsafe { & *raw_parent };
        Some (Rc::downgrade (&p.clone ()))
    }
}

#[no_mangle]
pub extern fn rsvg_rust_cnode_new (node_type:   NodeType,
                                   raw_parent:  *const RsvgNode,
                                   state:       *mut RsvgState,
                                   c_node_impl: *const RsvgCNodeImpl,
                                   set_atts_fn: CNodeSetAtts,
                                   draw_fn:     CNodeDraw,
                                   free_fn:     CNodeFree) -> *const RsvgNode {
    assert! (!state.is_null ());
    assert! (!c_node_impl.is_null ());

    let cnode = CNode {
        c_node_impl: c_node_impl,
        set_atts_fn: set_atts_fn,
        draw_fn:     draw_fn,
        free_fn:     free_fn
    };

    box_node (Rc::new (Node::new (node_type,
                                  parent_ptr_to_weak (raw_parent),
                                  state,
                                  Box::new (cnode))))
}

#[no_mangle]
pub extern fn rsvg_rust_cnode_get_impl (raw_node: *const RsvgNode) -> *const RsvgCNodeImpl {
    assert! (!raw_node.is_null ());
    let node: &RsvgNode = unsafe { & *raw_node };

    node.get_c_impl ()
}
<|endoftext|>


<commit_before>use base::world::{self, Chunk, GroundMaterial, PillarSection};
use base::math::*;
use glium;
use Camera;
use render::ToArr;
use std::collections::VecDeque;
use std::f32::consts;

/// Graphical representation of the `base::Chunk`.
pub struct ChunkView {
    vertices: glium::VertexBuffer<Vertex>,
    program: glium::Program,
    pillars: Vec<PillarView>,
    index_buffer: glium::index::IndexBuffer<u32>,
}
/// Calculates one Point-coordinates of a Hexagon
fn hex_corner(size: f32, i: i32) -> (f32, f32) {
    let angle_deg = 60.0 * (i as f32) + 30.0;
    let angle_rad = (consts::PI / 180.0) * angle_deg;

    (size * angle_rad.cos(), size * angle_rad.sin())
}

impl ChunkView {
    /// Creates the graphical representation of given chunk at the given chunk
    /// offset
    pub fn from_chunk<F>(chunk: &Chunk, offset: AxialPoint, facade: &F) -> Self
        where F: glium::backend::Facade
    {

        // Create one hexagon for this chunk
        let mut hexagon_vertex_buffer = VecDeque::new();
        for i in 0..6 {
            let (x, y) = hex_corner(world::HEX_OUTER_RADIUS, i);

            hexagon_vertex_buffer.push_front(Vertex {
                position: [x, y, world::PILLAR_STEP_HEIGHT],
                color: [0.15 * i as f32, 0.0, 0.0],
            });
            hexagon_vertex_buffer.push_back(Vertex {
                position: [x, y, 0.0],
                color: [1.0 - 0.15 * i as f32, 0.15 * i as f32, 0.0],
            });

        }

        // convert to vector
        let final_buffer: Vec<_> = hexagon_vertex_buffer.into_iter().collect();

        let vbuf = glium::VertexBuffer::new(facade, &final_buffer).unwrap();
        let prog = glium::Program::from_source(facade,
                                               include_str!("chunk_std.vert"),
                                               include_str!("chunk_std.frag"),
                                               None)
            .unwrap();

        let mut pillars = Vec::new();
        for q in 0..world::CHUNK_SIZE * world::CHUNK_SIZE {
            let pos = offset.to_real() +
                      AxialVector::new((q / world::CHUNK_SIZE).into(),
                                       (q % world::CHUNK_SIZE).into())
                .to_real();
            pillars.push(PillarView::from_pillar_section(pos,
                                                         chunk.pillars()
                                                             .get(q as usize)
                                                             .unwrap()
                                                             .sections()));
        }

        // Indecies
        let raw_index_buffer = [5, 0, 1, 2, 5, 1, 4, 5, 2, 3, 4, 2 /* TOP */, 6, 7, 8, 8, 9,
                                6, 9, 11, 6, 9, 10, 11 /* BOTTOM */, 6, 5, 4, 7, 6, 4, 6, 0,
                                5, 11, 0, 6, 10, 1, 0, 11, 10, 0, 9, 2, 1, 10, 9, 1, 8, 3, 2, 9,
                                8, 2, 7, 4, 3, 8, 7, 3 /* Body */];

        let ibuf = glium::index::IndexBuffer::new(facade,
                                                  glium::index::PrimitiveType::TrianglesList,
                                                  &raw_index_buffer)
            .unwrap();

        ChunkView {
            vertices: vbuf,
            program: prog,
            pillars: pillars,
            index_buffer: ibuf,
        }
    }

    pub fn draw<S>(&self, surface: &mut S, camera: &Camera)
        where S: glium::Surface
    {
        for pillar in &self.pillars {
            for i in 0..pillar.sections.len() {
                let uniforms = uniform!{

                    scale_matrix:[[1.0,0.0,0.0,0.0]
                               ,[0.0,1.0,0.0,0.0],
                                [0.0,0.0,pillar.sections.get(i as usize).unwrap()[1],
                                pillar.sections.get(i as usize).unwrap()[0]],
                                [0.0,0.0,0.0,1.0f32]],
                    offset: [pillar.pos.x, pillar.pos.y],
                    proj_matrix: camera.proj_matrix().to_arr(),
                    view_matrix: camera.view_matrix().to_arr(),
                    material_color: pillar.grounds.get(i as usize).unwrap().get_color(),
                };
                let params = glium::DrawParameters {
                    depth: glium::Depth {
                        write: true,
                        test: glium::draw_parameters::DepthTest::IfLess,
                        ..Default::default()
                    },
                    backface_culling:
                        glium::draw_parameters::BackfaceCullingMode::CullCounterClockwise,
                    ..Default::default()
                };

                surface.draw(&self.vertices,
                          &self.index_buffer,
                          &self.program,
                          &uniforms,
                          &params)
                    .unwrap();
            }

        }
    }
}

#[derive(Debug, Copy, Clone)]
pub struct Vertex {
    pub position: [f32; 3],
    pub color: [f32; 3],
}

implement_vertex!(Vertex, position, color);

pub struct PillarView {
    pos: Point2f,
    sections: Vec<[f32; 2]>,
    grounds: Vec<GroundMaterial>,
}

impl PillarView {
    fn from_pillar_section(pos: Point2f, pil_sections: &[PillarSection]) -> PillarView {
        let mut sections = Vec::new();
        let mut grounds = Vec::new();
        for section in pil_sections {
            sections.push([section.bottom.0 as f32,
         section.top.0 as f32 - section.bottom.0 as f32]);
            grounds.push(section.ground.clone());
        }
        PillarView {
            pos: pos,
            sections: sections,
            grounds: grounds,
        }
    }
}
<commit_msg>fixed format<commit_after>use base::world::{self, Chunk, GroundMaterial, PillarSection};
use base::math::*;
use glium;
use Camera;
use render::ToArr;
use std::collections::VecDeque;
use std::f32::consts;

/// Graphical representation of the `base::Chunk`.
pub struct ChunkView {
    vertices: glium::VertexBuffer<Vertex>,
    program: glium::Program,
    pillars: Vec<PillarView>,
    index_buffer: glium::index::IndexBuffer<u32>,
}
/// Calculates one Point-coordinates of a Hexagon
fn hex_corner(size: f32, i: i32) -> (f32, f32) {
    let angle_deg = 60.0 * (i as f32) + 30.0;
    let angle_rad = (consts::PI / 180.0) * angle_deg;

    (size * angle_rad.cos(), size * angle_rad.sin())
}

impl ChunkView {
    /// Creates the graphical representation of given chunk at the given chunk
    /// offset
    pub fn from_chunk<F>(chunk: &Chunk, offset: AxialPoint, facade: &F) -> Self
        where F: glium::backend::Facade
    {

        // Create one hexagon for this chunk
        let mut hexagon_vertex_buffer = VecDeque::new();
        for i in 0..6 {
            let (x, y) = hex_corner(world::HEX_OUTER_RADIUS, i);

            hexagon_vertex_buffer.push_front(Vertex {
                position: [x, y, world::PILLAR_STEP_HEIGHT],
                color: [0.15 * i as f32, 0.0, 0.0],
            });
            hexagon_vertex_buffer.push_back(Vertex {
                position: [x, y, 0.0],
                color: [1.0 - 0.15 * i as f32, 0.15 * i as f32, 0.0],
            });

        }

        // convert to vector
        let final_buffer: Vec<_> = hexagon_vertex_buffer.into_iter().collect();

        let vbuf = glium::VertexBuffer::new(facade, &final_buffer).unwrap();
        let prog = glium::Program::from_source(facade,
                                               include_str!("chunk_std.vert"),
                                               include_str!("chunk_std.frag"),
                                               None)
            .unwrap();

        let mut pillars = Vec::new();
        for q in 0..world::CHUNK_SIZE * world::CHUNK_SIZE {
            let pos = offset.to_real() +
                      AxialVector::new((q / world::CHUNK_SIZE).into(),
                                       (q % world::CHUNK_SIZE).into())
                .to_real();
            pillars.push(PillarView::from_pillar_section(pos,
                                                         chunk.pillars()
                                                             .get(q as usize)
                                                             .unwrap()
                                                             .sections()));
        }

        // Indecies
        let raw_index_buffer = [5, 0, 1, 2, 5, 1, 4, 5, 2, 3, 4, 2 /* TOP */, 6, 7, 8, 8, 9,
                                6, 9, 11, 6, 9, 10, 11 /* BOTTOM */, 6, 5, 4, 7, 6, 4, 6, 0,
                                5, 11, 0, 6, 10, 1, 0, 11, 10, 0, 9, 2, 1, 10, 9, 1, 8, 3, 2, 9,
                                8, 2, 7, 4, 3, 8, 7, 3 /* Body */];

        let ibuf = glium::index::IndexBuffer::new(facade,
                                                  glium::index::PrimitiveType::TrianglesList,
                                                  &raw_index_buffer)
            .unwrap();

        ChunkView {
            vertices: vbuf,
            program: prog,
            pillars: pillars,
            index_buffer: ibuf,
        }
    }

    pub fn draw<S>(&self, surface: &mut S, camera: &Camera)
        where S: glium::Surface
    {
        for pillar in &self.pillars {
            for i in 0..pillar.sections.len() {
                let uniforms = uniform!{

                    scale_matrix:[[1.0,0.0,0.0,0.0]
                               ,[0.0,1.0,0.0,0.0],
                                [0.0,0.0,pillar.sections.get(i as usize).unwrap()[1],
                                pillar.sections.get(i as usize).unwrap()[0]],
                                [0.0,0.0,0.0,1.0f32]],
                    offset: [pillar.pos.x, pillar.pos.y],
                    proj_matrix: camera.proj_matrix().to_arr(),
                    view_matrix: camera.view_matrix().to_arr(),
                    material_color: pillar.grounds.get(i as usize).unwrap().get_color(),
                };
                let params = glium::DrawParameters {
                    depth: glium::Depth {
                        write: true,
                        test: glium::draw_parameters::DepthTest::IfLess,
                        ..Default::default()
                    },
                    backface_culling:
                        glium::draw_parameters::BackfaceCullingMode::CullCounterClockwise,
                    ..Default::default()
                };

                surface.draw(&self.vertices,
                          &self.index_buffer,
                          &self.program,
                          &uniforms,
                          &params)
                    .unwrap();
            }

        }
    }
}

#[derive(Debug, Copy, Clone)]
pub struct Vertex {
    pub position: [f32; 3],
    pub color: [f32; 3],
}

implement_vertex!(Vertex, position, color);

pub struct PillarView {
    pos: Point2f,
    sections: Vec<[f32; 2]>,
    grounds: Vec<GroundMaterial>,
}

impl PillarView {
    fn from_pillar_section(pos: Point2f, pil_sections: &[PillarSection]) -> PillarView {
        let mut sections = Vec::new();
        let mut grounds = Vec::new();
        for section in pil_sections {
            sections.push([section.bottom.0 as f32,
         section.top.0 as f32 - section.bottom.0 as f32]);
            grounds.push(section.ground.clone());
        }
        PillarView {
            pos: pos,
            sections: sections,
            grounds: grounds,
        }
    }
}
<|endoftext|>


<commit_before>// primitive_types4.rs
// Get a slice out of Array a where the ??? is so that the test passes.
// Execute `rustlings hint primitive_types4` for hints!!

// I AM NOT DONE

#[test]
fn slice_out_of_array() {
    let a = [1, 2, 3, 4, 5];

}
<commit_msg>fix: revert primitive_types4 (#296)<commit_after>// primitive_types4.rs
// Get a slice out of Array a where the ??? is so that the test passes.
// Execute `rustlings hint primitive_types4` for hints!!

// I AM NOT DONE

#[test]
fn slice_out_of_array() {
    let a = [1, 2, 3, 4, 5];

    let nice_slice = ???

    assert_eq!([2, 3, 4], nice_slice)
}
<|endoftext|>


<commit_before>// CITA
// Copyright 2016-2017 Cryptape Technologies LLC.

// This program is free software: you can redistribute it
// and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any
// later version.

// This program is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#![feature(custom_attribute)]
#![feature(refcell_replace_swap, try_from)]
extern crate clap;
extern crate dotenv;
extern crate error;
#[macro_use]
extern crate libproto;
#[macro_use]
extern crate logger;
extern crate pubsub;
#[macro_use]
extern crate util;

mod snapshot_tool;

use clap::App;
use libproto::router::{MsgType, RoutingKey, SubModules};
use pubsub::start_pubsub;
use snapshot_tool::SnapShot;
use std::sync::mpsc::channel;
use util::set_panic_handler;

fn main() {
    micro_service_init!("cita-snapshot", "CITA:snapshot");

    let matches = App::new("snapshot")
        .version("0.1")
        .author("Cryptape")
        .about("CITA Block Chain Node powered by Rust")
        .arg_from_usage("-m, --cmd=[snapshot] 'snapshot or restore'")
        .arg_from_usage("-f, --file=[snapshot] 'the file of snapshot'")  //snap file path
        .arg_from_usage("-s, --start_height=[0] 'start height'")  //latest or valid ancient block_id
        .arg_from_usage("-e, --end_height=[1000] 'end height'")  //todo remove
        .get_matches();

    let cmd = matches.value_of("cmd").unwrap_or("snapshot");
    let file = matches.value_of("file").unwrap_or("snapshot");
    let start_height = matches
        .value_of("start_height")
        .unwrap_or("0")
        .parse::<u64>()
        .unwrap();
    let end_height = matches
        .value_of("end_height")
        .unwrap_or("0")
        .parse::<u64>()
        .unwrap();

    let (tx, rx) = channel();
    let (ctx_pub, crx_pub) = channel();

    let snapshot_instance = SnapShot::new(ctx_pub, start_height, end_height, file.to_string());

    start_pubsub(
        "snapshot",
        routing_key!([
            Net >> SnapshotResp,
            Consensus >> SnapshotResp,
            Executor >> SnapshotResp,
            Chain >> SnapshotResp,
            Auth >> SnapshotResp,
        ]),
        tx,
        crx_pub,
    );

    match cmd {
        "snapshot" => {
            snapshot_instance.snapshot();
            println!("snapshot_tool send snapshot cmd");
        }
        "restore" => {
            snapshot_instance.begin();
            println!("snapshot_tool send restore cmd");
        }
        _ => println!("snapshot_tool send error cmd"),
    }
    let mut exit = false;
    loop {
        if let Ok((key, msg)) = rx.recv() {
            info!("snapshot_tool receive ack key: {:?}", key);
            exit = snapshot_instance.parse_data(key, msg);
        }
        if exit {
            break;
        }
    }
}
<commit_msg>make it impossible to reentry snapshot_tool.<commit_after>// CITA
// Copyright 2016-2017 Cryptape Technologies LLC.

// This program is free software: you can redistribute it
// and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any
// later version.

// This program is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#![feature(custom_attribute)]
#![feature(refcell_replace_swap, try_from)]
extern crate clap;
extern crate dotenv;
extern crate error;
#[macro_use]
extern crate libproto;
#[macro_use]
extern crate logger;
extern crate pubsub;
#[macro_use]
extern crate util;

mod snapshot_tool;

use clap::App;
use libproto::router::{MsgType, RoutingKey, SubModules};
use pubsub::start_pubsub;
use snapshot_tool::SnapShot;
use std::fs::{self, File};
use std::path::Path;
use std::sync::mpsc::channel;
use util::set_panic_handler;

const SNAPSHOT_FILE: &'static str = ".cita_snapshot";

fn main() {
    micro_service_init!("cita-snapshot", "CITA:snapshot");

    // Judge whether snapshot_tool have started.
    let p = Path::new(SNAPSHOT_FILE);
    if p.exists() {
        warn!("Snapshot_tool already started, or existed abnormally last time.");
        return;
    } else {
        let _ = File::create(SNAPSHOT_FILE);
    }

    let matches = App::new("snapshot")
        .version("0.1")
        .author("Cryptape")
        .about("CITA Block Chain Node powered by Rust")
        .arg_from_usage("-m, --cmd=[snapshot] 'snapshot or restore'")
        .arg_from_usage("-f, --file=[snapshot] 'the file of snapshot'")  //snap file path
        .arg_from_usage("-s, --start_height=[0] 'start height'")  //latest or valid ancient block_id
        .arg_from_usage("-e, --end_height=[1000] 'end height'")  //todo remove
        .get_matches();

    let cmd = matches.value_of("cmd").unwrap_or("snapshot");
    let file = matches.value_of("file").unwrap_or("snapshot");
    let start_height = matches
        .value_of("start_height")
        .unwrap_or("0")
        .parse::<u64>()
        .unwrap();
    let end_height = matches
        .value_of("end_height")
        .unwrap_or("0")
        .parse::<u64>()
        .unwrap();

    let (tx, rx) = channel();
    let (ctx_pub, crx_pub) = channel();

    let snapshot_instance = SnapShot::new(ctx_pub, start_height, end_height, file.to_string());

    start_pubsub(
        "snapshot",
        routing_key!([
            Net >> SnapshotResp,
            Consensus >> SnapshotResp,
            Executor >> SnapshotResp,
            Chain >> SnapshotResp,
            Auth >> SnapshotResp,
        ]),
        tx,
        crx_pub,
    );

    match cmd {
        "snapshot" => {
            snapshot_instance.snapshot();
            println!("snapshot_tool send snapshot cmd");
        }
        "restore" => {
            snapshot_instance.begin();
            println!("snapshot_tool send restore cmd");
        }
        _ => println!("snapshot_tool send error cmd"),
    }
    let mut exit = false;
    loop {
        if let Ok((key, msg)) = rx.recv() {
            info!("snapshot_tool receive ack key: {:?}", key);
            exit = snapshot_instance.parse_data(key, msg);
        }
        if exit {
            // Remove the file
            let _ = fs::remove_file(p);
            break;
        }
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use engine::Engine;
use engine::EngineError;
use engine::EngineResult;
use engine::ErrorEnum;

use engine::Pool;
use engine::RenameAction;
use engine::Redundancy;

use std::cell::RefCell;
use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use std::collections::BTreeSet;
use std::iter::FromIterator;
use std::path::Path;
use std::path::PathBuf;
use std::rc::Rc;

use super::pool::SimPool;
use super::randomization::Randomizer;

#[derive(Debug)]
pub struct SimEngine {
    pools: BTreeMap<String, SimPool>,
    rdm: Rc<RefCell<Randomizer>>,
}

impl SimEngine {
    pub fn new() -> SimEngine {
        SimEngine {
            pools: BTreeMap::new(),
            rdm: Rc::new(RefCell::new(Randomizer::new())),
        }
    }
}

impl Engine for SimEngine {
    fn create_pool(&mut self,
                   name: &str,
                   blockdev_paths: &[&Path],
                   redundancy: Option<u16>,
                   _force: bool)
                   -> EngineResult<Vec<PathBuf>> {

        let redundancy = calculate_redundancy!(redundancy);

        if self.pools.contains_key(name) {
            return Err(EngineError::Engine(ErrorEnum::AlreadyExists, name.into()));
        }

        let devices =
            BTreeSet::from_iter(blockdev_paths).into_iter().map(|x| *x).collect::<Vec<&Path>>();

        let pool = SimPool::new(self.rdm.clone(), &devices, redundancy);

        if self.rdm.borrow_mut().throw_die() {
            return Err(EngineError::Engine(ErrorEnum::Error, "X".into()));
        }

        let bdev_paths = pool.block_devs.values().map(|p| p.devnode.clone()).collect();
        self.pools.insert(name.to_owned(), pool);

        Ok(bdev_paths)
    }

    fn destroy_pool(&mut self, name: &str) -> EngineResult<bool> {
        destroy_pool!{self; name}
    }

    fn rename_pool(&mut self, old_name: &str, new_name: &str) -> EngineResult<RenameAction> {
        rename_pool!{self; old_name; new_name}
    }

    /// Looks up the pool by its unique name
    fn get_pool(&mut self, name: &str) -> EngineResult<&mut Pool> {
        get_pool!(self; name)
    }

    /// Returns a collection of the Pool objects that belong to this engine
    fn pools(&mut self) -> BTreeMap<&str, &mut Pool> {
        pools!(self)
    }

    /// Set properties of the simulator
    fn configure_simulator(&mut self, denominator: u32) -> EngineResult<()> {
        self.rdm.borrow_mut().set_probability(denominator);
        Ok(())
    }
}

#[cfg(test)]
mod tests {

    use std::path::Path;

    use quickcheck::QuickCheck;

    use super::SimEngine;

    use engine::Engine;
    use engine::EngineError;
    use engine::ErrorEnum;
    use engine::RenameAction;

    #[test]
    fn prop_configure_simulator_runs() {

        /// Configure simulator should always return Ok.
        fn configure_simulator_runs(denominator: u32) -> bool {
            SimEngine::new().configure_simulator(denominator).is_ok()
        }

        QuickCheck::new().tests(10).quickcheck(configure_simulator_runs as fn(u32) -> bool);
    }

    #[test]
    /// When an engine has no pools, any name lookup should fail
    fn get_pool_err() {
        assert!(match SimEngine::new().get_pool("name") {
            Err(EngineError::Engine(ErrorEnum::NotFound, _)) => true,
            _ => false,
        });
    }

    #[test]
    /// When an engine has no pools, the thing returned by pools() is empty
    fn pools_empty() {
        assert!(SimEngine::new().pools().is_empty());
    }

    #[test]
    /// When an engine has no pools, destroying any pool must succeed
    fn destroy_pool_empty() {
        assert!(SimEngine::new().destroy_pool("name").is_ok());
    }

    #[test]
    /// Destroying an empty pool should succeed.
    fn destroy_empty_pool() {
        let name = "name";
        let mut engine = SimEngine::new();
        engine.create_pool(name, &[], None, false).unwrap();
        assert!(engine.destroy_pool(name).is_ok());
    }

    #[test]
    /// Destroying a pool with devices should fail
    fn destroy_pool_w_devices() {
        let name = "name";
        let mut engine = SimEngine::new();
        engine.create_pool(name, &[Path::new("/s/d")], None, false).unwrap();
        assert!(match engine.destroy_pool(name) {
            Err(EngineError::Engine(ErrorEnum::Busy, _)) => true,
            _ => false,
        });
    }

    #[test]
    #[ignore]
    /// Creating a new pool identical to the previous should succeed
    fn create_new_pool_twice() {
        let name = "name";
        let mut engine = SimEngine::new();
        engine.create_pool(name, &[], None, false).unwrap();
        assert!(match engine.create_pool(name, &[], None, false) {
            Ok(devs) => devs.is_empty(),
            Err(_) => false,
        });
    }

    #[test]
    /// Creating a new pool with the same name should fail
    fn create_pool_name_collision() {
        let name = "name";
        let mut engine = SimEngine::new();
        engine.create_pool(name, &[Path::new("/s/d")], None, false).unwrap();
        assert!(match engine.create_pool(name, &[], None, false) {
            Err(EngineError::Engine(ErrorEnum::AlreadyExists, _)) => true,
            _ => false,
        });
    }

    #[test]
    /// Creating a pool with duplicate devices should succeed
    fn create_pool_duplicate_devices() {
        let path = "/s/d";
        let mut engine = SimEngine::new();
        let devices = vec![Path::new(path), Path::new(path)];
        assert!(match engine.create_pool("name", &devices, None, false) {
            Ok(devs) => devs.len() == 1,
            _ => false,
        });
    }

    #[test]
    /// Creating a pool with an impossible raid level should fail
    fn create_pool_max_u16_raid() {
        let mut engine = SimEngine::new();
        assert!(engine.create_pool("name", &[], Some(u16::max_value()), false).is_err());
    }

    #[test]
    /// Renaming a pool on an empty engine always works
    fn rename_empty() {
        let mut engine = SimEngine::new();
        assert!(match engine.rename_pool("old_name", "new_name") {
            Ok(RenameAction::NoSource) => true,
            _ => false,
        });
    }

    #[test]
    /// Renaming a pool to itself in an empty engine always works
    fn rename_empty_identity() {
        let mut engine = SimEngine::new();
        assert!(match engine.rename_pool("old_name", "old_name") {
            Ok(RenameAction::Identity) => true,
            _ => false,
        });
    }

    #[test]
    /// Renaming a pool to itself always works
    fn rename_identity() {
        let name = "name";
        let mut engine = SimEngine::new();
        engine.create_pool(name, &[], None, false).unwrap();
        assert!(match engine.rename_pool(name, name) {
            Ok(RenameAction::Identity) => true,
            _ => false,
        });
    }

    #[test]
    /// Renaming a pool to another pool should work if new name not taken
    fn rename_happens() {
        let name = "old_name";
        let mut engine = SimEngine::new();
        engine.create_pool(name, &[], None, false).unwrap();
        assert!(match engine.rename_pool(name, "new_name") {
            Ok(RenameAction::Renamed) => true,
            _ => false,
        });
    }

    #[test]
    /// Renaming a pool to another pool should fail if new name taken
    fn rename_fails() {
        let old_name = "old_name";
        let new_name = "new_name";
        let mut engine = SimEngine::new();
        engine.create_pool(old_name, &[], None, false).unwrap();
        engine.create_pool(new_name, &[], None, false).unwrap();
        assert!(match engine.rename_pool(old_name, new_name) {
            Err(EngineError::Engine(ErrorEnum::AlreadyExists, _)) => true,
            _ => false,
        });
    }

    #[test]
    /// Renaming should succeed if old_name absent, new present
    fn rename_no_op() {
        let old_name = "old_name";
        let new_name = "new_name";
        let mut engine = SimEngine::new();
        engine.create_pool(new_name, &[], None, false).unwrap();
        assert!(match engine.rename_pool(old_name, new_name) {
            Ok(RenameAction::NoSource) => true,
            _ => false,
        });
    }

}
<commit_msg>Use the constant value, not the function for max value.<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use engine::Engine;
use engine::EngineError;
use engine::EngineResult;
use engine::ErrorEnum;

use engine::Pool;
use engine::RenameAction;
use engine::Redundancy;

use std::cell::RefCell;
use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use std::collections::BTreeSet;
use std::iter::FromIterator;
use std::path::Path;
use std::path::PathBuf;
use std::rc::Rc;

use super::pool::SimPool;
use super::randomization::Randomizer;

#[derive(Debug)]
pub struct SimEngine {
    pools: BTreeMap<String, SimPool>,
    rdm: Rc<RefCell<Randomizer>>,
}

impl SimEngine {
    pub fn new() -> SimEngine {
        SimEngine {
            pools: BTreeMap::new(),
            rdm: Rc::new(RefCell::new(Randomizer::new())),
        }
    }
}

impl Engine for SimEngine {
    fn create_pool(&mut self,
                   name: &str,
                   blockdev_paths: &[&Path],
                   redundancy: Option<u16>,
                   _force: bool)
                   -> EngineResult<Vec<PathBuf>> {

        let redundancy = calculate_redundancy!(redundancy);

        if self.pools.contains_key(name) {
            return Err(EngineError::Engine(ErrorEnum::AlreadyExists, name.into()));
        }

        let devices =
            BTreeSet::from_iter(blockdev_paths).into_iter().map(|x| *x).collect::<Vec<&Path>>();

        let pool = SimPool::new(self.rdm.clone(), &devices, redundancy);

        if self.rdm.borrow_mut().throw_die() {
            return Err(EngineError::Engine(ErrorEnum::Error, "X".into()));
        }

        let bdev_paths = pool.block_devs.values().map(|p| p.devnode.clone()).collect();
        self.pools.insert(name.to_owned(), pool);

        Ok(bdev_paths)
    }

    fn destroy_pool(&mut self, name: &str) -> EngineResult<bool> {
        destroy_pool!{self; name}
    }

    fn rename_pool(&mut self, old_name: &str, new_name: &str) -> EngineResult<RenameAction> {
        rename_pool!{self; old_name; new_name}
    }

    /// Looks up the pool by its unique name
    fn get_pool(&mut self, name: &str) -> EngineResult<&mut Pool> {
        get_pool!(self; name)
    }

    /// Returns a collection of the Pool objects that belong to this engine
    fn pools(&mut self) -> BTreeMap<&str, &mut Pool> {
        pools!(self)
    }

    /// Set properties of the simulator
    fn configure_simulator(&mut self, denominator: u32) -> EngineResult<()> {
        self.rdm.borrow_mut().set_probability(denominator);
        Ok(())
    }
}

#[cfg(test)]
mod tests {

    use std;
    use std::path::Path;

    use quickcheck::QuickCheck;

    use super::SimEngine;

    use engine::Engine;
    use engine::EngineError;
    use engine::ErrorEnum;
    use engine::RenameAction;

    #[test]
    fn prop_configure_simulator_runs() {

        /// Configure simulator should always return Ok.
        fn configure_simulator_runs(denominator: u32) -> bool {
            SimEngine::new().configure_simulator(denominator).is_ok()
        }

        QuickCheck::new().tests(10).quickcheck(configure_simulator_runs as fn(u32) -> bool);
    }

    #[test]
    /// When an engine has no pools, any name lookup should fail
    fn get_pool_err() {
        assert!(match SimEngine::new().get_pool("name") {
            Err(EngineError::Engine(ErrorEnum::NotFound, _)) => true,
            _ => false,
        });
    }

    #[test]
    /// When an engine has no pools, the thing returned by pools() is empty
    fn pools_empty() {
        assert!(SimEngine::new().pools().is_empty());
    }

    #[test]
    /// When an engine has no pools, destroying any pool must succeed
    fn destroy_pool_empty() {
        assert!(SimEngine::new().destroy_pool("name").is_ok());
    }

    #[test]
    /// Destroying an empty pool should succeed.
    fn destroy_empty_pool() {
        let name = "name";
        let mut engine = SimEngine::new();
        engine.create_pool(name, &[], None, false).unwrap();
        assert!(engine.destroy_pool(name).is_ok());
    }

    #[test]
    /// Destroying a pool with devices should fail
    fn destroy_pool_w_devices() {
        let name = "name";
        let mut engine = SimEngine::new();
        engine.create_pool(name, &[Path::new("/s/d")], None, false).unwrap();
        assert!(match engine.destroy_pool(name) {
            Err(EngineError::Engine(ErrorEnum::Busy, _)) => true,
            _ => false,
        });
    }

    #[test]
    #[ignore]
    /// Creating a new pool identical to the previous should succeed
    fn create_new_pool_twice() {
        let name = "name";
        let mut engine = SimEngine::new();
        engine.create_pool(name, &[], None, false).unwrap();
        assert!(match engine.create_pool(name, &[], None, false) {
            Ok(devs) => devs.is_empty(),
            Err(_) => false,
        });
    }

    #[test]
    /// Creating a new pool with the same name should fail
    fn create_pool_name_collision() {
        let name = "name";
        let mut engine = SimEngine::new();
        engine.create_pool(name, &[Path::new("/s/d")], None, false).unwrap();
        assert!(match engine.create_pool(name, &[], None, false) {
            Err(EngineError::Engine(ErrorEnum::AlreadyExists, _)) => true,
            _ => false,
        });
    }

    #[test]
    /// Creating a pool with duplicate devices should succeed
    fn create_pool_duplicate_devices() {
        let path = "/s/d";
        let mut engine = SimEngine::new();
        let devices = vec![Path::new(path), Path::new(path)];
        assert!(match engine.create_pool("name", &devices, None, false) {
            Ok(devs) => devs.len() == 1,
            _ => false,
        });
    }

    #[test]
    /// Creating a pool with an impossible raid level should fail
    fn create_pool_max_u16_raid() {
        let mut engine = SimEngine::new();
        assert!(engine.create_pool("name", &[], Some(std::u16::MAX), false).is_err());
    }

    #[test]
    /// Renaming a pool on an empty engine always works
    fn rename_empty() {
        let mut engine = SimEngine::new();
        assert!(match engine.rename_pool("old_name", "new_name") {
            Ok(RenameAction::NoSource) => true,
            _ => false,
        });
    }

    #[test]
    /// Renaming a pool to itself in an empty engine always works
    fn rename_empty_identity() {
        let mut engine = SimEngine::new();
        assert!(match engine.rename_pool("old_name", "old_name") {
            Ok(RenameAction::Identity) => true,
            _ => false,
        });
    }

    #[test]
    /// Renaming a pool to itself always works
    fn rename_identity() {
        let name = "name";
        let mut engine = SimEngine::new();
        engine.create_pool(name, &[], None, false).unwrap();
        assert!(match engine.rename_pool(name, name) {
            Ok(RenameAction::Identity) => true,
            _ => false,
        });
    }

    #[test]
    /// Renaming a pool to another pool should work if new name not taken
    fn rename_happens() {
        let name = "old_name";
        let mut engine = SimEngine::new();
        engine.create_pool(name, &[], None, false).unwrap();
        assert!(match engine.rename_pool(name, "new_name") {
            Ok(RenameAction::Renamed) => true,
            _ => false,
        });
    }

    #[test]
    /// Renaming a pool to another pool should fail if new name taken
    fn rename_fails() {
        let old_name = "old_name";
        let new_name = "new_name";
        let mut engine = SimEngine::new();
        engine.create_pool(old_name, &[], None, false).unwrap();
        engine.create_pool(new_name, &[], None, false).unwrap();
        assert!(match engine.rename_pool(old_name, new_name) {
            Err(EngineError::Engine(ErrorEnum::AlreadyExists, _)) => true,
            _ => false,
        });
    }

    #[test]
    /// Renaming should succeed if old_name absent, new present
    fn rename_no_op() {
        let old_name = "old_name";
        let new_name = "new_name";
        let mut engine = SimEngine::new();
        engine.create_pool(new_name, &[], None, false).unwrap();
        assert!(match engine.rename_pool(old_name, new_name) {
            Ok(RenameAction::NoSource) => true,
            _ => false,
        });
    }

}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::collections::{HashMap, HashSet};
use std::collections::hash_map::RandomState;
use std::iter::FromIterator;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;

use devicemapper::DM;
use devicemapper::{DataBlocks, Sectors};
use devicemapper::LinearDev;
use devicemapper::Segment;
use devicemapper::{ThinPoolDev, ThinPoolStatus, ThinPoolWorkingStatus};
use time::{now, Timespec};
use uuid::Uuid;
use serde_json;

use engine::EngineError;
use engine::EngineResult;
use engine::ErrorEnum;
use engine::Filesystem;
use engine::Pool;
use engine::RenameAction;
use engine::engine::Redundancy;
use engine::strat_engine::blockdev::wipe_sectors;

use super::super::engine::{FilesystemUuid, HasName, HasUuid};
use super::super::structures::Table;

use super::serde_structs::StratSave;
use super::blockdev::{BlockDev, initialize, resolve_devices};
use super::filesystem::{StratFilesystem, FilesystemStatus};
use super::metadata::MIN_MDA_SECTORS;

pub const DATA_BLOCK_SIZE: Sectors = Sectors(2048);
pub const META_LOWATER: u64 = 512;
pub const DATA_LOWATER: DataBlocks = DataBlocks(512);

#[derive(Debug)]
pub struct StratPool {
    name: String,
    pool_uuid: Uuid,
    pub block_devs: HashMap<PathBuf, BlockDev>,
    pub filesystems: Table<StratFilesystem>,
    redundancy: Redundancy,
    thin_pool: ThinPoolDev,
}

impl StratPool {
    /// Initialize a Stratis Pool.
    /// 1. Initialize the block devices specified by paths.
    /// 2. Set up thinpool device to back filesystems.
    pub fn initialize(name: &str,
                      dm: &DM,
                      paths: &[&Path],
                      redundancy: Redundancy,
                      force: bool)
                      -> EngineResult<StratPool> {
        let pool_uuid = Uuid::new_v4();

        let devices = try!(resolve_devices(paths));
        let bds = try!(initialize(&pool_uuid, devices, MIN_MDA_SECTORS, force));

        // TODO: We've got some temporary code in BlockDev::initialize that
        // makes sure we've got at least 2 blockdevs supplied - one for a meta
        // and one for data.  In the future, we will be able to deal with a
        // single blockdev.  When that code is added to use a single blockdev,
        // the check for 2 devs in BlockDev::initialize should be removed.
        assert!(bds.len() >= 2);

        let meta_dev = try!(LinearDev::new(&format!("stratis_{}_meta", name),
                                           dm,
                                           &[bds[0].avail_range_segment()]));
        // When constructing a thin-pool, Stratis reserves the first N
        // sectors on a block device by creating a linear device with a
        // starting offset. DM writes the super block in the first block.
        // DM requires this first block to be zeros when the meta data for
        // the thin-pool is initially created. If we don't zero the
        // superblock DM issue error messages because it triggers code paths
        // that are trying to re-adopt the device with the attributes that
        // have been passed.
        try!(wipe_sectors(&try!(meta_dev.devnode()), Sectors(0), DATA_BLOCK_SIZE));

        let segments = bds[1..]
            .iter()
            .map(|block_dev| block_dev.avail_range_segment())
            .collect::<Vec<Segment>>();

        let data_dev = try!(LinearDev::new(&format!("stratis_{}_data", name), dm, &segments));
        try!(wipe_sectors(&try!(data_dev.devnode()), Sectors(0), DATA_BLOCK_SIZE));
        let length = try!(data_dev.size()).sectors();

        // TODO Fix hard coded data blocksize and low water mark.
        let thinpool_dev = try!(ThinPoolDev::new(&format!("stratis_{}_thinpool", name),
                                                 dm,
                                                 length,
                                                 DATA_BLOCK_SIZE,
                                                 DataBlocks(256000),
                                                 meta_dev,
                                                 data_dev));

        let mut blockdevs = HashMap::new();
        for bd in bds {
            blockdevs.insert(bd.devnode.clone(), bd);
        }
        let mut pool = StratPool {
            name: name.to_owned(),
            pool_uuid: pool_uuid,
            block_devs: blockdevs,
            filesystems: Table::new(),
            redundancy: redundancy,
            thin_pool: thinpool_dev,
        };

        try!(pool.write_metadata());

        Ok(pool)
    }

    /// Return the metadata from the first blockdev with up-to-date, readable
    /// metadata.
    /// Precondition: All Blockdevs in blockdevs must belong to the same pool.
    pub fn load_state(blockdevs: &[&BlockDev]) -> Option<Vec<u8>> {
        if blockdevs.is_empty() {
            return None;
        }

        let most_recent_blockdev = blockdevs.iter()
            .max_by_key(|bd| bd.last_update_time())
            .expect("must be a maximum since bds is non-empty");

        let most_recent_time = most_recent_blockdev.last_update_time();

        if most_recent_time.is_none() {
            return None;
        }

        for bd in blockdevs.iter()
            .filter(|b| b.last_update_time() == most_recent_time) {
            match bd.load_state() {
                Ok(Some(data)) => return Some(data),
                _ => continue,
            }
        }

        None
    }

    /// Write the given data to all blockdevs marking with specified time.
    pub fn save_state(devs: &mut [&mut BlockDev],
                      time: &Timespec,
                      metadata: &[u8])
                      -> EngineResult<()> {
        // TODO: Do something better than panic when saving to blockdev fails.
        // Panic can occur for a the usual IO reasons, but also:
        // 1. If the timestamp is older than a previously written timestamp.
        // 2. If the variable length metadata is too large.
        for bd in devs {
            bd.save_state(time, metadata).unwrap();
        }
        Ok(())
    }

    /// Write pool metadata to all its blockdevs marking with current time.

    // TODO: Cap # of blockdevs written to, as described in SWDD

    // TODO: Check current time against global last updated, and use
    // alternate time value if earlier, as described in SWDD
    pub fn write_metadata(&mut self) -> EngineResult<()> {
        let data = try!(serde_json::to_string(&self.to_save()));
        let mut blockdevs: Vec<&mut BlockDev> = self.block_devs.values_mut().collect();
        StratPool::save_state(&mut blockdevs, &now().to_timespec(), data.as_bytes())
    }

    pub fn to_save(&self) -> StratSave {
        StratSave {
            name: self.name.clone(),
            id: self.pool_uuid.simple().to_string(),
            block_devs: self.block_devs
                .iter()
                .map(|(_, bd)| (bd.uuid().simple().to_string(), bd.to_save()))
                .collect(),
        }
    }

    pub fn check(&mut self) -> () {
        let dm = DM::new().expect("Could not get DM handle");

        let result = match self.thin_pool.status(&dm) {
            Ok(r) => r,
            Err(_) => {
                error!("Could not get thinpool status");
                // TODO: Take pool offline?
                return;
            }
        };

        match result {
            ThinPoolStatus::Good(wstatus, usage) => {
                match wstatus {
                    ThinPoolWorkingStatus::Good => {}
                    ThinPoolWorkingStatus::ReadOnly => {
                        // TODO: why is pool r/o and how do we get it
                        // rw again?
                    }
                    ThinPoolWorkingStatus::OutOfSpace => {
                        // TODO: Add more space if possible, or
                        // prevent further usage
                        // Should never happen -- we should be extending first!
                    }
                    ThinPoolWorkingStatus::NeedsCheck => {
                        // TODO: Take pool offline?
                        // TODO: run thin_check
                    }
                }

                if usage.used_meta > usage.total_meta - META_LOWATER {
                    // TODO: Extend meta device
                }

                if usage.used_data > usage.total_data - DATA_LOWATER {
                    // TODO: Extend data device
                }
            }
            ThinPoolStatus::Fail => {
                // TODO: Take pool offline?
                // TODO: Run thin_check
            }
        }

        for fs in self.filesystems.iter_mut() {
            match fs.check(&dm) {
                Ok(f_status) => {
                    if let FilesystemStatus::Failed = f_status {
                        // TODO: recover fs? (how?)
                    }
                }
                Err(_e) => error!("fs.check() failed"),
            }
        }
    }
}

impl Pool for StratPool {
    fn create_filesystems<'a, 'b>(&'a mut self,
                                  specs: &[&'b str])
                                  -> EngineResult<Vec<(&'b str, FilesystemUuid)>> {
        let names: HashSet<_, RandomState> = HashSet::from_iter(specs);
        for name in names.iter() {
            if self.filesystems.contains_name(name) {
                return Err(EngineError::Engine(ErrorEnum::AlreadyExists, name.to_string()));
            }
        }

        let mut result = Vec::new();
        for name in names.iter() {
            let uuid = Uuid::new_v4();
            let dm = try!(DM::new());
            let new_filesystem = try!(StratFilesystem::new(uuid, name, &dm, &mut self.thin_pool));
            self.filesystems.insert(new_filesystem);
            result.push((**name, uuid));
        }

        Ok(result)
    }

    fn add_blockdevs(&mut self, paths: &[&Path], force: bool) -> EngineResult<Vec<PathBuf>> {
        let devices = try!(resolve_devices(paths));
        let bds = try!(initialize(&self.pool_uuid, devices, MIN_MDA_SECTORS, force));
        let bdev_paths = bds.iter().map(|p| p.devnode.clone()).collect();
        for bd in bds {
            self.block_devs.insert(bd.devnode.clone(), bd);
        }
        try!(self.write_metadata());
        Ok(bdev_paths)
    }

    fn destroy(mut self) -> EngineResult<()> {

        // TODO Do we want to create a new File each time we interact with DM?
        let dm = try!(DM::new());
        try!(self.thin_pool.teardown(&dm));

        for (_, bd) in self.block_devs.drain() {
            try!(bd.wipe_metadata());
        }

        Ok(())
    }

    fn destroy_filesystems<'a, 'b>(&'a mut self,
                                   fs_uuids: &[&'b FilesystemUuid])
                                   -> EngineResult<Vec<&'b FilesystemUuid>> {
        destroy_filesystems!{self; fs_uuids}
    }

    fn rename_filesystem(&mut self,
                         uuid: &FilesystemUuid,
                         new_name: &str)
                         -> EngineResult<RenameAction> {
        rename_filesystem!{self; uuid; new_name}
    }

    fn rename(&mut self, name: &str) {
        self.name = name.to_owned();
    }

    fn get_filesystem(&mut self, uuid: &FilesystemUuid) -> Option<&mut Filesystem> {
        get_filesystem!(self; uuid)
    }
}

impl HasUuid for StratPool {
    fn uuid(&self) -> &FilesystemUuid {
        &self.pool_uuid
    }
}

impl HasName for StratPool {
    fn name(&self) -> &str {
        &self.name
    }
}
<commit_msg>Get rid of TODO<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::collections::{HashMap, HashSet};
use std::collections::hash_map::RandomState;
use std::iter::FromIterator;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;

use devicemapper::DM;
use devicemapper::{DataBlocks, Sectors};
use devicemapper::LinearDev;
use devicemapper::Segment;
use devicemapper::{ThinPoolDev, ThinPoolStatus, ThinPoolWorkingStatus};
use time::{now, Timespec};
use uuid::Uuid;
use serde_json;

use engine::EngineError;
use engine::EngineResult;
use engine::ErrorEnum;
use engine::Filesystem;
use engine::Pool;
use engine::RenameAction;
use engine::engine::Redundancy;
use engine::strat_engine::blockdev::wipe_sectors;

use super::super::engine::{FilesystemUuid, HasName, HasUuid};
use super::super::structures::Table;

use super::serde_structs::StratSave;
use super::blockdev::{BlockDev, initialize, resolve_devices};
use super::filesystem::{StratFilesystem, FilesystemStatus};
use super::metadata::MIN_MDA_SECTORS;

pub const DATA_BLOCK_SIZE: Sectors = Sectors(2048);
pub const META_LOWATER: u64 = 512;
pub const DATA_LOWATER: DataBlocks = DataBlocks(512);

#[derive(Debug)]
pub struct StratPool {
    name: String,
    pool_uuid: Uuid,
    pub block_devs: HashMap<PathBuf, BlockDev>,
    pub filesystems: Table<StratFilesystem>,
    redundancy: Redundancy,
    thin_pool: ThinPoolDev,
}

impl StratPool {
    /// Initialize a Stratis Pool.
    /// 1. Initialize the block devices specified by paths.
    /// 2. Set up thinpool device to back filesystems.
    pub fn initialize(name: &str,
                      dm: &DM,
                      paths: &[&Path],
                      redundancy: Redundancy,
                      force: bool)
                      -> EngineResult<StratPool> {
        let pool_uuid = Uuid::new_v4();

        let devices = try!(resolve_devices(paths));
        let bds = try!(initialize(&pool_uuid, devices, MIN_MDA_SECTORS, force));

        // TODO: We've got some temporary code in BlockDev::initialize that
        // makes sure we've got at least 2 blockdevs supplied - one for a meta
        // and one for data.  In the future, we will be able to deal with a
        // single blockdev.  When that code is added to use a single blockdev,
        // the check for 2 devs in BlockDev::initialize should be removed.
        assert!(bds.len() >= 2);

        let meta_dev = try!(LinearDev::new(&format!("stratis_{}_meta", name),
                                           dm,
                                           &[bds[0].avail_range_segment()]));
        // When constructing a thin-pool, Stratis reserves the first N
        // sectors on a block device by creating a linear device with a
        // starting offset. DM writes the super block in the first block.
        // DM requires this first block to be zeros when the meta data for
        // the thin-pool is initially created. If we don't zero the
        // superblock DM issue error messages because it triggers code paths
        // that are trying to re-adopt the device with the attributes that
        // have been passed.
        try!(wipe_sectors(&try!(meta_dev.devnode()), Sectors(0), DATA_BLOCK_SIZE));

        let segments = bds[1..]
            .iter()
            .map(|block_dev| block_dev.avail_range_segment())
            .collect::<Vec<Segment>>();

        let data_dev = try!(LinearDev::new(&format!("stratis_{}_data", name), dm, &segments));
        try!(wipe_sectors(&try!(data_dev.devnode()), Sectors(0), DATA_BLOCK_SIZE));
        let length = try!(data_dev.size()).sectors();

        // TODO Fix hard coded data blocksize and low water mark.
        let thinpool_dev = try!(ThinPoolDev::new(&format!("stratis_{}_thinpool", name),
                                                 dm,
                                                 length,
                                                 DATA_BLOCK_SIZE,
                                                 DataBlocks(256000),
                                                 meta_dev,
                                                 data_dev));

        let mut blockdevs = HashMap::new();
        for bd in bds {
            blockdevs.insert(bd.devnode.clone(), bd);
        }
        let mut pool = StratPool {
            name: name.to_owned(),
            pool_uuid: pool_uuid,
            block_devs: blockdevs,
            filesystems: Table::new(),
            redundancy: redundancy,
            thin_pool: thinpool_dev,
        };

        try!(pool.write_metadata());

        Ok(pool)
    }

    /// Return the metadata from the first blockdev with up-to-date, readable
    /// metadata.
    /// Precondition: All Blockdevs in blockdevs must belong to the same pool.
    pub fn load_state(blockdevs: &[&BlockDev]) -> Option<Vec<u8>> {
        if blockdevs.is_empty() {
            return None;
        }

        let most_recent_blockdev = blockdevs.iter()
            .max_by_key(|bd| bd.last_update_time())
            .expect("must be a maximum since bds is non-empty");

        let most_recent_time = most_recent_blockdev.last_update_time();

        if most_recent_time.is_none() {
            return None;
        }

        for bd in blockdevs.iter()
            .filter(|b| b.last_update_time() == most_recent_time) {
            match bd.load_state() {
                Ok(Some(data)) => return Some(data),
                _ => continue,
            }
        }

        None
    }

    /// Write the given data to all blockdevs marking with specified time.
    pub fn save_state(devs: &mut [&mut BlockDev],
                      time: &Timespec,
                      metadata: &[u8])
                      -> EngineResult<()> {
        // TODO: Do something better than panic when saving to blockdev fails.
        // Panic can occur for a the usual IO reasons, but also:
        // 1. If the timestamp is older than a previously written timestamp.
        // 2. If the variable length metadata is too large.
        for bd in devs {
            bd.save_state(time, metadata).unwrap();
        }
        Ok(())
    }

    /// Write pool metadata to all its blockdevs marking with current time.

    // TODO: Cap # of blockdevs written to, as described in SWDD

    // TODO: Check current time against global last updated, and use
    // alternate time value if earlier, as described in SWDD
    pub fn write_metadata(&mut self) -> EngineResult<()> {
        let data = try!(serde_json::to_string(&self.to_save()));
        let mut blockdevs: Vec<&mut BlockDev> = self.block_devs.values_mut().collect();
        StratPool::save_state(&mut blockdevs, &now().to_timespec(), data.as_bytes())
    }

    pub fn to_save(&self) -> StratSave {
        StratSave {
            name: self.name.clone(),
            id: self.pool_uuid.simple().to_string(),
            block_devs: self.block_devs
                .iter()
                .map(|(_, bd)| (bd.uuid().simple().to_string(), bd.to_save()))
                .collect(),
        }
    }

    pub fn check(&mut self) -> () {
        let dm = DM::new().expect("Could not get DM handle");

        let result = match self.thin_pool.status(&dm) {
            Ok(r) => r,
            Err(_) => {
                error!("Could not get thinpool status");
                // TODO: Take pool offline?
                return;
            }
        };

        match result {
            ThinPoolStatus::Good(wstatus, usage) => {
                match wstatus {
                    ThinPoolWorkingStatus::Good => {}
                    ThinPoolWorkingStatus::ReadOnly => {
                        // TODO: why is pool r/o and how do we get it
                        // rw again?
                    }
                    ThinPoolWorkingStatus::OutOfSpace => {
                        // TODO: Add more space if possible, or
                        // prevent further usage
                        // Should never happen -- we should be extending first!
                    }
                    ThinPoolWorkingStatus::NeedsCheck => {
                        // TODO: Take pool offline?
                        // TODO: run thin_check
                    }
                }

                if usage.used_meta > usage.total_meta - META_LOWATER {
                    // TODO: Extend meta device
                }

                if usage.used_data > usage.total_data - DATA_LOWATER {
                    // TODO: Extend data device
                }
            }
            ThinPoolStatus::Fail => {
                // TODO: Take pool offline?
                // TODO: Run thin_check
            }
        }

        for fs in self.filesystems.iter_mut() {
            match fs.check(&dm) {
                Ok(f_status) => {
                    if let FilesystemStatus::Failed = f_status {
                        // TODO: recover fs? (how?)
                    }
                }
                Err(_e) => error!("fs.check() failed"),
            }
        }
    }
}

impl Pool for StratPool {
    fn create_filesystems<'a, 'b>(&'a mut self,
                                  specs: &[&'b str])
                                  -> EngineResult<Vec<(&'b str, FilesystemUuid)>> {
        let names: HashSet<_, RandomState> = HashSet::from_iter(specs);
        for name in names.iter() {
            if self.filesystems.contains_name(name) {
                return Err(EngineError::Engine(ErrorEnum::AlreadyExists, name.to_string()));
            }
        }

        let mut result = Vec::new();
        for name in names.iter() {
            let uuid = Uuid::new_v4();
            let dm = try!(DM::new());
            let new_filesystem = try!(StratFilesystem::new(uuid, name, &dm, &mut self.thin_pool));
            self.filesystems.insert(new_filesystem);
            result.push((**name, uuid));
        }

        Ok(result)
    }

    fn add_blockdevs(&mut self, paths: &[&Path], force: bool) -> EngineResult<Vec<PathBuf>> {
        let devices = try!(resolve_devices(paths));
        let bds = try!(initialize(&self.pool_uuid, devices, MIN_MDA_SECTORS, force));
        let bdev_paths = bds.iter().map(|p| p.devnode.clone()).collect();
        for bd in bds {
            self.block_devs.insert(bd.devnode.clone(), bd);
        }
        try!(self.write_metadata());
        Ok(bdev_paths)
    }

    fn destroy(mut self) -> EngineResult<()> {
        let dm = try!(DM::new());
        try!(self.thin_pool.teardown(&dm));

        for (_, bd) in self.block_devs.drain() {
            try!(bd.wipe_metadata());
        }

        Ok(())
    }

    fn destroy_filesystems<'a, 'b>(&'a mut self,
                                   fs_uuids: &[&'b FilesystemUuid])
                                   -> EngineResult<Vec<&'b FilesystemUuid>> {
        destroy_filesystems!{self; fs_uuids}
    }

    fn rename_filesystem(&mut self,
                         uuid: &FilesystemUuid,
                         new_name: &str)
                         -> EngineResult<RenameAction> {
        rename_filesystem!{self; uuid; new_name}
    }

    fn rename(&mut self, name: &str) {
        self.name = name.to_owned();
    }

    fn get_filesystem(&mut self, uuid: &FilesystemUuid) -> Option<&mut Filesystem> {
        get_filesystem!(self; uuid)
    }
}

impl HasUuid for StratPool {
    fn uuid(&self) -> &FilesystemUuid {
        &self.pool_uuid
    }
}

impl HasName for StratPool {
    fn name(&self) -> &str {
        &self.name
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::collections::HashMap;
use std::iter::FromIterator;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;

use serde_json;
use uuid::Uuid;

use devicemapper::{Device, DM, DmName, DmNameBuf, Sectors};

use super::super::engine::{Filesystem, BlockDev, HasName, HasUuid, Pool};
use super::super::errors::{EngineError, EngineResult, ErrorEnum};
use super::super::types::{DevUuid, FilesystemUuid, PoolUuid, RenameAction, Redundancy};

use super::blockdevmgr::BlockDevMgr;
use super::metadata::MIN_MDA_SECTORS;
use super::serde_structs::{PoolSave, Recordable};
use super::setup::{get_blockdevs, get_metadata};
use super::thinpool::{ThinPool, ThinPoolSizeParams};

pub use super::thinpool::{DATA_BLOCK_SIZE, DATA_LOWATER, INITIAL_DATA_SIZE};

#[derive(Debug)]
pub struct StratPool {
    name: String,
    pool_uuid: PoolUuid,
    block_devs: BlockDevMgr,
    redundancy: Redundancy,
    thin_pool: ThinPool,
}

impl StratPool {
    /// Initialize a Stratis Pool.
    /// 1. Initialize the block devices specified by paths.
    /// 2. Set up thinpool device to back filesystems.
    pub fn initialize(dm: &DM,
                      name: &str,
                      paths: &[&Path],
                      redundancy: Redundancy,
                      force: bool)
                      -> EngineResult<StratPool> {
        let pool_uuid = Uuid::new_v4();

        let mut block_mgr = BlockDevMgr::initialize(pool_uuid, paths, MIN_MDA_SECTORS, force)?;

        let thinpool = ThinPool::new(pool_uuid,
                                     dm,
                                     &ThinPoolSizeParams::default(),
                                     DATA_BLOCK_SIZE,
                                     DATA_LOWATER,
                                     &mut block_mgr);
        let thinpool = match thinpool {
            Ok(thinpool) => thinpool,
            Err(err) => {
                let _ = block_mgr.destroy_all();
                return Err(err);
            }
        };

        let mut pool = StratPool {
            name: name.to_owned(),
            pool_uuid,
            block_devs: block_mgr,
            redundancy,
            thin_pool: thinpool,
        };

        pool.write_metadata()?;

        Ok(pool)
    }

    /// Setup a StratPool using its UUID and the list of devnodes it has.
    pub fn setup(dm: &DM,
                 uuid: PoolUuid,
                 devnodes: &HashMap<Device, PathBuf>)
                 -> EngineResult<StratPool> {
        let metadata = get_metadata(uuid, devnodes)?
            .ok_or_else(|| {
                            EngineError::Engine(ErrorEnum::NotFound,
                                                format!("no metadata for pool {}", uuid))
                        })?;
        let bd_mgr = BlockDevMgr::new(uuid, get_blockdevs(uuid, &metadata, devnodes)?, None);
        let thinpool = ThinPool::setup(dm,
                                       uuid,
                                       metadata.thinpool_dev.data_block_size,
                                       DATA_LOWATER,
                                       &metadata.flex_devs,
                                       &bd_mgr)?;

        Ok(StratPool {
               name: metadata.name,
               pool_uuid: uuid,
               block_devs: bd_mgr,
               redundancy: Redundancy::NONE,
               thin_pool: thinpool,
           })
    }

    /// Write current metadata to pool members.
    pub fn write_metadata(&mut self) -> EngineResult<()> {
        let data = serde_json::to_string(&self.record())?;
        self.block_devs.save_state(data.as_bytes())
    }

    pub fn check(&mut self) -> EngineResult<()> {
        // FIXME: The context should not be created here as this is not
        // a public method. Ideally the context should be created in the
        // invoking method, Engine::check(). However, since we hope that
        // method will go away entirely, we just fix half of the problem
        // with this method, and leave the rest alone.
        if self.thin_pool.check(&DM::new()?, &mut self.block_devs)? {
            self.write_metadata()?;
        }
        Ok(())
    }

    /// Teardown a pool.
    pub fn teardown(self) -> EngineResult<()> {
        self.thin_pool.teardown(&DM::new()?)
    }

    pub fn has_filesystems(&self) -> bool {
        self.thin_pool.has_filesystems()
    }

    /// The names of DM devices belonging to this pool that may generate events
    pub fn get_eventing_dev_names(&self) -> Vec<DmNameBuf> {
        self.thin_pool.get_eventing_dev_names()
    }

    /// Called when a DM device in this pool has generated an event.
    // TODO: Just check the device that evented. Currently checks
    // everything.
    pub fn event_on(&mut self, dm_name: &DmName) -> EngineResult<()> {
        assert!(self.thin_pool
                    .get_eventing_dev_names()
                    .iter()
                    .any(|x| dm_name == &**x));
        if self.thin_pool.check(&DM::new()?, &mut self.block_devs)? {
            self.write_metadata()?;
        }
        Ok(())
    }
}

impl Pool for StratPool {
    fn create_filesystems<'a, 'b>(&'a mut self,
                                  specs: &[(&'b str, Option<Sectors>)])
                                  -> EngineResult<Vec<(&'b str, FilesystemUuid)>> {
        let names: HashMap<_, _> = HashMap::from_iter(specs.iter().map(|&tup| (tup.0, tup.1)));
        for name in names.keys() {
            if self.thin_pool
                   .get_mut_filesystem_by_name(*name)
                   .is_some() {
                return Err(EngineError::Engine(ErrorEnum::AlreadyExists, name.to_string()));
            }
        }

        // TODO: Roll back on filesystem initialization failure.
        let dm = DM::new()?;
        let pool_name = self.name().to_owned();
        let mut result = Vec::new();
        for (name, size) in names {
            let fs_uuid = self.thin_pool
                .create_filesystem(&pool_name, name, &dm, size)?;
            result.push((name, fs_uuid));
        }

        Ok(result)
    }

    fn add_blockdevs(&mut self, paths: &[&Path], force: bool) -> EngineResult<Vec<DevUuid>> {
        let bdev_info = self.block_devs.add(paths, force)?;
        self.write_metadata()?;
        Ok(bdev_info)
    }

    fn destroy(self) -> EngineResult<()> {
        self.thin_pool.teardown(&DM::new()?)?;
        self.block_devs.destroy_all()?;
        Ok(())
    }

    fn destroy_filesystems<'a>(&'a mut self,
                               fs_uuids: &[FilesystemUuid])
                               -> EngineResult<Vec<FilesystemUuid>> {
        let dm = DM::new()?;

        let mut removed = Vec::new();
        for &uuid in fs_uuids {
            self.thin_pool
                .destroy_filesystem(&dm, &self.name, uuid)?;
            removed.push(uuid);
        }

        Ok(removed)
    }

    fn rename_filesystem(&mut self,
                         uuid: FilesystemUuid,
                         new_name: &str)
                         -> EngineResult<RenameAction> {
        self.thin_pool
            .rename_filesystem(&self.name, uuid, new_name)
    }

    fn snapshot_filesystem(&mut self,
                           origin_uuid: FilesystemUuid,
                           snapshot_name: &str)
                           -> EngineResult<FilesystemUuid> {
        let pool_name = self.name().to_owned();
        self.thin_pool
            .snapshot_filesystem(&DM::new()?, &pool_name, origin_uuid, snapshot_name)
    }

    fn rename(&mut self, name: &str) {
        self.name = name.to_owned();
    }

    fn total_physical_size(&self) -> Sectors {
        self.block_devs.current_capacity()
    }

    fn total_physical_used(&self) -> EngineResult<Sectors> {
        self.thin_pool
            .total_physical_used()
            .and_then(|v| Ok(v + self.block_devs.metadata_size()))
    }

    fn filesystems(&self) -> Vec<&Filesystem> {
        self.thin_pool.filesystems()
    }

    fn get_filesystem(&self, uuid: FilesystemUuid) -> Option<&Filesystem> {
        self.thin_pool
            .get_filesystem_by_uuid(uuid)
            .map(|fs| fs as &Filesystem)
    }

    fn get_mut_filesystem(&mut self, uuid: FilesystemUuid) -> Option<&mut Filesystem> {
        self.thin_pool
            .get_mut_filesystem_by_uuid(uuid)
            .map(|fs| fs as &mut Filesystem)
    }

    fn blockdevs(&self) -> Vec<&BlockDev> {
        self.block_devs.blockdevs()
    }

    fn get_blockdev(&self, uuid: DevUuid) -> Option<&BlockDev> {
        self.block_devs.get_blockdev_by_uuid(uuid)
    }

    fn get_mut_blockdev(&mut self, uuid: DevUuid) -> Option<&mut BlockDev> {
        self.block_devs.get_mut_blockdev_by_uuid(uuid)
    }

    fn save_state(&mut self) -> EngineResult<()> {
        self.write_metadata()
    }
}

impl HasUuid for StratPool {
    fn uuid(&self) -> PoolUuid {
        self.pool_uuid
    }
}

impl HasName for StratPool {
    fn name(&self) -> &str {
        &self.name
    }
}

impl Recordable<PoolSave> for StratPool {
    fn record(&self) -> PoolSave {
        PoolSave {
            name: self.name.clone(),
            block_devs: self.block_devs.record(),
            flex_devs: self.thin_pool.record(),
            thinpool_dev: self.thin_pool.record(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::super::super::types::Redundancy;

    use super::super::setup::find_all;
    use super::super::tests::{loopbacked, real};

    use super::*;

    /// Verify that metadata can be read from pools.
    /// 1. Split paths into two separate sets.
    /// 2. Create pools from the two sets.
    /// 3. Use find_all() to get the devices in the pool.
    /// 4. Use get_metadata to find metadata for each pool and verify
    /// correctness.
    /// 5. Teardown the engine and repeat.
    fn test_basic_metadata(paths: &[&Path]) {
        assert!(paths.len() > 1);

        let (paths1, paths2) = paths.split_at(paths.len() / 2);
        let dm = DM::new().unwrap();

        let name1 = "name1";
        let pool1 = StratPool::initialize(&dm, &name1, paths1, Redundancy::NONE, false).unwrap();
        let uuid1 = pool1.uuid();
        let metadata1 = pool1.record();

        let name2 = "name2";
        let pool2 = StratPool::initialize(&dm, &name2, paths2, Redundancy::NONE, false).unwrap();
        let uuid2 = pool2.uuid();
        let metadata2 = pool2.record();

        let pools = find_all().unwrap();
        assert_eq!(pools.len(), 2);
        let devnodes1 = pools.get(&uuid1).unwrap();
        let devnodes2 = pools.get(&uuid2).unwrap();
        let pool_save1 = get_metadata(uuid1, devnodes1).unwrap().unwrap();
        let pool_save2 = get_metadata(uuid2, devnodes2).unwrap().unwrap();
        assert_eq!(pool_save1, metadata1);
        assert_eq!(pool_save2, metadata2);
        let blockdevs1 = get_blockdevs(uuid1, &pool_save1, devnodes1).unwrap();
        let blockdevs2 = get_blockdevs(uuid2, &pool_save2, devnodes2).unwrap();
        assert_eq!(blockdevs1.len(), pool_save1.block_devs.len());
        assert_eq!(blockdevs2.len(), pool_save2.block_devs.len());

        pool1.teardown().unwrap();
        pool2.teardown().unwrap();
        let pools = find_all().unwrap();
        assert_eq!(pools.len(), 2);
        let devnodes1 = pools.get(&uuid1).unwrap();
        let devnodes2 = pools.get(&uuid2).unwrap();
        let pool_save1 = get_metadata(uuid1, devnodes1).unwrap().unwrap();
        let pool_save2 = get_metadata(uuid2, devnodes2).unwrap().unwrap();
        assert_eq!(pool_save1, metadata1);
        assert_eq!(pool_save2, metadata2);
        let blockdevs1 = get_blockdevs(uuid1, &pool_save1, devnodes1).unwrap();
        let blockdevs2 = get_blockdevs(uuid2, &pool_save2, devnodes2).unwrap();
        assert_eq!(blockdevs1.len(), pool_save1.block_devs.len());
        assert_eq!(blockdevs2.len(), pool_save2.block_devs.len());
    }

    #[test]
    pub fn loop_test_basic_metadata() {
        loopbacked::test_with_spec(loopbacked::DeviceLimits::Range(2, 3), test_basic_metadata);
    }

    #[test]
    pub fn real_test_basic_metadata() {
        real::test_with_spec(real::DeviceLimits::AtLeast(2), test_basic_metadata);
    }
    /// Verify that a pool with no devices does not have the minimum amount of
    /// space required.
    fn test_empty_pool(paths: &[&Path]) -> () {
        assert_eq!(paths.len(), 0);
        let dm = DM::new().unwrap();
        assert!(match StratPool::initialize(&dm,
                                            "stratis_test_pool",
                                            paths,
                                            Redundancy::NONE,
                                            true)
                              .unwrap_err() {
                    EngineError::Engine(ErrorEnum::Invalid, _) => true,
                    _ => false,
                });
    }

    #[test]
    pub fn loop_test_empty_pool() {
        loopbacked::test_with_spec(loopbacked::DeviceLimits::Exactly(0), test_empty_pool);
    }

    #[test]
    pub fn real_test_empty_pool() {
        real::test_with_spec(real::DeviceLimits::Exactly(0), test_empty_pool);
    }
}
<commit_msg>Add a test to verify that no metadata sectors are being allocated<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::collections::HashMap;
use std::iter::FromIterator;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;

use serde_json;
use uuid::Uuid;

use devicemapper::{Device, DM, DmName, DmNameBuf, Sectors};

use super::super::engine::{Filesystem, BlockDev, HasName, HasUuid, Pool};
use super::super::errors::{EngineError, EngineResult, ErrorEnum};
use super::super::types::{DevUuid, FilesystemUuid, PoolUuid, RenameAction, Redundancy};

use super::blockdevmgr::BlockDevMgr;
use super::metadata::MIN_MDA_SECTORS;
use super::serde_structs::{PoolSave, Recordable};
use super::setup::{get_blockdevs, get_metadata};
use super::thinpool::{ThinPool, ThinPoolSizeParams};

pub use super::thinpool::{DATA_BLOCK_SIZE, DATA_LOWATER, INITIAL_DATA_SIZE};

#[derive(Debug)]
pub struct StratPool {
    name: String,
    pool_uuid: PoolUuid,
    block_devs: BlockDevMgr,
    redundancy: Redundancy,
    thin_pool: ThinPool,
}

impl StratPool {
    /// Initialize a Stratis Pool.
    /// 1. Initialize the block devices specified by paths.
    /// 2. Set up thinpool device to back filesystems.
    pub fn initialize(dm: &DM,
                      name: &str,
                      paths: &[&Path],
                      redundancy: Redundancy,
                      force: bool)
                      -> EngineResult<StratPool> {
        let pool_uuid = Uuid::new_v4();

        let mut block_mgr = BlockDevMgr::initialize(pool_uuid, paths, MIN_MDA_SECTORS, force)?;

        let thinpool = ThinPool::new(pool_uuid,
                                     dm,
                                     &ThinPoolSizeParams::default(),
                                     DATA_BLOCK_SIZE,
                                     DATA_LOWATER,
                                     &mut block_mgr);
        let thinpool = match thinpool {
            Ok(thinpool) => thinpool,
            Err(err) => {
                let _ = block_mgr.destroy_all();
                return Err(err);
            }
        };

        let mut pool = StratPool {
            name: name.to_owned(),
            pool_uuid,
            block_devs: block_mgr,
            redundancy,
            thin_pool: thinpool,
        };

        pool.write_metadata()?;

        Ok(pool)
    }

    /// Setup a StratPool using its UUID and the list of devnodes it has.
    pub fn setup(dm: &DM,
                 uuid: PoolUuid,
                 devnodes: &HashMap<Device, PathBuf>)
                 -> EngineResult<StratPool> {
        let metadata = get_metadata(uuid, devnodes)?
            .ok_or_else(|| {
                            EngineError::Engine(ErrorEnum::NotFound,
                                                format!("no metadata for pool {}", uuid))
                        })?;
        let bd_mgr = BlockDevMgr::new(uuid, get_blockdevs(uuid, &metadata, devnodes)?, None);
        let thinpool = ThinPool::setup(dm,
                                       uuid,
                                       metadata.thinpool_dev.data_block_size,
                                       DATA_LOWATER,
                                       &metadata.flex_devs,
                                       &bd_mgr)?;

        Ok(StratPool {
               name: metadata.name,
               pool_uuid: uuid,
               block_devs: bd_mgr,
               redundancy: Redundancy::NONE,
               thin_pool: thinpool,
           })
    }

    /// Write current metadata to pool members.
    pub fn write_metadata(&mut self) -> EngineResult<()> {
        let data = serde_json::to_string(&self.record())?;
        self.block_devs.save_state(data.as_bytes())
    }

    pub fn check(&mut self) -> EngineResult<()> {
        // FIXME: The context should not be created here as this is not
        // a public method. Ideally the context should be created in the
        // invoking method, Engine::check(). However, since we hope that
        // method will go away entirely, we just fix half of the problem
        // with this method, and leave the rest alone.
        if self.thin_pool.check(&DM::new()?, &mut self.block_devs)? {
            self.write_metadata()?;
        }
        Ok(())
    }

    /// Teardown a pool.
    pub fn teardown(self) -> EngineResult<()> {
        self.thin_pool.teardown(&DM::new()?)
    }

    pub fn has_filesystems(&self) -> bool {
        self.thin_pool.has_filesystems()
    }

    /// The names of DM devices belonging to this pool that may generate events
    pub fn get_eventing_dev_names(&self) -> Vec<DmNameBuf> {
        self.thin_pool.get_eventing_dev_names()
    }

    /// Called when a DM device in this pool has generated an event.
    // TODO: Just check the device that evented. Currently checks
    // everything.
    pub fn event_on(&mut self, dm_name: &DmName) -> EngineResult<()> {
        assert!(self.thin_pool
                    .get_eventing_dev_names()
                    .iter()
                    .any(|x| dm_name == &**x));
        if self.thin_pool.check(&DM::new()?, &mut self.block_devs)? {
            self.write_metadata()?;
        }
        Ok(())
    }
}

impl Pool for StratPool {
    fn create_filesystems<'a, 'b>(&'a mut self,
                                  specs: &[(&'b str, Option<Sectors>)])
                                  -> EngineResult<Vec<(&'b str, FilesystemUuid)>> {
        let names: HashMap<_, _> = HashMap::from_iter(specs.iter().map(|&tup| (tup.0, tup.1)));
        for name in names.keys() {
            if self.thin_pool
                   .get_mut_filesystem_by_name(*name)
                   .is_some() {
                return Err(EngineError::Engine(ErrorEnum::AlreadyExists, name.to_string()));
            }
        }

        // TODO: Roll back on filesystem initialization failure.
        let dm = DM::new()?;
        let pool_name = self.name().to_owned();
        let mut result = Vec::new();
        for (name, size) in names {
            let fs_uuid = self.thin_pool
                .create_filesystem(&pool_name, name, &dm, size)?;
            result.push((name, fs_uuid));
        }

        Ok(result)
    }

    fn add_blockdevs(&mut self, paths: &[&Path], force: bool) -> EngineResult<Vec<DevUuid>> {
        let bdev_info = self.block_devs.add(paths, force)?;
        self.write_metadata()?;
        Ok(bdev_info)
    }

    fn destroy(self) -> EngineResult<()> {
        self.thin_pool.teardown(&DM::new()?)?;
        self.block_devs.destroy_all()?;
        Ok(())
    }

    fn destroy_filesystems<'a>(&'a mut self,
                               fs_uuids: &[FilesystemUuid])
                               -> EngineResult<Vec<FilesystemUuid>> {
        let dm = DM::new()?;

        let mut removed = Vec::new();
        for &uuid in fs_uuids {
            self.thin_pool
                .destroy_filesystem(&dm, &self.name, uuid)?;
            removed.push(uuid);
        }

        Ok(removed)
    }

    fn rename_filesystem(&mut self,
                         uuid: FilesystemUuid,
                         new_name: &str)
                         -> EngineResult<RenameAction> {
        self.thin_pool
            .rename_filesystem(&self.name, uuid, new_name)
    }

    fn snapshot_filesystem(&mut self,
                           origin_uuid: FilesystemUuid,
                           snapshot_name: &str)
                           -> EngineResult<FilesystemUuid> {
        let pool_name = self.name().to_owned();
        self.thin_pool
            .snapshot_filesystem(&DM::new()?, &pool_name, origin_uuid, snapshot_name)
    }

    fn rename(&mut self, name: &str) {
        self.name = name.to_owned();
    }

    fn total_physical_size(&self) -> Sectors {
        self.block_devs.current_capacity()
    }

    fn total_physical_used(&self) -> EngineResult<Sectors> {
        self.thin_pool
            .total_physical_used()
            .and_then(|v| Ok(v + self.block_devs.metadata_size()))
    }

    fn filesystems(&self) -> Vec<&Filesystem> {
        self.thin_pool.filesystems()
    }

    fn get_filesystem(&self, uuid: FilesystemUuid) -> Option<&Filesystem> {
        self.thin_pool
            .get_filesystem_by_uuid(uuid)
            .map(|fs| fs as &Filesystem)
    }

    fn get_mut_filesystem(&mut self, uuid: FilesystemUuid) -> Option<&mut Filesystem> {
        self.thin_pool
            .get_mut_filesystem_by_uuid(uuid)
            .map(|fs| fs as &mut Filesystem)
    }

    fn blockdevs(&self) -> Vec<&BlockDev> {
        self.block_devs.blockdevs()
    }

    fn get_blockdev(&self, uuid: DevUuid) -> Option<&BlockDev> {
        self.block_devs.get_blockdev_by_uuid(uuid)
    }

    fn get_mut_blockdev(&mut self, uuid: DevUuid) -> Option<&mut BlockDev> {
        self.block_devs.get_mut_blockdev_by_uuid(uuid)
    }

    fn save_state(&mut self) -> EngineResult<()> {
        self.write_metadata()
    }
}

impl HasUuid for StratPool {
    fn uuid(&self) -> PoolUuid {
        self.pool_uuid
    }
}

impl HasName for StratPool {
    fn name(&self) -> &str {
        &self.name
    }
}

impl Recordable<PoolSave> for StratPool {
    fn record(&self) -> PoolSave {
        PoolSave {
            name: self.name.clone(),
            block_devs: self.block_devs.record(),
            flex_devs: self.thin_pool.record(),
            thinpool_dev: self.thin_pool.record(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::super::super::types::Redundancy;

    use super::super::setup::find_all;
    use super::super::tests::{loopbacked, real};

    use super::*;

    /// Verify that metadata can be read from pools.
    /// 1. Split paths into two separate sets.
    /// 2. Create pools from the two sets.
    /// 3. Use find_all() to get the devices in the pool.
    /// 4. Use get_metadata to find metadata for each pool and verify
    /// correctness.
    /// 5. Teardown the engine and repeat.
    fn test_basic_metadata(paths: &[&Path]) {
        assert!(paths.len() > 1);

        let (paths1, paths2) = paths.split_at(paths.len() / 2);
        let dm = DM::new().unwrap();

        let name1 = "name1";
        let pool1 = StratPool::initialize(&dm, &name1, paths1, Redundancy::NONE, false).unwrap();
        let uuid1 = pool1.uuid();
        let metadata1 = pool1.record();

        let name2 = "name2";
        let pool2 = StratPool::initialize(&dm, &name2, paths2, Redundancy::NONE, false).unwrap();
        let uuid2 = pool2.uuid();
        let metadata2 = pool2.record();

        let pools = find_all().unwrap();
        assert_eq!(pools.len(), 2);
        let devnodes1 = pools.get(&uuid1).unwrap();
        let devnodes2 = pools.get(&uuid2).unwrap();
        let pool_save1 = get_metadata(uuid1, devnodes1).unwrap().unwrap();
        let pool_save2 = get_metadata(uuid2, devnodes2).unwrap().unwrap();
        assert_eq!(pool_save1, metadata1);
        assert_eq!(pool_save2, metadata2);
        let blockdevs1 = get_blockdevs(uuid1, &pool_save1, devnodes1).unwrap();
        let blockdevs2 = get_blockdevs(uuid2, &pool_save2, devnodes2).unwrap();
        assert_eq!(blockdevs1.len(), pool_save1.block_devs.len());
        assert_eq!(blockdevs2.len(), pool_save2.block_devs.len());

        for mut blockdev in blockdevs1 {
            let (amt, seg) = blockdev.request_space(Sectors(1));
            assert_eq!(amt, Sectors(1));
            assert!(seg[0].0 >= blockdev.metadata_size());
        }

        for mut blockdev in blockdevs2 {
            let (amt, seg) = blockdev.request_space(Sectors(1));
            assert_eq!(amt, Sectors(1));
            assert!(seg[0].0 >= blockdev.metadata_size());
        }

        pool1.teardown().unwrap();
        pool2.teardown().unwrap();
        let pools = find_all().unwrap();
        assert_eq!(pools.len(), 2);
        let devnodes1 = pools.get(&uuid1).unwrap();
        let devnodes2 = pools.get(&uuid2).unwrap();
        let pool_save1 = get_metadata(uuid1, devnodes1).unwrap().unwrap();
        let pool_save2 = get_metadata(uuid2, devnodes2).unwrap().unwrap();
        assert_eq!(pool_save1, metadata1);
        assert_eq!(pool_save2, metadata2);
        let blockdevs1 = get_blockdevs(uuid1, &pool_save1, devnodes1).unwrap();
        let blockdevs2 = get_blockdevs(uuid2, &pool_save2, devnodes2).unwrap();
        assert_eq!(blockdevs1.len(), pool_save1.block_devs.len());
        assert_eq!(blockdevs2.len(), pool_save2.block_devs.len());

        for mut blockdev in blockdevs1 {
            let (amt, seg) = blockdev.request_space(Sectors(1));
            assert_eq!(amt, Sectors(1));
            assert!(seg[0].0 >= blockdev.metadata_size());
        }

        for mut blockdev in blockdevs2 {
            let (amt, seg) = blockdev.request_space(Sectors(1));
            assert_eq!(amt, Sectors(1));
            assert!(seg[0].0 >= blockdev.metadata_size());
        }
    }

    #[test]
    pub fn loop_test_basic_metadata() {
        loopbacked::test_with_spec(loopbacked::DeviceLimits::Range(2, 3), test_basic_metadata);
    }

    #[test]
    pub fn real_test_basic_metadata() {
        real::test_with_spec(real::DeviceLimits::AtLeast(2), test_basic_metadata);
    }
    /// Verify that a pool with no devices does not have the minimum amount of
    /// space required.
    fn test_empty_pool(paths: &[&Path]) -> () {
        assert_eq!(paths.len(), 0);
        let dm = DM::new().unwrap();
        assert!(match StratPool::initialize(&dm,
                                            "stratis_test_pool",
                                            paths,
                                            Redundancy::NONE,
                                            true)
                              .unwrap_err() {
                    EngineError::Engine(ErrorEnum::Invalid, _) => true,
                    _ => false,
                });
    }

    #[test]
    pub fn loop_test_empty_pool() {
        loopbacked::test_with_spec(loopbacked::DeviceLimits::Exactly(0), test_empty_pool);
    }

    #[test]
    pub fn real_test_empty_pool() {
        real::test_with_spec(real::DeviceLimits::Exactly(0), test_empty_pool);
    }
}
<|endoftext|>