Package ganeti :: Package cmdlib :: Module instance_query
[hide private]
[frames] | no frames]

Source Code for Module ganeti.cmdlib.instance_query

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc. 
  5  # 
  6  # This program is free software; you can redistribute it and/or modify 
  7  # it under the terms of the GNU General Public License as published by 
  8  # the Free Software Foundation; either version 2 of the License, or 
  9  # (at your option) any later version. 
 10  # 
 11  # This program is distributed in the hope that it will be useful, but 
 12  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 14  # General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU General Public License 
 17  # along with this program; if not, write to the Free Software 
 18  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
 19  # 02110-1301, USA. 
 20   
 21   
 22  """Logical units for querying instances.""" 
 23   
 24  import itertools 
 25  import logging 
 26  import operator 
 27   
 28  from ganeti import compat 
 29  from ganeti import constants 
 30  from ganeti import locking 
 31  from ganeti import qlang 
 32  from ganeti import query 
 33  from ganeti.cmdlib.base import QueryBase, NoHooksLU 
 34  from ganeti.cmdlib.common import ShareAll, GetWantedInstances, \ 
 35    CheckInstanceNodeGroups, CheckInstancesNodeGroups, AnnotateDiskParams 
 36  from ganeti.cmdlib.instance_operation import GetInstanceConsole 
 37  from ganeti.cmdlib.instance_utils import NICListToTuple 
 38   
 39  import ganeti.masterd.instance 
40 41 42 -class InstanceQuery(QueryBase):
43 FIELDS = query.INSTANCE_FIELDS 44
45 - def ExpandNames(self, lu):
46 lu.needed_locks = {} 47 lu.share_locks = ShareAll() 48 49 if self.names: 50 self.wanted = GetWantedInstances(lu, self.names) 51 else: 52 self.wanted = locking.ALL_SET 53 54 self.do_locking = (self.use_locking and 55 query.IQ_LIVE in self.requested_data) 56 if self.do_locking: 57 lu.needed_locks[locking.LEVEL_INSTANCE] = self.wanted 58 lu.needed_locks[locking.LEVEL_NODEGROUP] = [] 59 lu.needed_locks[locking.LEVEL_NODE] = [] 60 lu.needed_locks[locking.LEVEL_NETWORK] = [] 61 lu.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE 62 63 self.do_grouplocks = (self.do_locking and 64 query.IQ_NODES in self.requested_data)
65
66 - def DeclareLocks(self, lu, level):
67 if self.do_locking: 68 if level == locking.LEVEL_NODEGROUP and self.do_grouplocks: 69 assert not lu.needed_locks[locking.LEVEL_NODEGROUP] 70 71 # Lock all groups used by instances optimistically; this requires going 72 # via the node before it's locked, requiring verification later on 73 lu.needed_locks[locking.LEVEL_NODEGROUP] = \ 74 set(group_uuid 75 for instance_name in lu.owned_locks(locking.LEVEL_INSTANCE) 76 for group_uuid in lu.cfg.GetInstanceNodeGroups(instance_name)) 77 elif level == locking.LEVEL_NODE: 78 lu._LockInstancesNodes() # pylint: disable=W0212 79 80 elif level == locking.LEVEL_NETWORK: 81 lu.needed_locks[locking.LEVEL_NETWORK] = \ 82 frozenset(net_uuid 83 for instance_name in lu.owned_locks(locking.LEVEL_INSTANCE) 84 for net_uuid in lu.cfg.GetInstanceNetworks(instance_name))
85 86 @staticmethod
87 - def _CheckGroupLocks(lu):
88 owned_instances = frozenset(lu.owned_locks(locking.LEVEL_INSTANCE)) 89 owned_groups = frozenset(lu.owned_locks(locking.LEVEL_NODEGROUP)) 90 91 # Check if node groups for locked instances are still correct 92 for instance_name in owned_instances: 93 CheckInstanceNodeGroups(lu.cfg, instance_name, owned_groups)
94
95 - def _GetQueryData(self, lu):
96 """Computes the list of instances and their attributes. 97 98 """ 99 if self.do_grouplocks: 100 self._CheckGroupLocks(lu) 101 102 cluster = lu.cfg.GetClusterInfo() 103 all_info = lu.cfg.GetAllInstancesInfo() 104 105 instance_names = self._GetNames(lu, all_info.keys(), locking.LEVEL_INSTANCE) 106 107 instance_list = [all_info[name] for name in instance_names] 108 nodes = frozenset(itertools.chain(*(inst.all_nodes 109 for inst in instance_list))) 110 hv_list = list(set([inst.hypervisor for inst in instance_list])) 111 bad_nodes = [] 112 offline_nodes = [] 113 wrongnode_inst = set() 114 115 # Gather data as requested 116 if self.requested_data & set([query.IQ_LIVE, query.IQ_CONSOLE]): 117 live_data = {} 118 node_data = lu.rpc.call_all_instances_info(nodes, hv_list) 119 for name in nodes: 120 result = node_data[name] 121 if result.offline: 122 # offline nodes will be in both lists 123 assert result.fail_msg 124 offline_nodes.append(name) 125 if result.fail_msg: 126 bad_nodes.append(name) 127 elif result.payload: 128 for inst in result.payload: 129 if inst in all_info: 130 if all_info[inst].primary_node == name: 131 live_data.update(result.payload) 132 else: 133 wrongnode_inst.add(inst) 134 else: 135 # orphan instance; we don't list it here as we don't 136 # handle this case yet in the output of instance listing 137 logging.warning("Orphan instance '%s' found on node %s", 138 inst, name) 139 # else no instance is alive 140 else: 141 live_data = {} 142 143 if query.IQ_DISKUSAGE in self.requested_data: 144 gmi = ganeti.masterd.instance 145 disk_usage = dict((inst.name, 146 gmi.ComputeDiskSize(inst.disk_template, 147 [{constants.IDISK_SIZE: disk.size} 148 for disk in inst.disks])) 149 for inst in instance_list) 150 else: 151 disk_usage = None 152 153 if query.IQ_CONSOLE in self.requested_data: 154 consinfo = {} 155 for inst in instance_list: 156 if inst.name in live_data: 157 # Instance is running 158 consinfo[inst.name] = GetInstanceConsole(cluster, inst) 159 else: 160 consinfo[inst.name] = None 161 assert set(consinfo.keys()) == set(instance_names) 162 else: 163 consinfo = None 164 165 if query.IQ_NODES in self.requested_data: 166 node_names = set(itertools.chain(*map(operator.attrgetter("all_nodes"), 167 instance_list))) 168 nodes = dict(lu.cfg.GetMultiNodeInfo(node_names)) 169 groups = dict((uuid, lu.cfg.GetNodeGroup(uuid)) 170 for uuid in set(map(operator.attrgetter("group"), 171 nodes.values()))) 172 else: 173 nodes = None 174 groups = None 175 176 if query.IQ_NETWORKS in self.requested_data: 177 net_uuids = itertools.chain(*(lu.cfg.GetInstanceNetworks(i.name) 178 for i in instance_list)) 179 networks = dict((uuid, lu.cfg.GetNetwork(uuid)) for uuid in net_uuids) 180 else: 181 networks = None 182 183 return query.InstanceQueryData(instance_list, lu.cfg.GetClusterInfo(), 184 disk_usage, offline_nodes, bad_nodes, 185 live_data, wrongnode_inst, consinfo, 186 nodes, groups, networks)
187
188 189 -class LUInstanceQuery(NoHooksLU):
190 """Logical unit for querying instances. 191 192 """ 193 # pylint: disable=W0142 194 REQ_BGL = False 195
196 - def CheckArguments(self):
197 self.iq = InstanceQuery(qlang.MakeSimpleFilter("name", self.op.names), 198 self.op.output_fields, self.op.use_locking)
199
200 - def ExpandNames(self):
201 self.iq.ExpandNames(self)
202
203 - def DeclareLocks(self, level):
204 self.iq.DeclareLocks(self, level)
205
206 - def Exec(self, feedback_fn):
207 return self.iq.OldStyleQuery(self)
208
209 210 -class LUInstanceQueryData(NoHooksLU):
211 """Query runtime instance data. 212 213 """ 214 REQ_BGL = False 215
216 - def ExpandNames(self):
217 self.needed_locks = {} 218 219 # Use locking if requested or when non-static information is wanted 220 if not (self.op.static or self.op.use_locking): 221 self.LogWarning("Non-static data requested, locks need to be acquired") 222 self.op.use_locking = True 223 224 if self.op.instances or not self.op.use_locking: 225 # Expand instance names right here 226 self.wanted_names = GetWantedInstances(self, self.op.instances) 227 else: 228 # Will use acquired locks 229 self.wanted_names = None 230 231 if self.op.use_locking: 232 self.share_locks = ShareAll() 233 234 if self.wanted_names is None: 235 self.needed_locks[locking.LEVEL_INSTANCE] = locking.ALL_SET 236 else: 237 self.needed_locks[locking.LEVEL_INSTANCE] = self.wanted_names 238 239 self.needed_locks[locking.LEVEL_NODEGROUP] = [] 240 self.needed_locks[locking.LEVEL_NODE] = [] 241 self.needed_locks[locking.LEVEL_NETWORK] = [] 242 self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
243
244 - def DeclareLocks(self, level):
245 if self.op.use_locking: 246 owned_instances = self.owned_locks(locking.LEVEL_INSTANCE) 247 if level == locking.LEVEL_NODEGROUP: 248 249 # Lock all groups used by instances optimistically; this requires going 250 # via the node before it's locked, requiring verification later on 251 self.needed_locks[locking.LEVEL_NODEGROUP] = \ 252 frozenset(group_uuid 253 for instance_name in owned_instances 254 for group_uuid in 255 self.cfg.GetInstanceNodeGroups(instance_name)) 256 257 elif level == locking.LEVEL_NODE: 258 self._LockInstancesNodes() 259 260 elif level == locking.LEVEL_NETWORK: 261 self.needed_locks[locking.LEVEL_NETWORK] = \ 262 frozenset(net_uuid 263 for instance_name in owned_instances 264 for net_uuid in 265 self.cfg.GetInstanceNetworks(instance_name))
266
267 - def CheckPrereq(self):
268 """Check prerequisites. 269 270 This only checks the optional instance list against the existing names. 271 272 """ 273 owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE)) 274 owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP)) 275 owned_nodes = frozenset(self.owned_locks(locking.LEVEL_NODE)) 276 owned_networks = frozenset(self.owned_locks(locking.LEVEL_NETWORK)) 277 278 if self.wanted_names is None: 279 assert self.op.use_locking, "Locking was not used" 280 self.wanted_names = owned_instances 281 282 instances = dict(self.cfg.GetMultiInstanceInfo(self.wanted_names)) 283 284 if self.op.use_locking: 285 CheckInstancesNodeGroups(self.cfg, instances, owned_groups, owned_nodes, 286 None) 287 else: 288 assert not (owned_instances or owned_groups or 289 owned_nodes or owned_networks) 290 291 self.wanted_instances = instances.values()
292
293 - def _ComputeBlockdevStatus(self, node, instance, dev):
294 """Returns the status of a block device 295 296 """ 297 if self.op.static or not node: 298 return None 299 300 self.cfg.SetDiskID(dev, node) 301 302 result = self.rpc.call_blockdev_find(node, dev) 303 if result.offline: 304 return None 305 306 result.Raise("Can't compute disk status for %s" % instance.name) 307 308 status = result.payload 309 if status is None: 310 return None 311 312 return (status.dev_path, status.major, status.minor, 313 status.sync_percent, status.estimated_time, 314 status.is_degraded, status.ldisk_status)
315
316 - def _ComputeDiskStatus(self, instance, snode, dev):
317 """Compute block device status. 318 319 """ 320 (anno_dev,) = AnnotateDiskParams(instance, [dev], self.cfg) 321 322 return self._ComputeDiskStatusInner(instance, snode, anno_dev)
323
324 - def _ComputeDiskStatusInner(self, instance, snode, dev):
325 """Compute block device status. 326 327 @attention: The device has to be annotated already. 328 329 """ 330 if dev.dev_type in constants.LDS_DRBD: 331 # we change the snode then (otherwise we use the one passed in) 332 if dev.logical_id[0] == instance.primary_node: 333 snode = dev.logical_id[1] 334 else: 335 snode = dev.logical_id[0] 336 337 dev_pstatus = self._ComputeBlockdevStatus(instance.primary_node, 338 instance, dev) 339 dev_sstatus = self._ComputeBlockdevStatus(snode, instance, dev) 340 341 if dev.children: 342 dev_children = map(compat.partial(self._ComputeDiskStatusInner, 343 instance, snode), 344 dev.children) 345 else: 346 dev_children = [] 347 348 return { 349 "iv_name": dev.iv_name, 350 "dev_type": dev.dev_type, 351 "logical_id": dev.logical_id, 352 "physical_id": dev.physical_id, 353 "pstatus": dev_pstatus, 354 "sstatus": dev_sstatus, 355 "children": dev_children, 356 "mode": dev.mode, 357 "size": dev.size, 358 "name": dev.name, 359 "uuid": dev.uuid, 360 }
361
362 - def Exec(self, feedback_fn):
363 """Gather and return data""" 364 result = {} 365 366 cluster = self.cfg.GetClusterInfo() 367 368 node_names = itertools.chain(*(i.all_nodes for i in self.wanted_instances)) 369 nodes = dict(self.cfg.GetMultiNodeInfo(node_names)) 370 371 groups = dict(self.cfg.GetMultiNodeGroupInfo(node.group 372 for node in nodes.values())) 373 374 group2name_fn = lambda uuid: groups[uuid].name 375 for instance in self.wanted_instances: 376 pnode = nodes[instance.primary_node] 377 378 if self.op.static or pnode.offline: 379 remote_state = None 380 if pnode.offline: 381 self.LogWarning("Primary node %s is marked offline, returning static" 382 " information only for instance %s" % 383 (pnode.name, instance.name)) 384 else: 385 remote_info = self.rpc.call_instance_info(instance.primary_node, 386 instance.name, 387 instance.hypervisor) 388 remote_info.Raise("Error checking node %s" % instance.primary_node) 389 remote_info = remote_info.payload 390 if remote_info and "state" in remote_info: 391 remote_state = "up" 392 else: 393 if instance.admin_state == constants.ADMINST_UP: 394 remote_state = "down" 395 else: 396 remote_state = instance.admin_state 397 398 disks = map(compat.partial(self._ComputeDiskStatus, instance, None), 399 instance.disks) 400 401 snodes_group_uuids = [nodes[snode_name].group 402 for snode_name in instance.secondary_nodes] 403 404 result[instance.name] = { 405 "name": instance.name, 406 "config_state": instance.admin_state, 407 "run_state": remote_state, 408 "pnode": instance.primary_node, 409 "pnode_group_uuid": pnode.group, 410 "pnode_group_name": group2name_fn(pnode.group), 411 "snodes": instance.secondary_nodes, 412 "snodes_group_uuids": snodes_group_uuids, 413 "snodes_group_names": map(group2name_fn, snodes_group_uuids), 414 "os": instance.os, 415 # this happens to be the same format used for hooks 416 "nics": NICListToTuple(self, instance.nics), 417 "disk_template": instance.disk_template, 418 "disks": disks, 419 "hypervisor": instance.hypervisor, 420 "network_port": instance.network_port, 421 "hv_instance": instance.hvparams, 422 "hv_actual": cluster.FillHV(instance, skip_globals=True), 423 "be_instance": instance.beparams, 424 "be_actual": cluster.FillBE(instance), 425 "os_instance": instance.osparams, 426 "os_actual": cluster.SimpleFillOS(instance.os, instance.osparams), 427 "serial_no": instance.serial_no, 428 "mtime": instance.mtime, 429 "ctime": instance.ctime, 430 "uuid": instance.uuid, 431 } 432 433 return result
434