Revision dcb93971
b/daemons/ganeti-noded | ||
---|---|---|
235 | 235 |
def perspective_node_leave_cluster(self, params): |
236 | 236 |
return backend.LeaveCluster() |
237 | 237 |
|
238 |
def perspective_node_volumes(self, params): |
|
239 |
return backend.NodeVolumes() |
|
240 |
|
|
238 | 241 |
# cluster -------------------------- |
239 | 242 |
|
240 | 243 |
def perspective_version(self,params): |
b/lib/backend.py | ||
---|---|---|
260 | 260 |
return utils.ListVolumeGroups() |
261 | 261 |
|
262 | 262 |
|
263 |
def NodeVolumes(): |
|
264 |
"""List all volumes on this node. |
|
265 |
|
|
266 |
""" |
|
267 |
result = utils.RunCmd(["lvs", "--noheadings", "--units=m", "--nosuffix", |
|
268 |
"--separator=|", |
|
269 |
"--options=lv_name,lv_size,devices,vg_name"]) |
|
270 |
if result.failed: |
|
271 |
logger.Error("Failed to list logical volumes, lvs output: %s" % |
|
272 |
result.output) |
|
273 |
return {} |
|
274 |
|
|
275 |
def parse_dev(dev): |
|
276 |
if '(' in dev: |
|
277 |
return dev.split('(')[0] |
|
278 |
else: |
|
279 |
return dev |
|
280 |
|
|
281 |
def map_line(line): |
|
282 |
return { |
|
283 |
'name': line[0].strip(), |
|
284 |
'size': line[1].strip(), |
|
285 |
'dev': parse_dev(line[2].strip()), |
|
286 |
'vg': line[3].strip(), |
|
287 |
} |
|
288 |
|
|
289 |
return [map_line(line.split('|')) for line in result.output.splitlines()] |
|
290 |
|
|
291 |
|
|
263 | 292 |
def BridgesExist(bridges_list): |
264 | 293 |
"""Check if a list of bridges exist on the current node |
265 | 294 |
|
b/lib/cli.py | ||
---|---|---|
39 | 39 |
__all__ = ["DEBUG_OPT", "NOHDR_OPT", "SEP_OPT", "GenericMain", "SubmitOpCode", |
40 | 40 |
"cli_option", |
41 | 41 |
"ARGS_NONE", "ARGS_FIXED", "ARGS_ATLEAST", "ARGS_ANY", "ARGS_ONE", |
42 |
"USEUNITS_OPT"] |
|
42 |
"USEUNITS_OPT", "FIELDS_OPT"]
|
|
43 | 43 |
|
44 | 44 |
DEBUG_OPT = make_option("-d", "--debug", default=False, |
45 | 45 |
action="store_true", |
... | ... | |
58 | 58 |
action="store_true", dest="human_readable", |
59 | 59 |
help="Print sizes in human readable format") |
60 | 60 |
|
61 |
FIELDS_OPT = make_option("-o", "--output", dest="output", action="store", |
|
62 |
type="string", help="Select output fields", |
|
63 |
metavar="FIELDS") |
|
64 |
|
|
61 | 65 |
_LOCK_OPT = make_option("--lock-retries", default=None, |
62 | 66 |
type="int", help=SUPPRESS_HELP) |
63 | 67 |
|
b/lib/cmdlib.py | ||
---|---|---|
164 | 164 |
return |
165 | 165 |
|
166 | 166 |
|
167 |
def _GetWantedNodes(lu, nodes): |
|
168 |
if nodes is not None and not isinstance(nodes, list): |
|
169 |
raise errors.OpPrereqError, "Invalid argument type 'nodes'" |
|
170 |
|
|
171 |
if nodes: |
|
172 |
wanted_nodes = [] |
|
173 |
|
|
174 |
for name in nodes: |
|
175 |
node = lu.cfg.GetNodeInfo(lu.cfg.ExpandNodeName(name)) |
|
176 |
if node is None: |
|
177 |
raise errors.OpPrereqError, ("No such node name '%s'" % name) |
|
178 |
wanted_nodes.append(node) |
|
179 |
|
|
180 |
return wanted_nodes |
|
181 |
else: |
|
182 |
return [lu.cfg.GetNodeInfo(name) for name in lu.cfg.GetNodeList()] |
|
183 |
|
|
184 |
|
|
185 |
def _CheckOutputFields(static, dynamic, selected): |
|
186 |
static_fields = frozenset(static) |
|
187 |
dynamic_fields = frozenset(dynamic) |
|
188 |
|
|
189 |
all_fields = static_fields | dynamic_fields |
|
190 |
|
|
191 |
if not all_fields.issuperset(selected): |
|
192 |
raise errors.OpPrereqError, ("Unknown output fields selected: %s" |
|
193 |
% ",".join(frozenset(selected). |
|
194 |
difference(all_fields))) |
|
195 |
|
|
196 |
|
|
167 | 197 |
def _UpdateEtcHosts(fullnode, ip): |
168 | 198 |
"""Ensure a node has a correct entry in /etc/hosts. |
169 | 199 |
|
... | ... | |
1028 | 1058 |
This checks that the fields required are valid output fields. |
1029 | 1059 |
|
1030 | 1060 |
""" |
1031 |
self.static_fields = frozenset(["name", "pinst", "sinst", "pip", "sip"]) |
|
1032 | 1061 |
self.dynamic_fields = frozenset(["dtotal", "dfree", |
1033 | 1062 |
"mtotal", "mnode", "mfree"]) |
1034 |
self.all_fields = self.static_fields | self.dynamic_fields |
|
1035 | 1063 |
|
1036 |
if not self.all_fields.issuperset(self.op.output_fields): |
|
1037 |
raise errors.OpPrereqError, ("Unknown output fields selected: %s" |
|
1038 |
% ",".join(frozenset(self.op.output_fields). |
|
1039 |
difference(self.all_fields))) |
|
1064 |
_CheckOutputFields(static=["name", "pinst", "sinst", "pip", "sip"], |
|
1065 |
dynamic=self.dynamic_fields, |
|
1066 |
selected=self.op.output_fields) |
|
1040 | 1067 |
|
1041 | 1068 |
|
1042 | 1069 |
def Exec(self, feedback_fn): |
... | ... | |
1106 | 1133 |
return output |
1107 | 1134 |
|
1108 | 1135 |
|
1136 |
class LUQueryNodeVolumes(NoHooksLU): |
|
1137 |
"""Logical unit for getting volumes on node(s). |
|
1138 |
|
|
1139 |
""" |
|
1140 |
_OP_REQP = ["nodes", "output_fields"] |
|
1141 |
|
|
1142 |
def CheckPrereq(self): |
|
1143 |
"""Check prerequisites. |
|
1144 |
|
|
1145 |
This checks that the fields required are valid output fields. |
|
1146 |
|
|
1147 |
""" |
|
1148 |
self.nodes = _GetWantedNodes(self, self.op.nodes) |
|
1149 |
|
|
1150 |
_CheckOutputFields(static=["node"], |
|
1151 |
dynamic=["phys", "vg", "name", "size", "instance"], |
|
1152 |
selected=self.op.output_fields) |
|
1153 |
|
|
1154 |
|
|
1155 |
def Exec(self, feedback_fn): |
|
1156 |
"""Computes the list of nodes and their attributes. |
|
1157 |
|
|
1158 |
""" |
|
1159 |
nodenames = utils.NiceSort([node.name for node in self.nodes]) |
|
1160 |
volumes = rpc.call_node_volumes(nodenames) |
|
1161 |
|
|
1162 |
ilist = [self.cfg.GetInstanceInfo(iname) for iname |
|
1163 |
in self.cfg.GetInstanceList()] |
|
1164 |
|
|
1165 |
lv_by_node = dict([(inst, inst.MapLVsByNode()) for inst in ilist]) |
|
1166 |
|
|
1167 |
output = [] |
|
1168 |
for node in nodenames: |
|
1169 |
node_vols = volumes[node][:] |
|
1170 |
node_vols.sort(key=lambda vol: vol['dev']) |
|
1171 |
|
|
1172 |
for vol in node_vols: |
|
1173 |
node_output = [] |
|
1174 |
for field in self.op.output_fields: |
|
1175 |
if field == "node": |
|
1176 |
val = node |
|
1177 |
elif field == "phys": |
|
1178 |
val = vol['dev'] |
|
1179 |
elif field == "vg": |
|
1180 |
val = vol['vg'] |
|
1181 |
elif field == "name": |
|
1182 |
val = vol['name'] |
|
1183 |
elif field == "size": |
|
1184 |
val = int(float(vol['size'])) |
|
1185 |
elif field == "instance": |
|
1186 |
for inst in ilist: |
|
1187 |
if node not in lv_by_node[inst]: |
|
1188 |
continue |
|
1189 |
if vol['name'] in lv_by_node[inst][node]: |
|
1190 |
val = inst.name |
|
1191 |
break |
|
1192 |
else: |
|
1193 |
val = '-' |
|
1194 |
else: |
|
1195 |
raise errors.ParameterError, field |
|
1196 |
node_output.append(str(val)) |
|
1197 |
|
|
1198 |
output.append(node_output) |
|
1199 |
|
|
1200 |
return output |
|
1201 |
|
|
1202 |
|
|
1109 | 1203 |
def _CheckNodesDirs(node_list, paths): |
1110 | 1204 |
"""Verify if the given nodes have the same files. |
1111 | 1205 |
|
... | ... | |
1477 | 1571 |
""" |
1478 | 1572 |
if not os.path.exists(self.op.filename): |
1479 | 1573 |
raise errors.OpPrereqError("No such filename '%s'" % self.op.filename) |
1480 |
if self.op.nodes: |
|
1481 |
nodes = self.op.nodes |
|
1482 |
else: |
|
1483 |
nodes = self.cfg.GetNodeList() |
|
1484 |
self.nodes = [] |
|
1485 |
for node in nodes: |
|
1486 |
nname = self.cfg.ExpandNodeName(node) |
|
1487 |
if nname is None: |
|
1488 |
raise errors.OpPrereqError, ("Node '%s' is unknown." % node) |
|
1489 |
self.nodes.append(nname) |
|
1574 |
|
|
1575 |
self.nodes = _GetWantedNodes(self, self.op.nodes) |
|
1490 | 1576 |
|
1491 | 1577 |
def Exec(self, feedback_fn): |
1492 | 1578 |
"""Copy a file from master to some nodes. |
... | ... | |
1540 | 1626 |
It checks that the given list of nodes is valid. |
1541 | 1627 |
|
1542 | 1628 |
""" |
1543 |
if self.op.nodes: |
|
1544 |
nodes = self.op.nodes |
|
1545 |
else: |
|
1546 |
nodes = self.cfg.GetNodeList() |
|
1547 |
self.nodes = [] |
|
1548 |
for node in nodes: |
|
1549 |
nname = self.cfg.ExpandNodeName(node) |
|
1550 |
if nname is None: |
|
1551 |
raise errors.OpPrereqError, ("Node '%s' is unknown." % node) |
|
1552 |
self.nodes.append(nname) |
|
1629 |
self.nodes = _GetWantedNodes(self, self.op.nodes) |
|
1553 | 1630 |
|
1554 | 1631 |
def Exec(self, feedback_fn): |
1555 | 1632 |
"""Run a command on some nodes. |
... | ... | |
1557 | 1634 |
""" |
1558 | 1635 |
data = [] |
1559 | 1636 |
for node in self.nodes: |
1560 |
result = utils.RunCmd(["ssh", node, self.op.command]) |
|
1561 |
data.append((node, result.cmd, result.output, result.exit_code)) |
|
1637 |
result = utils.RunCmd(["ssh", node.name, self.op.command])
|
|
1638 |
data.append((node.name, result.cmd, result.output, result.exit_code))
|
|
1562 | 1639 |
|
1563 | 1640 |
return data |
1564 | 1641 |
|
... | ... | |
1884 | 1961 |
"""Logical unit for querying instances. |
1885 | 1962 |
|
1886 | 1963 |
""" |
1887 |
OP_REQP = ["output_fields"] |
|
1964 |
_OP_REQP = ["output_fields"]
|
|
1888 | 1965 |
|
1889 | 1966 |
def CheckPrereq(self): |
1890 | 1967 |
"""Check prerequisites. |
... | ... | |
1892 | 1969 |
This checks that the fields required are valid output fields. |
1893 | 1970 |
|
1894 | 1971 |
""" |
1895 |
|
|
1896 |
self.static_fields = frozenset(["name", "os", "pnode", "snodes", |
|
1897 |
"admin_state", "admin_ram", |
|
1898 |
"disk_template", "ip", "mac", "bridge"]) |
|
1899 | 1972 |
self.dynamic_fields = frozenset(["oper_state", "oper_ram"]) |
1900 |
self.all_fields = self.static_fields | self.dynamic_fields |
|
1901 |
|
|
1902 |
if not self.all_fields.issuperset(self.op.output_fields): |
|
1903 |
raise errors.OpPrereqError, ("Unknown output fields selected: %s" |
|
1904 |
% ",".join(frozenset(self.op.output_fields). |
|
1905 |
difference(self.all_fields))) |
|
1973 |
_CheckOutputFields(static=["name", "os", "pnode", "snodes", |
|
1974 |
"admin_state", "admin_ram", |
|
1975 |
"disk_template", "ip", "mac", "bridge"], |
|
1976 |
dynamic=self.dynamic_fields, |
|
1977 |
selected=self.op.output_fields) |
|
1906 | 1978 |
|
1907 | 1979 |
def Exec(self, feedback_fn): |
1908 | 1980 |
"""Computes the list of nodes and their attributes. |
... | ... | |
3074 | 3146 |
This only checks the optional node list against the existing names. |
3075 | 3147 |
|
3076 | 3148 |
""" |
3077 |
if not isinstance(self.op.nodes, list): |
|
3078 |
raise errors.OpPrereqError, "Invalid argument type 'nodes'" |
|
3079 |
if self.op.nodes: |
|
3080 |
self.wanted_nodes = [] |
|
3081 |
names = self.op.nodes |
|
3082 |
for name in names: |
|
3083 |
node = self.cfg.GetNodeInfo(self.cfg.ExpandNodeName(name)) |
|
3084 |
if node is None: |
|
3085 |
raise errors.OpPrereqError, ("No such node name '%s'" % name) |
|
3086 |
self.wanted_nodes.append(node) |
|
3087 |
else: |
|
3088 |
self.wanted_nodes = [self.cfg.GetNodeInfo(name) for name |
|
3089 |
in self.cfg.GetNodeList()] |
|
3090 |
return |
|
3149 |
self.wanted_nodes = _GetWantedNodes(self, self.op.nodes) |
|
3091 | 3150 |
|
3092 | 3151 |
def Exec(self, feedback_fn): |
3093 | 3152 |
"""Compute and return the list of nodes. |
... | ... | |
3214 | 3273 |
"""Check that the nodelist contains only existing nodes. |
3215 | 3274 |
|
3216 | 3275 |
""" |
3217 |
nodes = getattr(self.op, "nodes", None) |
|
3218 |
if not nodes: |
|
3219 |
self.op.nodes = self.cfg.GetNodeList() |
|
3220 |
else: |
|
3221 |
expnodes = [self.cfg.ExpandNodeName(node) for node in nodes] |
|
3222 |
if expnodes.count(None) > 0: |
|
3223 |
raise errors.OpPrereqError, ("At least one of the given nodes %s" |
|
3224 |
" is unknown" % self.op.nodes) |
|
3225 |
self.op.nodes = expnodes |
|
3276 |
self.nodes = _GetWantedNodes(self, getattr(self.op, "nodes", None)) |
|
3226 | 3277 |
|
3227 | 3278 |
def Exec(self, feedback_fn): |
3228 |
|
|
3229 | 3279 |
"""Compute the list of all the exported system images. |
3230 | 3280 |
|
3231 | 3281 |
Returns: |
... | ... | |
3234 | 3284 |
that node. |
3235 | 3285 |
|
3236 | 3286 |
""" |
3237 |
return rpc.call_export_list(self.op.nodes)
|
|
3287 |
return rpc.call_export_list([node.name for node in self.nodes])
|
|
3238 | 3288 |
|
3239 | 3289 |
|
3240 | 3290 |
class LUExportInstance(LogicalUnit): |
b/lib/mcpu.py | ||
---|---|---|
59 | 59 |
opcodes.OpAddNode: cmdlib.LUAddNode, |
60 | 60 |
opcodes.OpQueryNodes: cmdlib.LUQueryNodes, |
61 | 61 |
opcodes.OpQueryNodeData: cmdlib.LUQueryNodeData, |
62 |
opcodes.OpQueryNodeVolumes: cmdlib.LUQueryNodeVolumes, |
|
62 | 63 |
opcodes.OpRemoveNode: cmdlib.LURemoveNode, |
63 | 64 |
# instance lu |
64 | 65 |
opcodes.OpCreateInstance: cmdlib.LUCreateInstance, |
b/lib/opcodes.py | ||
---|---|---|
122 | 122 |
__slots__ = ["nodes"] |
123 | 123 |
|
124 | 124 |
|
125 |
class OpQueryNodeVolumes(OpCode): |
|
126 |
"""Get list of volumes on node.""" |
|
127 |
OP_ID = "OP_NODE_QUERYVOLS" |
|
128 |
__slots__ = ["nodes", "output_fields"] |
|
129 |
|
|
130 |
|
|
125 | 131 |
# instance opcodes |
126 | 132 |
|
127 | 133 |
class OpCreateInstance(OpCode): |
b/lib/rpc.py | ||
---|---|---|
762 | 762 |
c.connect(node) |
763 | 763 |
c.run() |
764 | 764 |
return c.getresult().get(node, False) |
765 |
|
|
766 |
|
|
767 |
def call_node_volumes(node_list): |
|
768 |
"""Gets all volumes on node(s). |
|
769 |
|
|
770 |
This is a multi-node call. |
|
771 |
|
|
772 |
""" |
|
773 |
c = Client("node_volumes", []) |
|
774 |
c.connect_list(node_list) |
|
775 |
c.run() |
|
776 |
return c.getresult() |
b/man/gnt-node.sgml | ||
---|---|---|
261 | 261 |
</para> |
262 | 262 |
</refsect2> |
263 | 263 |
|
264 |
<refsect2> |
|
265 |
<title>VOLUMES</title> |
|
266 |
|
|
267 |
<cmdsynopsis> |
|
268 |
<command>volumes</command> |
|
269 |
<arg rep="repeat"><replaceable>node</replaceable></arg> |
|
270 |
</cmdsynopsis> |
|
271 |
|
|
272 |
<para> |
|
273 |
Lists all logical volumes and their physical disks from the node(s) |
|
274 |
provided. |
|
275 |
</para> |
|
276 |
|
|
277 |
<para> |
|
278 |
Example: |
|
279 |
<screen> |
|
280 |
# gnt-node volumes node5.example.com |
|
281 |
Node PhysDev VG Name Size Instance |
|
282 |
node1.example.com /dev/hdc1 xenvg instance1.example.com-sda_11000.meta 128 instance1.example.com |
|
283 |
node1.example.com /dev/hdc1 xenvg instance1.example.com-sda_11001.data 256 instance1.example.com |
|
284 |
</screen> |
|
285 |
</para> |
|
286 |
</refsect2> |
|
287 |
|
|
264 | 288 |
</refsect1> |
265 | 289 |
|
266 | 290 |
&footer; |
b/scripts/gnt-instance | ||
---|---|---|
489 | 489 |
'info': (ShowInstanceConfig, ARGS_ANY, [DEBUG_OPT], "[<instance>...]", |
490 | 490 |
"Show information on the specified instance"), |
491 | 491 |
'list': (ListInstances, ARGS_NONE, |
492 |
[DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, |
|
493 |
make_option("-o", "--output", dest="output", action="store", |
|
494 |
type="string", help="Select output fields", |
|
495 |
metavar="FIELDS") |
|
496 |
], |
|
492 |
[DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT], |
|
497 | 493 |
"", "Lists the instances and their status"), |
498 | 494 |
'remove': (RemoveInstance, ARGS_ONE, [DEBUG_OPT, force_opt], |
499 | 495 |
"[-f] <instance>", "Shuts down the instance and removes it"), |
b/scripts/gnt-node | ||
---|---|---|
130 | 130 |
SubmitOpCode(op) |
131 | 131 |
|
132 | 132 |
|
133 |
def ListVolumes(opts, args): |
|
134 |
"""List logical volumes on node(s). |
|
135 |
|
|
136 |
""" |
|
137 |
if opts.output is None: |
|
138 |
selected_fields = ["node", "phys", "vg", |
|
139 |
"name", "size", "instance"] |
|
140 |
else: |
|
141 |
selected_fields = opts.output.split(",") |
|
142 |
|
|
143 |
op = opcodes.OpQueryNodeVolumes(nodes=args, output_fields=selected_fields) |
|
144 |
output = SubmitOpCode(op) |
|
145 |
|
|
146 |
mlens = [0 for name in selected_fields] |
|
147 |
format_fields = [] |
|
148 |
unitformat_fields = ("size",) |
|
149 |
for field in selected_fields: |
|
150 |
if field in unitformat_fields: |
|
151 |
format_fields.append("%*s") |
|
152 |
else: |
|
153 |
format_fields.append("%-*s") |
|
154 |
|
|
155 |
separator = opts.separator |
|
156 |
if "%" in separator: |
|
157 |
separator = separator.replace("%", "%%") |
|
158 |
format = separator.join(format_fields) |
|
159 |
|
|
160 |
for row in output: |
|
161 |
for idx, val in enumerate(row): |
|
162 |
if opts.human_readable and selected_fields[idx] in unitformat_fields: |
|
163 |
try: |
|
164 |
val = int(val) |
|
165 |
except ValueError: |
|
166 |
pass |
|
167 |
else: |
|
168 |
val = row[idx] = utils.FormatUnit(val) |
|
169 |
mlens[idx] = max(mlens[idx], len(val)) |
|
170 |
|
|
171 |
if not opts.no_headers: |
|
172 |
header_list = {"node": "Node", "phys": "PhysDev", |
|
173 |
"vg": "VG", "name": "Name", |
|
174 |
"size": "Size", "instance": "Instance"} |
|
175 |
args = [] |
|
176 |
for idx, name in enumerate(selected_fields): |
|
177 |
hdr = header_list[name] |
|
178 |
mlens[idx] = max(mlens[idx], len(hdr)) |
|
179 |
args.append(mlens[idx]) |
|
180 |
args.append(hdr) |
|
181 |
logger.ToStdout(format % tuple(args)) |
|
182 |
|
|
183 |
for row in output: |
|
184 |
args = [] |
|
185 |
for idx, val in enumerate(row): |
|
186 |
args.append(mlens[idx]) |
|
187 |
args.append(val) |
|
188 |
logger.ToStdout(format % tuple(args)) |
|
189 |
|
|
190 |
return 0 |
|
191 |
|
|
192 |
|
|
133 | 193 |
commands = { |
134 | 194 |
'add': (AddNode, ARGS_ONE, |
135 | 195 |
[DEBUG_OPT, |
... | ... | |
140 | 200 |
'info': (ShowNodeConfig, ARGS_ANY, [DEBUG_OPT], |
141 | 201 |
"[<node_name>...]", "Show information about the node(s)"), |
142 | 202 |
'list': (ListNodes, ARGS_NONE, |
143 |
[DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, |
|
144 |
make_option("-o", "--output", dest="output", action="store", |
|
145 |
type="string", help="Select output fields", |
|
146 |
metavar="FIELDS") |
|
147 |
], |
|
203 |
[DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT], |
|
148 | 204 |
"", "Lists the nodes in the cluster"), |
149 | 205 |
'remove': (RemoveNode, ARGS_ONE, [DEBUG_OPT], |
150 | 206 |
"<node_name>", "Removes a node from the cluster"), |
207 |
'volumes': (ListVolumes, ARGS_ANY, |
|
208 |
[DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT], |
|
209 |
"[<node_name>...]", "List logical volumes on node(s)"), |
|
151 | 210 |
} |
152 | 211 |
|
153 | 212 |
|
Also available in: Unified diff