Commit 3234695b authored by Michael Hanselmann's avatar Michael Hanselmann
Browse files

Merge branch 'devel-2.7'

* devel-2.7:
  NEWS: Fix the fix in commit 82b6f9ac

  NEWS: Fix release date for 2.7.0 beta1
  Minor fixes regarding change
  Fix issue 378
  NEWS: Mention multi-alloc and fix typo
  Add cfgupgrade for changing to uuid
  Remove useless code in backend for network hooks
  Show network name and not uuid in instance info
  Add IQ_NETWORKS in query tests
  Implement network locking in Instance queries
  Changes in query to support as uuid
  Modify query LUs to supoprt as uuid
  Add GetInstanceNetworks() config method
  cmdlib changes to support as uuid
  Make network config methods take uuid as argument
  Document possible hypervisor bug in serial_console
  Update man pages wrt ExtStorage
Signed-off-by: default avatarMichael Hanselmann <>
Reviewed-by: default avatarIustin Pop <>
parents daff2f81 b5a21c81
......@@ -5,7 +5,7 @@ News
Version 2.7.0 beta1
*(Released Wed, 6 Mar 2013)*
*(Released Wed, 6 Feb 2013)*
- ``gnt-instance batch-create`` has been changed to use the bulk create
opcode from Ganeti. This lead to incompatible changes in the format of
......@@ -48,10 +48,12 @@ Version 2.7.0 beta1
<rapi>` interface and when an instance allocator is used. If the
``opportunistic_locking`` parameter is set the opcode will try to
acquire as many locks as possible, but will not wait for any locks
held by other opcodes. If the not enough resources can be found to
held by other opcodes. If not enough resources can be found to
allocate the instance, the temporary error code
:pyeval:`errors.ECODE_TEMP_NORES` is returned. The operation can be
retried thereafter, with or without opportunistic locking.
- The functionality for allocating multiple instances at once has been
overhauled and is now also available through :doc:`RAPI <rapi>`.
- Man pages can now be included when the documentation is built, in
which case the output is in ``doc/man-html``. The configure-time
option is ``--enable-manpages-in-doc``. Sphinx 1.0 or higher is
......@@ -11,6 +11,8 @@ The following design documents have been implemented in Ganeti 2.7:
- :doc:`design-virtual-clusters`
- :doc:`design-network`
- :doc:`design-linuxha`
- :doc:`design-shared-storage` (Updated to reflect the new ExtStorage
The following designs have been partially implemented in Ganeti 2.7:
Ganeti shared storage support for 2.3+
Ganeti shared storage support
This document describes the changes in Ganeti 2.3+ compared to Ganeti
2.3 storage model.
2.3 storage model. It also documents the ExtStorage Interface.
.. contents:: :depth: 4
.. highlight:: shell-example
......@@ -721,10 +721,19 @@ modify``.
Your instance types, networking environment, hypervisor type and version
may all affect what kind of parameters should be used on your cluster.
For example kvm instances are by default configured to use a host
kernel, and to be reached via serial console, which works nice for Linux
paravirtualized instances. If you want fully virtualized instances you
may want to handle their kernel inside the instance, and to use VNC.
.. admonition:: KVM
Instances are by default configured to use a host kernel, and to be
reached via serial console, which works nice for Linux paravirtualized
instances. If you want fully virtualized instances you may want to
handle their kernel inside the instance, and to use VNC.
Some versions of KVM have a bug that will make an instance hang when
configured to use the serial console (which is the default) unless a
connection is made to it within about 2 seconds of the instance's
startup. For such case it's recommended to disable the
``serial_console`` option.
Joining the nodes to the cluster
......@@ -2559,11 +2559,6 @@ def OSEnvironment(instance, inst_os, debug=0):
if nic.netinfo:
nobj = objects.Network.FromDict(nic.netinfo)
result.update(nobj.HooksDict("NIC_%d_" % idx))
# FIXME: broken network reference: the instance NIC specifies a network,
# but the relevant network entry was not in the config. This should be
# made impossible.
result["INSTANCE_NIC%d_NETWORK" % idx] =
if constants.HV_NIC_TYPE in instance.hvparams:
result["NIC_%d_FRONTEND_TYPE" % idx] = \
......@@ -1179,10 +1179,13 @@ def ShowInstanceConfig(opts, args):
FormatParameterDict(buf, instance["be_instance"], be_actual, level=2)
# TODO(ganeti 2.7) rework the NICs as well
buf.write(" - NICs:\n")
for idx, (ip, mac, mode, link, network, _) in enumerate(instance["nics"]):
for idx, (ip, mac, mode, link, _, netinfo) in enumerate(instance["nics"]):
network_name = None
if netinfo:
network_name = netinfo["name"]
buf.write(" - nic/%d: MAC: %s, IP: %s,"
" mode: %s, link: %s, network: %s\n" %
(idx, mac, ip, mode, link, network))
(idx, mac, ip, mode, link, network_name))
buf.write(" Disk template: %s\n" % instance["disk_template"])
buf.write(" Disks:\n")
......@@ -252,7 +252,7 @@ def ShowNetworkConfig(_, args):
l = lambda value: ", ".join(str(idx) + ":" + str(ip)
for idx, (ip, net) in enumerate(value)
if net == name)
if net == uuid)
ToStdout(" %s : %s", inst, l(zip(ips, networks)))
......@@ -1557,9 +1557,8 @@ def _NICToTuple(lu, nic):
link = filled_params[constants.NIC_LINK]
netinfo = None
net_uuid = lu.cfg.LookupNetwork(
netinfo = objects.Network.ToDict(lu.cfg.GetNetwork(net_uuid))
nobj = lu.cfg.GetNetwork(
netinfo = objects.Network.ToDict(nobj)
return (nic.ip, nic.mac, mode, link,, netinfo)
......@@ -5752,6 +5751,7 @@ class _InstanceQuery(_QueryBase):
lu.needed_locks[locking.LEVEL_INSTANCE] = self.wanted
lu.needed_locks[locking.LEVEL_NODEGROUP] = []
lu.needed_locks[locking.LEVEL_NODE] = []
lu.needed_locks[locking.LEVEL_NETWORK] = []
lu.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
self.do_grouplocks = (self.do_locking and
......@@ -5771,6 +5771,12 @@ class _InstanceQuery(_QueryBase):
elif level == locking.LEVEL_NODE:
lu._LockInstancesNodes() # pylint: disable=W0212
elif level == locking.LEVEL_NETWORK:
lu.needed_locks[locking.LEVEL_NETWORK] = \
for instance_name in lu.owned_locks(locking.LEVEL_INSTANCE)
for net_uuid in lu.cfg.GetInstanceNetworks(instance_name))
def _CheckGroupLocks(lu):
owned_instances = frozenset(lu.owned_locks(locking.LEVEL_INSTANCE))
......@@ -5861,10 +5867,17 @@ class _InstanceQuery(_QueryBase):
nodes = None
groups = None
if query.IQ_NETWORKS in self.requested_data:
net_uuids = itertools.chain(*(lu.cfg.GetInstanceNetworks(
for i in instance_list))
networks = dict((uuid, lu.cfg.GetNetwork(uuid)) for uuid in net_uuids)
networks = None
return query.InstanceQueryData(instance_list, lu.cfg.GetClusterInfo(),
disk_usage, offline_nodes, bad_nodes,
live_data, wrongnode_inst, consinfo,
nodes, groups)
nodes, groups, networks)
class LUQuery(NoHooksLU):
......@@ -9952,8 +9965,9 @@ def _ComputeNics(op, cluster, default_ip, cfg, ec_id):
check_params = cluster.SimpleFillNIC(nicparams)
net_uuid = cfg.LookupNetwork(net)
nics.append(objects.NIC(mac=mac, ip=nic_ip,
network=net, nicparams=nicparams))
network=net_uuid, nicparams=nicparams))
return nics
......@@ -10689,14 +10703,15 @@ class LUInstanceCreate(LogicalUnit):
# Fill in any IPs from IP pools. This must happen here, because we need to
# know the nic's primary node, as specified by the iallocator
for idx, nic in enumerate(self.nics):
net =
if net is not None:
netparams = self.cfg.GetGroupNetParams(net,
net_uuid =
if net_uuid is not None:
nobj = self.cfg.GetNetwork(net_uuid)
netparams = self.cfg.GetGroupNetParams(net_uuid,
if netparams is None:
raise errors.OpPrereqError("No netparams found for network"
" %s. Propably not connected to"
" node's %s nodegroup" %
self.LogInfo("NIC/%d inherits netparams %s" %
(idx, netparams.values()))
......@@ -10704,19 +10719,19 @@ class LUInstanceCreate(LogicalUnit):
if nic.ip is not None:
if nic.ip.lower() == constants.NIC_IP_POOL:
nic.ip = self.cfg.GenerateIp(net, self.proc.GetECId())
nic.ip = self.cfg.GenerateIp(net_uuid, self.proc.GetECId())
except errors.ReservationError:
raise errors.OpPrereqError("Unable to get a free IP for NIC %d"
" from the address pool" % idx,
self.LogInfo("Chose IP %s from network %s", nic.ip, net)
self.LogInfo("Chose IP %s from network %s", nic.ip,
self.cfg.ReserveIp(net, nic.ip, self.proc.GetECId())
self.cfg.ReserveIp(net_uuid, nic.ip, self.proc.GetECId())
except errors.ReservationError:
raise errors.OpPrereqError("IP address %s already in use"
" or does not belong to network %s" %
(nic.ip, net),
# net is None, ip None or given
......@@ -12817,12 +12832,13 @@ class LUInstanceQueryData(NoHooksLU):
self.needed_locks[locking.LEVEL_NODEGROUP] = []
self.needed_locks[locking.LEVEL_NODE] = []
self.needed_locks[locking.LEVEL_NETWORK] = []
self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
def DeclareLocks(self, level):
if self.op.use_locking:
owned_instances = self.owned_locks(locking.LEVEL_INSTANCE)
if level == locking.LEVEL_NODEGROUP:
owned_instances = self.owned_locks(locking.LEVEL_INSTANCE)
# Lock all groups used by instances optimistically; this requires going
# via the node before it's locked, requiring verification later on
......@@ -12835,6 +12851,13 @@ class LUInstanceQueryData(NoHooksLU):
elif level == locking.LEVEL_NODE:
elif level == locking.LEVEL_NETWORK:
self.needed_locks[locking.LEVEL_NETWORK] = \
for instance_name in owned_instances
for net_uuid in
def CheckPrereq(self):
"""Check prerequisites.
......@@ -12844,6 +12867,7 @@ class LUInstanceQueryData(NoHooksLU):
owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
owned_nodes = frozenset(self.owned_locks(locking.LEVEL_NODE))
owned_networks = frozenset(self.owned_locks(locking.LEVEL_NETWORK))
if self.wanted_names is None:
assert self.op.use_locking, "Locking was not used"
......@@ -12855,7 +12879,8 @@ class LUInstanceQueryData(NoHooksLU):
_CheckInstancesNodeGroups(self.cfg, instances, owned_groups, owned_nodes,
assert not (owned_instances or owned_groups or owned_nodes)
assert not (owned_instances or owned_groups or
owned_nodes or owned_networks)
self.wanted_instances = instances.values()
......@@ -12939,7 +12964,6 @@ class LUInstanceQueryData(NoHooksLU):
for node in nodes.values()))
group2name_fn = lambda uuid: groups[uuid].name
for instance in self.wanted_instances:
pnode = nodes[instance.primary_node]
......@@ -13383,7 +13407,7 @@ class LUInstanceSetParams(LogicalUnit):
nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
return (nl, nl)
def _PrepareNicModification(self, params, private, old_ip, old_net,
def _PrepareNicModification(self, params, private, old_ip, old_net_uuid,
old_params, cluster, pnode):
update_params_dict = dict([(key, params[key])
......@@ -13393,13 +13417,21 @@ class LUInstanceSetParams(LogicalUnit):
req_link = update_params_dict.get(constants.NIC_LINK, None)
req_mode = update_params_dict.get(constants.NIC_MODE, None)
new_net = params.get(constants.INIC_NETWORK, old_net)
if new_net is not None:
netparams = self.cfg.GetGroupNetParams(new_net, pnode)
if netparams is None:
new_net_uuid = None
new_net_uuid_or_name = params.get(constants.INIC_NETWORK, old_net_uuid)
if new_net_uuid_or_name:
new_net_uuid = self.cfg.LookupNetwork(new_net_uuid_or_name)
new_net_obj = self.cfg.GetNetwork(new_net_uuid)
if old_net_uuid:
old_net_obj = self.cfg.GetNetwork(old_net_uuid)
if new_net_uuid:
netparams = self.cfg.GetGroupNetParams(new_net_uuid, pnode)
if not netparams:
raise errors.OpPrereqError("No netparams found for the network"
" %s, probably not connected" % new_net,
" %s, probably not connected" %, errors.ECODE_INVAL)
new_params = dict(netparams)
new_params = _GetUpdatedParams(old_params, update_params_dict)
......@@ -13438,7 +13470,7 @@ class LUInstanceSetParams(LogicalUnit):
elif mac in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
# otherwise generate the MAC address
params[constants.INIC_MAC] = \
self.cfg.GenerateMAC(new_net, self.proc.GetECId())
self.cfg.GenerateMAC(new_net_uuid, self.proc.GetECId())
# or validate/reserve the current one
......@@ -13447,61 +13479,61 @@ class LUInstanceSetParams(LogicalUnit):
raise errors.OpPrereqError("MAC address '%s' already in use"
" in cluster" % mac,
elif new_net != old_net:
elif new_net_uuid != old_net_uuid:
def get_net_prefix(net):
def get_net_prefix(net_uuid):
mac_prefix = None
if net:
uuid = self.cfg.LookupNetwork(net)
mac_prefix = self.cfg.GetNetwork(uuid).mac_prefix
if net_uuid:
nobj = self.cfg.GetNetwork(net_uuid)
mac_prefix = nobj.mac_prefix
return mac_prefix
new_prefix = get_net_prefix(new_net)
old_prefix = get_net_prefix(old_net)
new_prefix = get_net_prefix(new_net_uuid)
old_prefix = get_net_prefix(old_net_uuid)
if old_prefix != new_prefix:
params[constants.INIC_MAC] = \
self.cfg.GenerateMAC(new_net, self.proc.GetECId())
self.cfg.GenerateMAC(new_net_uuid, self.proc.GetECId())
#if there is a change in nic-network configuration
#if there is a change in nic's ip/network configuration
new_ip = params.get(constants.INIC_IP, old_ip)
if (new_ip, new_net) != (old_ip, old_net):
if (new_ip, new_net_uuid) != (old_ip, old_net_uuid):
if new_ip:
if new_net:
if new_ip.lower() == constants.NIC_IP_POOL:
new_ip = self.cfg.GenerateIp(new_net, self.proc.GetECId())
except errors.ReservationError:
raise errors.OpPrereqError("Unable to get a free IP"
" from the address pool",
self.LogInfo("Chose IP %s from pool %s", new_ip, new_net)
params[constants.INIC_IP] = new_ip
elif new_ip != old_ip or new_net != old_net:
self.LogInfo("Reserving IP %s in pool %s", new_ip, new_net)
self.cfg.ReserveIp(new_net, new_ip, self.proc.GetECId())
except errors.ReservationError:
raise errors.OpPrereqError("IP %s not available in network %s" %
(new_ip, new_net),
elif new_ip.lower() == constants.NIC_IP_POOL:
raise errors.OpPrereqError("ip=pool, but no network found",
if new_ip.lower() == constants.NIC_IP_POOL:
if not new_net_uuid:
raise errors.OpPrereqError("ip=pool, but no network found",
new_ip = self.cfg.GenerateIp(new_net_uuid, self.proc.GetECId())
except errors.ReservationError:
raise errors.OpPrereqError("Unable to get a free IP"
" from the address pool",
self.LogInfo("Chose IP %s from network %s", new_ip,
params[constants.INIC_IP] = new_ip
elif new_ip != old_ip or new_net_uuid != old_net_uuid:
self.cfg.ReserveIp(new_net_uuid, new_ip, self.proc.GetECId())
self.LogInfo("Reserving IP %s in network %s",
except errors.ReservationError:
raise errors.OpPrereqError("IP %s not available in network %s" %
# new net is None
elif self.op.conflicts_check:
elif not new_net_uuid and self.op.conflicts_check:
_CheckForConflictingIp(self, new_ip, pnode)
if old_ip and old_net:
if old_ip:
self.cfg.ReleaseIp(old_net, old_ip, self.proc.GetECId())
except errors.AddressPoolError, err:
logging.warning("Releasing IP address '%s' from network '%s'"
" failed: %s", old_ip, old_net, err)
self.cfg.ReleaseIp(old_net_uuid, old_ip, self.proc.GetECId())
except errors.AddressPoolError:
logging.warning("Release IP %s not contained in network %s",
# there are no changes in (net, ip) tuple
elif (old_net is not None and
elif (old_net_uuid is not None and
(req_link is not None or req_mode is not None)):
raise errors.OpPrereqError("Not allowed to change link or mode of"
" a NIC that is connected to a network",
......@@ -16536,8 +16568,6 @@ class _NetworkQuery(_QueryBase):
network_uuids = self._GetNames(lu, all_networks.keys(),
name_to_uuid = dict((, n.uuid) for n in all_networks.values())
do_instances = query.NETQ_INST in self.requested_data
do_groups = query.NETQ_GROUP in self.requested_data
......@@ -16562,10 +16592,8 @@ class _NetworkQuery(_QueryBase):
network_to_instances = dict((uuid, []) for uuid in network_uuids)
for instance in all_instances.values():
for nic in instance.nics:
net_uuid = name_to_uuid[]
if net_uuid in network_uuids:
if in network_uuids:
if query.NETQ_STATS in self.requested_data:
......@@ -16797,7 +16825,7 @@ class LUNetworkDisconnect(LogicalUnit):
self.connected = False
_NetworkConflictCheck(self, lambda nic: == self.network_name,
_NetworkConflictCheck(self, lambda nic: == self.network_uuid,
"disconnect from")
def Exec(self, feedback_fn):
......@@ -270,13 +270,12 @@ class ConfigWriter:
return self._config_data.cluster.SimpleFillDP(group.diskparams)
def _UnlockedGetNetworkMACPrefix(self, net):
def _UnlockedGetNetworkMACPrefix(self, net_uuid):
"""Return the network mac prefix if it exists or the cluster level default.
prefix = None
if net:
net_uuid = self._UnlockedLookupNetwork(net)
if net_uuid:
nobj = self._UnlockedGetNetwork(net_uuid)
if nobj.mac_prefix:
prefix = nobj.mac_prefix
......@@ -302,14 +301,14 @@ class ConfigWriter:
return GenMac
@locking.ssynchronized(_config_lock, shared=1)
def GenerateMAC(self, net, ec_id):
def GenerateMAC(self, net_uuid, ec_id):
"""Generate a MAC for an instance.
This should check the current instances for duplicates.
existing = self._AllMACs()
prefix = self._UnlockedGetNetworkMACPrefix(net)
prefix = self._UnlockedGetNetworkMACPrefix(net_uuid)
gen_mac = self._GenerateOneMAC(prefix)
return self._temporary_ids.Generate(existing, gen_mac, ec_id)
......@@ -358,21 +357,20 @@ class ConfigWriter:
(constants.RELEASE_ACTION, address, net_uuid))
@locking.ssynchronized(_config_lock, shared=1)
def ReleaseIp(self, net, address, ec_id):
def ReleaseIp(self, net_uuid, address, ec_id):
"""Give a specified IP address back to an IP pool.
This is just a wrapper around _UnlockedReleaseIp.
net_uuid = self._UnlockedLookupNetwork(net)
self._UnlockedReleaseIp(net_uuid, address, ec_id)
if net_uuid:
self._UnlockedReleaseIp(net_uuid, address, ec_id)
@locking.ssynchronized(_config_lock, shared=1)
def GenerateIp(self, net, ec_id):
def GenerateIp(self, net_uuid, ec_id):
"""Find a free IPv4 address for an instance.
net_uuid = self._UnlockedLookupNetwork(net)
nobj = self._UnlockedGetNetwork(net_uuid)
pool = network.AddressPool(nobj)
......@@ -404,12 +402,12 @@ class ConfigWriter:
address, net_uuid))
@locking.ssynchronized(_config_lock, shared=1)
def ReserveIp(self, net, address, ec_id):
def ReserveIp(self, net_uuid, address, ec_id):
"""Reserve a given IPv4 address for use by an instance.
net_uuid = self._UnlockedLookupNetwork(net)
return self._UnlockedReserveIp(net_uuid, address, ec_id)
if net_uuid:
return self._UnlockedReserveIp(net_uuid, address, ec_id)
@locking.ssynchronized(_config_lock, shared=1)
def ReserveLV(self, lv_name, ec_id):
......@@ -1452,10 +1450,9 @@ class ConfigWriter:
instance = self._UnlockedGetInstanceInfo(instance_name)
for nic in instance.nics:
if is not None and nic.ip is not None:
net_uuid = self._UnlockedLookupNetwork(
if and nic.ip:
# Return all IP addresses to the respective address pools
self._UnlockedCommitIp(constants.RELEASE_ACTION, net_uuid, nic.ip)
self._UnlockedCommitIp(constants.RELEASE_ACTION,, nic.ip)
del self._config_data.instances[instance_name]
self._config_data.cluster.serial_no += 1
......@@ -1573,6 +1570,24 @@ class ConfigWriter:
return frozenset(self._UnlockedGetNodeInfo(node_name).group
for node_name in nodes)
@locking.ssynchronized(_config_lock, shared=1)
def GetInstanceNetworks(self, instance_name):
"""Returns set of network UUIDs for instance's nics.
@rtype: frozenset
instance = self._UnlockedGetInstanceInfo(instance_name)
if not instance:
raise errors.ConfigurationError("Unknown instance '%s'" % instance_name)
networks = set()
for nic in instance.nics:
return frozenset(networks)
@locking.ssynchronized(_config_lock, shared=1)
def GetMultiInstanceInfo(self, instances):
"""Get the configuration of multiple instances.
......@@ -2487,6 +2502,8 @@ class ConfigWriter:
@raises errors.OpPrereqError: when the target network cannot be found
if target is None:
return None
if target in self._config_data.networks:
return target
for net in self._config_data.networks.values():
......@@ -2526,20 +2543,19 @@ class ConfigWriter:
self._config_data.cluster.serial_no += 1
def _UnlockedGetGroupNetParams(self, net, node):
def _UnlockedGetGroupNetParams(self, net_uuid, node):
"""Get the netparams (mode, link) of a network.
Get a network's netparams for a given node.
@type net: string
@param net: network name
@type net_uuid: string
@param net_uuid: network uuid
@type node: string
@param node: node name
@rtype: dict or None
@return: netparams
net_uuid = self._UnlockedLookupNetwork(net)
node_info = self._UnlockedGetNodeInfo(node)
nodegroup_info = self._UnlockedGetNodeGroup(
netparams = nodegroup_info.networks.get(net_uuid, None)
......@@ -2547,11 +2563,11 @@ class ConfigWriter:
return netparams
@locking.ssynchronized(_config_lock, shared=1)
def GetGroupNetParams(self, net, node):
def GetGroupNetParams(self, net_uuid, node):
"""Locking wrapper of _UnlockedGetGroupNetParams()
return self._UnlockedGetGroupNetParams(net, node)
return self._UnlockedGetGroupNetParams(net_uuid, node)
@locking.ssynchronized(_config_lock, shared=1)
def CheckIPInNodeGroup(self, ip, node):
......@@ -90,7 +90,8 @@ from ganeti.constants import (QFT_UNKNOWN, QFT_TEXT, QFT_BOOL, QFT_NUMBER,
IQ_NODES) = range(100, 105)
IQ_NETWORKS) = range(100, 106)
......@@ -1383,7 +1384,7 @@ class InstanceQueryData:
def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes,
live_data, wrongnode_inst, console, nodes, groups):
live_data, wrongnode_inst, console, nodes, groups, networks):
"""Initializes this class.
@param instances: List of instance objects
......@@ -1402,6 +1403,8 @@ class InstanceQueryData:
@param console: Per-instance console information
@type nodes: dict; node name as key
@param nodes: Node objects
@type networks: dict; net_uuid as key
@param networks: Network objects
assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
......@@ -1419,6 +1422,7 @@ class InstanceQueryData:
self.console = console
self.nodes = nodes
self.groups = groups
self.networks = networks
# Used for individual rows
self.inst_hvparams = None
......@@ -1569,6 +1573,20 @@ def _GetInstNic(index, cb):
return fn
def _GetInstNicNetworkName(ctx, _, nic): # pylint: disable=W0613
"""Get a NIC's Network.
@type ctx: L{InstanceQueryData}
@type nic: L{objects.NIC}
@param nic: NIC object