Revision 620a85fd
b/lib/cmdlib.py | ||
---|---|---|
2618 | 2618 |
""" |
2619 | 2619 |
_OP_REQP = ["nodes", "storage_type", "output_fields"] |
2620 | 2620 |
REQ_BGL = False |
2621 |
_FIELDS_STATIC = utils.FieldSet("node")
|
|
2621 |
_FIELDS_STATIC = utils.FieldSet(constants.SF_NODE)
|
|
2622 | 2622 |
|
2623 | 2623 |
def ExpandNames(self): |
2624 | 2624 |
storage_type = self.op.storage_type |
2625 | 2625 |
|
2626 |
if storage_type not in constants.VALID_STORAGE_FIELDS:
|
|
2626 |
if storage_type not in constants.VALID_STORAGE_TYPES:
|
|
2627 | 2627 |
raise errors.OpPrereqError("Unknown storage type: %s" % storage_type) |
2628 | 2628 |
|
2629 |
dynamic_fields = constants.VALID_STORAGE_FIELDS[storage_type] |
|
2630 |
|
|
2631 | 2629 |
_CheckOutputFields(static=self._FIELDS_STATIC, |
2632 |
dynamic=utils.FieldSet(*dynamic_fields),
|
|
2630 |
dynamic=utils.FieldSet(*constants.VALID_STORAGE_FIELDS),
|
|
2633 | 2631 |
selected=self.op.output_fields) |
2634 | 2632 |
|
2635 | 2633 |
self.needed_locks = {} |
... | ... | |
2661 | 2659 |
else: |
2662 | 2660 |
fields = [constants.SF_NAME] + self.op.output_fields |
2663 | 2661 |
|
2664 |
# Never ask for node as it's only known to the LU |
|
2665 |
while "node" in fields: |
|
2666 |
fields.remove("node") |
|
2662 |
# Never ask for node or type as it's only known to the LU |
|
2663 |
for extra in [constants.SF_NODE, constants.SF_TYPE]: |
|
2664 |
while extra in fields: |
|
2665 |
fields.remove(extra) |
|
2667 | 2666 |
|
2668 | 2667 |
field_idx = dict([(name, idx) for (idx, name) in enumerate(fields)]) |
2669 | 2668 |
name_idx = field_idx[constants.SF_NAME] |
... | ... | |
2693 | 2692 |
out = [] |
2694 | 2693 |
|
2695 | 2694 |
for field in self.op.output_fields: |
2696 |
if field == "node":
|
|
2695 |
if field == constants.SF_NODE:
|
|
2697 | 2696 |
val = node |
2697 |
elif field == constants.SF_TYPE: |
|
2698 |
val = self.op.storage_type |
|
2698 | 2699 |
elif field in field_idx: |
2699 | 2700 |
val = row[field_idx[field]] |
2700 | 2701 |
else: |
... | ... | |
2722 | 2723 |
self.op.node_name = node_name |
2723 | 2724 |
|
2724 | 2725 |
storage_type = self.op.storage_type |
2725 |
if storage_type not in constants.VALID_STORAGE_FIELDS:
|
|
2726 |
if storage_type not in constants.VALID_STORAGE_TYPES:
|
|
2726 | 2727 |
raise errors.OpPrereqError("Unknown storage type: %s" % storage_type) |
2727 | 2728 |
|
2728 | 2729 |
def ExpandNames(self): |
b/lib/constants.py | ||
---|---|---|
196 | 196 |
ST_LVM_VG = "lvm-vg" |
197 | 197 |
|
198 | 198 |
# Storage fields |
199 |
# first two are valid in LU context only, not passed to backend |
|
200 |
SF_NODE = "node" |
|
201 |
SF_TYPE = "type" |
|
202 |
# and the rest are valid in backend |
|
199 | 203 |
SF_NAME = "name" |
200 | 204 |
SF_SIZE = "size" |
201 | 205 |
SF_FREE = "free" |
... | ... | |
206 | 210 |
SO_FIX_CONSISTENCY = "fix-consistency" |
207 | 211 |
|
208 | 212 |
# Available fields per storage type |
209 |
VALID_STORAGE_FIELDS = { |
|
210 |
ST_FILE: frozenset([SF_NAME, SF_USED, SF_FREE]), |
|
211 |
ST_LVM_PV: frozenset([SF_NAME, SF_SIZE, SF_USED, SF_FREE, SF_ALLOCATABLE]), |
|
212 |
ST_LVM_VG: frozenset([SF_NAME, SF_SIZE]), |
|
213 |
} |
|
213 |
VALID_STORAGE_FIELDS = frozenset([SF_NAME, SF_TYPE, SF_SIZE, |
|
214 |
SF_USED, SF_FREE, SF_ALLOCATABLE]) |
|
215 |
|
|
216 |
VALID_STORAGE_TYPES = frozenset([ST_FILE, ST_LVM_PV, ST_LVM_VG]) |
|
214 | 217 |
|
215 | 218 |
MODIFIABLE_STORAGE_FIELDS = { |
216 | 219 |
ST_LVM_PV: frozenset([SF_ALLOCATABLE]), |
b/lib/storage.py | ||
---|---|---|
125 | 125 |
else: |
126 | 126 |
dirsize = None |
127 | 127 |
|
128 |
if constants.SF_FREE in fields: |
|
129 |
fsfree = utils.GetFreeFilesystemSpace(path)
|
|
128 |
if constants.SF_FREE in fields or constants.SF_SIZE in fields:
|
|
129 |
fsstats = utils.GetFilesystemStats(path)
|
|
130 | 130 |
else: |
131 |
fsfree = None
|
|
131 |
fsstats = None
|
|
132 | 132 |
|
133 | 133 |
# Make sure to update constants.VALID_STORAGE_FIELDS when changing fields. |
134 | 134 |
for field_name in fields: |
... | ... | |
139 | 139 |
values.append(dirsize) |
140 | 140 |
|
141 | 141 |
elif field_name == constants.SF_FREE: |
142 |
values.append(fsfree) |
|
142 |
values.append(fsstats[1]) |
|
143 |
|
|
144 |
elif field_name == constants.SF_SIZE: |
|
145 |
values.append(fsstats[0]) |
|
146 |
|
|
147 |
elif field_name == constants.SF_ALLOCATABLE: |
|
148 |
values.append(True) |
|
143 | 149 |
|
144 | 150 |
else: |
145 | 151 |
raise errors.StorageError("Unknown field: %r" % field_name) |
... | ... | |
150 | 156 |
class _LvmBase(_Base): |
151 | 157 |
"""Base class for LVM storage containers. |
152 | 158 |
|
159 |
@cvar LIST_FIELDS: list of tuples consisting of three elements: SF_* |
|
160 |
constants, lvm command output fields (list), and conversion |
|
161 |
function or static value (for static value, the lvm output field |
|
162 |
can be an empty list) |
|
163 |
|
|
153 | 164 |
""" |
154 | 165 |
LIST_SEP = "|" |
155 | 166 |
LIST_COMMAND = None |
... | ... | |
200 | 211 |
except IndexError: |
201 | 212 |
raise errors.StorageError("Unknown field: %r" % field_name) |
202 | 213 |
|
203 |
(_, lvm_name, _) = fields_def[idx] |
|
214 |
(_, lvm_names, _) = fields_def[idx]
|
|
204 | 215 |
|
205 |
lvm_fields.append(lvm_name)
|
|
216 |
lvm_fields.extend(lvm_names)
|
|
206 | 217 |
|
207 | 218 |
return utils.UniqueSequence(lvm_fields) |
208 | 219 |
|
... | ... | |
231 | 242 |
row = [] |
232 | 243 |
|
233 | 244 |
for field_name in wanted_field_names: |
234 |
(_, lvm_name, convert_fn) = fields_def[field_to_idx[field_name]]
|
|
245 |
(_, lvm_names, mapper) = fields_def[field_to_idx[field_name]]
|
|
235 | 246 |
|
236 |
value = raw_data[lvm_name_to_idx[lvm_name]]
|
|
247 |
values = [raw_data[lvm_name_to_idx[i]] for i in lvm_names]
|
|
237 | 248 |
|
238 |
if convert_fn: |
|
239 |
value = convert_fn(value) |
|
249 |
if callable(mapper): |
|
250 |
# we got a function, call it with all the declared fields |
|
251 |
val = mapper(*values) |
|
252 |
elif len(values) == 1: |
|
253 |
# we don't have a function, but we had a single field |
|
254 |
# declared, pass it unchanged |
|
255 |
val = values[0] |
|
256 |
else: |
|
257 |
# let's make sure there are no fields declared (cannot map > |
|
258 |
# 1 field without a function) |
|
259 |
assert not values, "LVM storage has multi-fields without a function" |
|
260 |
val = mapper |
|
240 | 261 |
|
241 |
row.append(value)
|
|
262 |
row.append(val) |
|
242 | 263 |
|
243 | 264 |
data.append(row) |
244 | 265 |
|
... | ... | |
297 | 318 |
fields = line.strip().split(sep) |
298 | 319 |
|
299 | 320 |
if len(fields) != fieldcount: |
321 |
logging.warning("Invalid line returned from lvm command: %s", line) |
|
300 | 322 |
continue |
301 | 323 |
|
302 | 324 |
yield fields |
... | ... | |
318 | 340 |
# Make sure to update constants.VALID_STORAGE_FIELDS when changing field |
319 | 341 |
# definitions. |
320 | 342 |
LIST_FIELDS = [ |
321 |
(constants.SF_NAME, "pv_name", None),
|
|
322 |
(constants.SF_SIZE, "pv_size", _ParseSize),
|
|
323 |
(constants.SF_USED, "pv_used", _ParseSize),
|
|
324 |
(constants.SF_FREE, "pv_free", _ParseSize),
|
|
325 |
(constants.SF_ALLOCATABLE, "pv_attr", _GetAllocatable),
|
|
343 |
(constants.SF_NAME, ["pv_name"], None),
|
|
344 |
(constants.SF_SIZE, ["pv_size"], _ParseSize),
|
|
345 |
(constants.SF_USED, ["pv_used"], _ParseSize),
|
|
346 |
(constants.SF_FREE, ["pv_free"], _ParseSize),
|
|
347 |
(constants.SF_ALLOCATABLE, ["pv_attr"], _GetAllocatable),
|
|
326 | 348 |
] |
327 | 349 |
|
328 | 350 |
def _SetAllocatable(self, name, allocatable): |
... | ... | |
372 | 394 |
# Make sure to update constants.VALID_STORAGE_FIELDS when changing field |
373 | 395 |
# definitions. |
374 | 396 |
LIST_FIELDS = [ |
375 |
(constants.SF_NAME, "vg_name", None), |
|
376 |
(constants.SF_SIZE, "vg_size", _ParseSize), |
|
397 |
(constants.SF_NAME, ["vg_name"], None), |
|
398 |
(constants.SF_SIZE, ["vg_size"], _ParseSize), |
|
399 |
(constants.SF_FREE, ["vg_free"], _ParseSize), |
|
400 |
(constants.SF_USED, ["vg_size", "vg_free"], |
|
401 |
lambda x, y: _ParseSize(x) - _ParseSize(y)), |
|
402 |
(constants.SF_ALLOCATABLE, [], True), |
|
377 | 403 |
] |
378 | 404 |
|
379 | 405 |
def _RemoveMissing(self, name): |
b/lib/utils.py | ||
---|---|---|
1890 | 1890 |
return BytesToMebibyte(size) |
1891 | 1891 |
|
1892 | 1892 |
|
1893 |
def GetFreeFilesystemSpace(path):
|
|
1894 |
"""Returns the free space on a filesystem. |
|
1893 |
def GetFilesystemStats(path):
|
|
1894 |
"""Returns the total and free space on a filesystem.
|
|
1895 | 1895 |
|
1896 | 1896 |
@type path: string |
1897 | 1897 |
@param path: Path on filesystem to be examined |
1898 | 1898 |
@rtype: int |
1899 |
@return: Free space in mebibytes
|
|
1899 |
@return: tuple of (Total space, Free space) in mebibytes
|
|
1900 | 1900 |
|
1901 | 1901 |
""" |
1902 | 1902 |
st = os.statvfs(path) |
1903 | 1903 |
|
1904 |
return BytesToMebibyte(st.f_bavail * st.f_frsize) |
|
1904 |
fsize = BytesToMebibyte(st.f_bavail * st.f_frsize) |
|
1905 |
tsize = BytesToMebibyte(st.f_blocks * st.f_frsize) |
|
1906 |
return (tsize, fsize) |
|
1905 | 1907 |
|
1906 | 1908 |
|
1907 | 1909 |
def LockedMethod(fn): |
b/man/gnt-node.sgml | ||
---|---|---|
794 | 794 |
<para> |
795 | 795 |
The <option>--storage-type</option> option can be used to choose a |
796 | 796 |
storage unit type. Possible choices are <literal>lvm-pv</literal>, |
797 |
<literal>lvm-vg</literal> or <literal>file</literal>. Depending on the |
|
798 |
storage type, the available output fields change. |
|
797 |
<literal>lvm-vg</literal> or <literal>file</literal>. |
|
799 | 798 |
</para> |
800 | 799 |
|
801 | 800 |
<para> |
... | ... | |
809 | 808 |
</listitem> |
810 | 809 |
</varlistentry> |
811 | 810 |
<varlistentry> |
811 |
<term>type</term> |
|
812 |
<listitem> |
|
813 |
<simpara>the type of the storage unit (currently just |
|
814 |
what is passed in via |
|
815 |
<option>--storage-type</option>)</simpara> |
|
816 |
</listitem> |
|
817 |
</varlistentry> |
|
818 |
<varlistentry> |
|
812 | 819 |
<term>name</term> |
813 | 820 |
<listitem> |
814 |
<simpara>the physical drive name</simpara>
|
|
821 |
<simpara>the path/identifier of the storage unit</simpara>
|
|
815 | 822 |
</listitem> |
816 | 823 |
</varlistentry> |
817 | 824 |
<varlistentry> |
818 | 825 |
<term>size</term> |
819 | 826 |
<listitem> |
820 | 827 |
<simpara> |
821 |
the physical drive size |
|
822 |
(<literal>lvm-pv</literal> and <literal>lvm-vg</literal> only) |
|
828 |
total size of the unit; for the file type see a note below |
|
823 | 829 |
</simpara> |
824 | 830 |
</listitem> |
825 | 831 |
</varlistentry> |
... | ... | |
827 | 833 |
<term>used</term> |
828 | 834 |
<listitem> |
829 | 835 |
<simpara> |
830 |
used disk space |
|
831 |
(<literal>lvm-pv</literal> and <literal>file</literal> only) |
|
836 |
used space in the unit; for the file type see a note below |
|
832 | 837 |
</simpara> |
833 | 838 |
</listitem> |
834 | 839 |
</varlistentry> |
... | ... | |
837 | 842 |
<listitem> |
838 | 843 |
<simpara> |
839 | 844 |
available disk space |
840 |
(<literal>lvm-pv</literal> and <literal>file</literal> only) |
|
841 | 845 |
</simpara> |
842 | 846 |
</listitem> |
843 | 847 |
</varlistentry> |
... | ... | |
845 | 849 |
<term>allocatable</term> |
846 | 850 |
<listitem> |
847 | 851 |
<simpara> |
848 |
whether physical volume is allocatable |
|
849 |
(<literal>lvm-pv</literal> only) |
|
852 |
whether we the unit is available for allocation |
|
853 |
(only <literal>lvm-pv</literal> can change this |
|
854 |
setting, the other types always report true) |
|
850 | 855 |
</simpara> |
851 | 856 |
</listitem> |
852 | 857 |
</varlistentry> |
... | ... | |
854 | 859 |
</para> |
855 | 860 |
|
856 | 861 |
<para> |
862 |
Note that for the <quote>file</quote> type, the total disk |
|
863 |
space might not equal to the sum of used and free, due to the |
|
864 |
method Ganeti uses to compute each of them. The total and free |
|
865 |
values are computed as the total and free space values for the |
|
866 |
filesystem to which the directory belongs, but the used space |
|
867 |
is computed from the used space under that directory |
|
868 |
<emphasis>only</emphasis>, which might not be necessarily the |
|
869 |
root of the filesystem, and as such there could be files |
|
870 |
outside the file storage directory using disk space and |
|
871 |
causing a mismatch in the values. |
|
872 |
</para> |
|
873 |
|
|
874 |
<para> |
|
857 | 875 |
Example: |
858 | 876 |
<screen> |
859 |
# gnt-node list-storage node5.example.com
|
|
860 |
Node Name Size Used Free
|
|
861 |
node5.example.com /dev/sda7 673.8G 0M 673.8G
|
|
862 |
node5.example.com /dev/sdb1 698.6G 1.3G 697.4G
|
|
877 |
node1# gnt-node list-storage node2
|
|
878 |
Node Type Name Size Used Free Allocatable
|
|
879 |
node2 lvm-pv /dev/sda7 673.8G 1.5G 672.3G Y
|
|
880 |
node2 lvm-pv /dev/sdb1 698.6G 0M 698.6G Y
|
|
863 | 881 |
</screen> |
864 | 882 |
</para> |
865 | 883 |
</refsect2> |
b/scripts/gnt-node | ||
---|---|---|
41 | 41 |
"pinst_cnt", "sinst_cnt", |
42 | 42 |
] |
43 | 43 |
|
44 |
|
|
45 |
#: default list of field for L{ListStorage} |
|
46 |
_LIST_STOR_DEF_FIELDS = [ |
|
47 |
constants.SF_NODE, |
|
48 |
constants.SF_TYPE, |
|
49 |
constants.SF_NAME, |
|
50 |
constants.SF_SIZE, |
|
51 |
constants.SF_USED, |
|
52 |
constants.SF_FREE, |
|
53 |
constants.SF_ALLOCATABLE, |
|
54 |
] |
|
55 |
|
|
56 |
|
|
44 | 57 |
#: headers (and full field list for L{ListNodes} |
45 | 58 |
_LIST_HEADERS = { |
46 | 59 |
"name": "Node", "pinst_cnt": "Pinst", "sinst_cnt": "Sinst", |
... | ... | |
59 | 72 |
"ctime": "CTime", "mtime": "MTime", "uuid": "UUID" |
60 | 73 |
} |
61 | 74 |
|
75 |
|
|
76 |
#: headers (and full field list for L{ListStorage} |
|
77 |
_LIST_STOR_HEADERS = { |
|
78 |
constants.SF_NODE: "Node", |
|
79 |
constants.SF_TYPE: "Type", |
|
80 |
constants.SF_NAME: "Name", |
|
81 |
constants.SF_SIZE: "Size", |
|
82 |
constants.SF_USED: "Used", |
|
83 |
constants.SF_FREE: "Free", |
|
84 |
constants.SF_ALLOCATABLE: "Allocatable", |
|
85 |
} |
|
86 |
|
|
87 |
|
|
62 | 88 |
#: User-facing storage unit types |
63 | 89 |
_USER_STORAGE_TYPE = { |
64 | 90 |
constants.ST_FILE: "file", |
... | ... | |
476 | 502 |
|
477 | 503 |
storage_type = ConvertStorageType(opts.user_storage_type) |
478 | 504 |
|
479 |
default_fields = { |
|
480 |
constants.ST_FILE: [ |
|
481 |
constants.SF_NAME, |
|
482 |
constants.SF_USED, |
|
483 |
constants.SF_FREE, |
|
484 |
], |
|
485 |
constants.ST_LVM_PV: [ |
|
486 |
constants.SF_NAME, |
|
487 |
constants.SF_SIZE, |
|
488 |
constants.SF_USED, |
|
489 |
constants.SF_FREE, |
|
490 |
], |
|
491 |
constants.ST_LVM_VG: [ |
|
492 |
constants.SF_NAME, |
|
493 |
constants.SF_SIZE, |
|
494 |
], |
|
495 |
} |
|
496 |
|
|
497 |
def_fields = ["node"] + default_fields[storage_type] |
|
498 | 505 |
if opts.output is None: |
499 |
selected_fields = def_fields
|
|
506 |
selected_fields = _LIST_STOR_DEF_FIELDS
|
|
500 | 507 |
elif opts.output.startswith("+"): |
501 |
selected_fields = def_fields + opts.output[1:].split(",")
|
|
508 |
selected_fields = _LIST_STOR_DEF_FIELDS + opts.output[1:].split(",")
|
|
502 | 509 |
else: |
503 | 510 |
selected_fields = opts.output.split(",") |
504 | 511 |
|
... | ... | |
509 | 516 |
|
510 | 517 |
if not opts.no_headers: |
511 | 518 |
headers = { |
512 |
"node": "Node", |
|
519 |
constants.SF_NODE: "Node", |
|
520 |
constants.SF_TYPE: "Type", |
|
513 | 521 |
constants.SF_NAME: "Name", |
514 | 522 |
constants.SF_SIZE: "Size", |
515 | 523 |
constants.SF_USED: "Used", |
... | ... | |
568 | 576 |
name=volume_name, |
569 | 577 |
changes=changes) |
570 | 578 |
SubmitOpCode(op) |
579 |
else: |
|
580 |
ToStderr("No changes to perform, exiting.") |
|
571 | 581 |
|
572 | 582 |
|
573 | 583 |
def RepairStorage(opts, args): |
... | ... | |
683 | 693 |
'list-storage': ( |
684 | 694 |
ListStorage, ARGS_MANY_NODES, |
685 | 695 |
[NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT, _STORAGE_TYPE_OPT], |
686 |
"[<node_name>...]", "List physical volumes on node(s)"), |
|
696 |
"[<node_name>...]", "List physical volumes on node(s). The available" |
|
697 |
" fields are (see the man page for details): %s." % |
|
698 |
(", ".join(_LIST_STOR_HEADERS))), |
|
687 | 699 |
'modify-storage': ( |
688 | 700 |
ModifyStorage, |
689 | 701 |
[ArgNode(min=1, max=1), |
Also available in: Unified diff