File: lv.ml

package info (click to toggle)
xen-api-libs 0.5.2-3
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 1,940 kB
  • sloc: ml: 13,925; sh: 2,930; ansic: 1,699; makefile: 1,240; python: 83
file content (174 lines) | stat: -rw-r--r-- 5,705 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
open Absty
open Fun
open Listext

type stat = 
    | Read
    | Write
    | Visible
	
and striped_segment = {
  st_stripe_size : int64; (* In sectors *)
  st_stripes : (string * int64) list; (* pv name * start extent *)
}

and linear_segment = {
  l_pv_name : string;
  l_pv_start_extent : int64;
}

and segclass = 
  | Linear of linear_segment
  | Striped of striped_segment

and segment = 
    { s_start_extent : int64; 
      s_extent_count : int64;
      s_cls : segclass; }

and logical_volume = {
  name : string;
  id : string;
  tags : Tag.t list;
  status : stat list;
  segments : segment list;
} with rpc

let status_to_string s =
  match s with
    | Read -> "READ"
    | Write -> "WRITE"
    | Visible -> "VISIBLE"

let status_of_string s =
  match s with
    | "READ" -> Read
    | "WRITE" -> Write
    | "VISIBLE" -> Visible
    | _ -> failwith "Bad LV status string"

let sort_segments s =
  List.sort (fun s1 s2 -> compare s1.s_start_extent s2.s_start_extent) s

let write_to_buffer b lv =
  let bprintf = Printf.bprintf in
  bprintf b "\n%s {\nid = \"%s\"\nstatus = [%s]\n" lv.name lv.id 
    (String.concat ", " (List.map (o quote status_to_string) lv.status));
  if List.length lv.tags > 0 then 
    bprintf b "tags = [%s]\n" (String.concat ", " (List.map (quote ++ Tag.string_of) lv.tags));
  bprintf b "segment_count = %d\n\n" (List.length lv.segments);
  Listext.List.iteri
    (fun i s -> 
      bprintf b "segment%d {\nstart_extent = %Ld\nextent_count = %Ld\n\n"
	(i+1) s.s_start_extent s.s_extent_count;
       match s.s_cls with
	 | Linear l ->
	     bprintf b "type = \"striped\"\nstripe_count = 1\t#linear\n\n";
	     bprintf b "stripes = [\n\"%s\", %Ld\n]\n}\n" l.l_pv_name l.l_pv_start_extent
	 | Striped st ->
	     let stripes = List.length st.st_stripes in
	     bprintf b "type = \"striped\"\nstripe_count = %d\nstripe_size = %Ld\n\nstripes = [\n"
	       stripes st.st_stripe_size;
	     List.iter (fun (pv,offset) -> bprintf b "%s, %Ld\n" (quote pv) offset) st.st_stripes;
	     bprintf b "]\n}\n") lv.segments;
  bprintf b "}\n"

let segment_of_metadata name config =
  let start_extent = expect_mapped_int "start_extent" config in
  let extent_count = expect_mapped_int "extent_count" config in
  let ty = expect_mapped_string "type" config in
  if ty<>"striped" then failwith (Printf.sprintf "Cannot handle LV segment type '%s'" ty);
  let stripes = expect_mapped_array "stripes" config in
  let rec handle_stripes ss =
    match ss with
      | name::offset::rest ->
	  let name = expect_string "name" name in
	  let offset = expect_int "offset" offset in
	  (name,offset)::handle_stripes rest
      | _ -> []
  in 
  {s_start_extent = start_extent;
   s_extent_count = extent_count;
   s_cls = 
      if List.length stripes = 2 then 
	match stripes with 
	  | [name;offset] ->
	      Linear { l_pv_name=expect_string "name" name;
		       l_pv_start_extent=expect_int "offset" offset }
	  | _ -> failwith "Invalid format of segment"
      else 
	let stripe_size = expect_mapped_int "stripe_size" config in
	let stripes = (handle_stripes stripes) in
	Striped {st_stripe_size=stripe_size;
		 st_stripes=stripes}
  }

(** Builds a logical_volume structure out of a name and metadata. *)
let of_metadata name config =
	let id = expect_mapped_string "id" config in
	let status = map_expected_mapped_array "status"
		(fun a -> status_of_string (expect_string "status" a)) config in
	let tags =
		List.map Tag.of_string
			(if List.mem_assoc "tags" config
			 then map_expected_mapped_array "tags" (expect_string "tags") config
			 else []) in
	let segments = filter_structs config in
	let segments = List.map
		(fun (a,_) ->
			 segment_of_metadata a (expect_mapped_struct a segments)) segments in
	{ name = name;
	  id = id;
	  status = status;
	  tags = tags;
	  segments = sort_segments segments }

let allocation_of_segment s =
  match s.s_cls with
    | Linear l ->
	[(l.l_pv_name, (l.l_pv_start_extent, s.s_extent_count))]
    | Striped st ->
(* LVM appears to always round up the number of extents allocated such
   that it's divisible by the number of stripes, so we always fully allocate
   each extent in each PV. Let's be tolerant to broken metadata when this
   isn't the case by rounding up rather than down, so partially allocated
   extents are included in the allocation *)
	let extent_count = s.s_extent_count in
	let nstripes = Int64.of_int (List.length st.st_stripes) in
	List.map (fun (name,start) ->
		    let allocated_extents = 
		      Int64.div 
			(Int64.sub 
			   (Int64.add 
			      extent_count nstripes) 1L) nstripes
		    in
		    (name,(start,allocated_extents)))
	  (st.st_stripes)

let allocation_of_lv lv =
  List.flatten 
    (List.map allocation_of_segment lv.segments)

let size_in_extents lv =
  List.fold_left (Int64.add) 0L
    (List.map (fun seg -> seg.s_extent_count) lv.segments)
	    
let reduce_size_to lv new_seg_count =
  let cur_size = size_in_extents lv in
  Debug.debug "Beginning reduce_size_to:";
  if cur_size < new_seg_count then (failwith (Printf.sprintf "Cannot reduce size: current size (%Ld) is less than requested size (%Ld)" cur_size new_seg_count));
  let rec doit segs left acc =
    match segs with 
      | s::ss ->
	  Debug.debug (Printf.sprintf "Lv.reduce_size_to: s.s_start_extent=%Ld s.s_extent_count=%Ld left=%Ld" 
			  s.s_start_extent s.s_extent_count left);
	  if left > s.s_extent_count then
	    doit ss (Int64.sub left s.s_extent_count) (s::acc)
	  else
	    {s with s_extent_count = left}::acc
      | _ -> acc
  in
  {lv with segments = sort_segments (doit lv.segments new_seg_count [])}

let increase_allocation lv new_segs =
  {lv with segments = sort_segments (lv.segments @ new_segs)}