NQ_LIVE,
NQ_GROUP) = range(1, 5)
+(IQ_CONFIG,
+ IQ_LIVE,
+ IQ_DISKUSAGE) = range(100, 103)
+
FIELD_NAME_RE = re.compile(r"^[a-z0-9/._]+$")
TITLE_RE = re.compile(r"^[^\s]+$")
return _PrepareFieldList(fields)
+class InstanceQueryData:
+ """Data container for instance data queries.
+
+ """
+ def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes,
+ live_data):
+ """Initializes this class.
+
+ @param instances: List of instance objects
+ @param cluster: Cluster object
+ @type disk_usage: dict; instance name as key
+ @param disk_usage: Per-instance disk usage
+ @type offline_nodes: list of strings
+ @param offline_nodes: List of offline nodes
+ @type bad_nodes: list of strings
+ @param bad_nodes: List of faulty nodes
+ @type live_data: dict; instance name as key
+ @param live_data: Per-instance live data
+
+ """
+ assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
+ "Offline nodes not included in bad nodes"
+ assert not (set(live_data.keys()) & set(bad_nodes)), \
+ "Found live data for bad or offline nodes"
+
+ self.instances = instances
+ self.cluster = cluster
+ self.disk_usage = disk_usage
+ self.offline_nodes = offline_nodes
+ self.bad_nodes = bad_nodes
+ self.live_data = live_data
+
+ # Used for individual rows
+ self.inst_hvparams = None
+ self.inst_beparams = None
+ self.inst_nicparams = None
+
+ def __iter__(self):
+ """Iterate over all instances.
+
+ This function has side-effects and only one instance of the resulting
+ generator should be used at a time.
+
+ """
+ for inst in self.instances:
+ self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True)
+ self.inst_beparams = self.cluster.FillBE(inst)
+ self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams)
+ for nic in inst.nics]
+
+ yield inst
+
+
+def _GetInstOperState(ctx, inst):
+ """Get instance's operational status.
+
+ @type ctx: L{InstanceQueryData}
+ @type inst: L{objects.Instance}
+ @param inst: Instance object
+
+ """
+ if inst.primary_node in ctx.bad_nodes:
+ return (constants.QRFS_NODATA, None)
+ else:
+ return (constants.QRFS_NORMAL, bool(ctx.live_data.get(inst.name)))
+
+
+def _GetInstLiveData(name):
+ """Build function for retrieving live data.
+
+ @type name: string
+ @param name: Live data field name
+
+ """
+ def fn(ctx, inst):
+ """Get live data for an instance.
+
+ @type ctx: L{InstanceQueryData}
+ @type inst: L{objects.Instance}
+ @param inst: Instance object
+
+ """
+ if (inst.primary_node in ctx.bad_nodes or
+ inst.primary_node in ctx.offline_nodes):
+ return (constants.QRFS_NODATA, None)
+
+ if inst.name in ctx.live_data:
+ data = ctx.live_data[inst.name]
+ if name in data:
+ return (constants.QRFS_NORMAL, data[name])
+
+ return (constants.QRFS_UNAVAIL, None)
+
+ return fn
+
+
+def _GetInstStatus(ctx, inst):
+ """Get instance status.
+
+ @type ctx: L{InstanceQueryData}
+ @type inst: L{objects.Instance}
+ @param inst: Instance object
+
+ """
+ if inst.primary_node in ctx.offline_nodes:
+ return (constants.QRFS_NORMAL, "ERROR_nodeoffline")
+
+ if inst.primary_node in ctx.bad_nodes:
+ return (constants.QRFS_NORMAL, "ERROR_nodedown")
+
+ if bool(ctx.live_data.get(inst.name)):
+ if inst.admin_up:
+ return (constants.QRFS_NORMAL, "running")
+ else:
+ return (constants.QRFS_NORMAL, "ERROR_up")
+
+ if inst.admin_up:
+ return (constants.QRFS_NORMAL, "ERROR_down")
+
+ return (constants.QRFS_NORMAL, "ADMIN_down")
+
+
+def _GetInstDiskSize(index):
+ """Build function for retrieving disk size.
+
+ @type index: int
+ @param index: Disk index
+
+ """
+ def fn(_, inst):
+ """Get size of a disk.
+
+ @type inst: L{objects.Instance}
+ @param inst: Instance object
+
+ """
+ try:
+ return (constants.QRFS_NORMAL, inst.disks[index].size)
+ except IndexError:
+ return (constants.QRFS_UNAVAIL, None)
+
+ return fn
+
+
+def _GetInstNic(index, cb):
+ """Build function for calling another function with an instance NIC.
+
+ @type index: int
+ @param index: NIC index
+ @type cb: callable
+ @param cb: Callback
+
+ """
+ def fn(ctx, inst):
+ """Call helper function with instance NIC.
+
+ @type ctx: L{InstanceQueryData}
+ @type inst: L{objects.Instance}
+ @param inst: Instance object
+
+ """
+ try:
+ nic = inst.nics[index]
+ except IndexError:
+ return (constants.QRFS_UNAVAIL, None)
+
+ return cb(ctx, index, nic)
+
+ return fn
+
+
+def _GetInstNicIp(ctx, _, nic): # pylint: disable-msg=W0613
+ """Get a NIC's IP address.
+
+ @type ctx: L{InstanceQueryData}
+ @type nic: L{objects.NIC}
+ @param nic: NIC object
+
+ """
+ if nic.ip is None:
+ return (constants.QRFS_UNAVAIL, None)
+ else:
+ return (constants.QRFS_NORMAL, nic.ip)
+
+
+def _GetInstNicBridge(ctx, index, _):
+ """Get a NIC's bridge.
+
+ @type ctx: L{InstanceQueryData}
+ @type index: int
+ @param index: NIC index
+
+ """
+ assert len(ctx.inst_nicparams) >= index
+
+ nicparams = ctx.inst_nicparams[index]
+
+ if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
+ return (constants.QRFS_NORMAL, nicparams[constants.NIC_LINK])
+ else:
+ return (constants.QRFS_UNAVAIL, None)
+
+
+def _GetInstAllNicBridges(ctx, inst):
+ """Get all network bridges for an instance.
+
+ @type ctx: L{InstanceQueryData}
+ @type inst: L{objects.Instance}
+ @param inst: Instance object
+
+ """
+ assert len(ctx.inst_nicparams) == len(inst.nics)
+
+ result = []
+
+ for nicp in ctx.inst_nicparams:
+ if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
+ result.append(nicp[constants.NIC_LINK])
+ else:
+ result.append(None)
+
+ assert len(result) == len(inst.nics)
+
+ return (constants.QRFS_NORMAL, result)
+
+
+def _GetInstNicParam(name):
+ """Build function for retrieving a NIC parameter.
+
+ @type name: string
+ @param name: Parameter name
+
+ """
+ def fn(ctx, index, _):
+ """Get a NIC's bridge.
+
+ @type ctx: L{InstanceQueryData}
+ @type inst: L{objects.Instance}
+ @param inst: Instance object
+ @type nic: L{objects.NIC}
+ @param nic: NIC object
+
+ """
+ assert len(ctx.inst_nicparams) >= index
+ return (constants.QRFS_NORMAL, ctx.inst_nicparams[index][name])
+
+ return fn
+
+
+def _GetInstanceNetworkFields():
+ """Get instance fields involving network interfaces.
+
+ @return: List of field definitions used as input for L{_PrepareFieldList}
+
+ """
+ nic_mac_fn = lambda ctx, _, nic: (constants.QRFS_NORMAL, nic.mac)
+ nic_mode_fn = _GetInstNicParam(constants.NIC_MODE)
+ nic_link_fn = _GetInstNicParam(constants.NIC_LINK)
+
+ fields = [
+ # First NIC (legacy)
+ (_MakeField("ip", "IP_address", constants.QFT_TEXT), IQ_CONFIG,
+ _GetInstNic(0, _GetInstNicIp)),
+ (_MakeField("mac", "MAC_address", constants.QFT_TEXT), IQ_CONFIG,
+ _GetInstNic(0, nic_mac_fn)),
+ (_MakeField("bridge", "Bridge", constants.QFT_TEXT), IQ_CONFIG,
+ _GetInstNic(0, _GetInstNicBridge)),
+ (_MakeField("nic_mode", "NIC_Mode", constants.QFT_TEXT), IQ_CONFIG,
+ _GetInstNic(0, nic_mode_fn)),
+ (_MakeField("nic_link", "NIC_Link", constants.QFT_TEXT), IQ_CONFIG,
+ _GetInstNic(0, nic_link_fn)),
+
+ # All NICs
+ (_MakeField("nic.count", "NICs", constants.QFT_NUMBER), IQ_CONFIG,
+ lambda ctx, inst: (constants.QRFS_NORMAL, len(inst.nics))),
+ (_MakeField("nic.macs", "NIC_MACs", constants.QFT_OTHER), IQ_CONFIG,
+ lambda ctx, inst: (constants.QRFS_NORMAL, [nic.mac for nic in inst.nics])),
+ (_MakeField("nic.ips", "NIC_IPs", constants.QFT_OTHER), IQ_CONFIG,
+ lambda ctx, inst: (constants.QRFS_NORMAL, [nic.ip for nic in inst.nics])),
+ (_MakeField("nic.modes", "NIC_modes", constants.QFT_OTHER), IQ_CONFIG,
+ lambda ctx, inst: (constants.QRFS_NORMAL,
+ [nicp[constants.NIC_MODE]
+ for nicp in ctx.inst_nicparams])),
+ (_MakeField("nic.links", "NIC_links", constants.QFT_OTHER), IQ_CONFIG,
+ lambda ctx, inst: (constants.QRFS_NORMAL,
+ [nicp[constants.NIC_LINK]
+ for nicp in ctx.inst_nicparams])),
+ (_MakeField("nic.bridges", "NIC_bridges", constants.QFT_OTHER), IQ_CONFIG,
+ _GetInstAllNicBridges),
+ ]
+
+ # NICs by number
+ for i in range(constants.MAX_NICS):
+ fields.extend([
+ (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, constants.QFT_TEXT),
+ IQ_CONFIG, _GetInstNic(i, _GetInstNicIp)),
+ (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, constants.QFT_TEXT),
+ IQ_CONFIG, _GetInstNic(i, nic_mac_fn)),
+ (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, constants.QFT_TEXT),
+ IQ_CONFIG, _GetInstNic(i, nic_mode_fn)),
+ (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, constants.QFT_TEXT),
+ IQ_CONFIG, _GetInstNic(i, nic_link_fn)),
+ (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, constants.QFT_TEXT),
+ IQ_CONFIG, _GetInstNic(i, _GetInstNicBridge)),
+ ])
+
+ return fields
+
+
+def _GetInstDiskUsage(ctx, inst):
+ """Get disk usage for an instance.
+
+ @type ctx: L{InstanceQueryData}
+ @type inst: L{objects.Instance}
+ @param inst: Instance object
+
+ """
+ usage = ctx.disk_usage[inst.name]
+
+ if usage is None:
+ usage = 0
+
+ return (constants.QRFS_NORMAL, usage)
+
+
+def _GetInstanceDiskFields():
+ """Get instance fields involving disks.
+
+ @return: List of field definitions used as input for L{_PrepareFieldList}
+
+ """
+ fields = [
+ (_MakeField("disk_usage", "DiskUsage", constants.QFT_UNIT), IQ_DISKUSAGE,
+ _GetInstDiskUsage),
+ (_MakeField("sda_size", "LegacyDisk/0", constants.QFT_UNIT), IQ_CONFIG,
+ _GetInstDiskSize(0)),
+ (_MakeField("sdb_size", "LegacyDisk/1", constants.QFT_UNIT), IQ_CONFIG,
+ _GetInstDiskSize(1)),
+ (_MakeField("disk.count", "Disks", constants.QFT_NUMBER), IQ_CONFIG,
+ lambda ctx, inst: (constants.QRFS_NORMAL, len(inst.disks))),
+ (_MakeField("disk.sizes", "Disk_sizes", constants.QFT_OTHER), IQ_CONFIG,
+ lambda ctx, inst: (constants.QRFS_NORMAL,
+ [disk.size for disk in inst.disks])),
+ ]
+
+ # Disks by number
+ fields.extend([
+ (_MakeField("disk.size/%s" % i, "Disk/%s" % i, constants.QFT_UNIT),
+ IQ_CONFIG, _GetInstDiskSize(i))
+ for i in range(constants.MAX_DISKS)
+ ])
+
+ return fields
+
+
+def _GetInstanceParameterFields():
+ """Get instance fields involving parameters.
+
+ @return: List of field definitions used as input for L{_PrepareFieldList}
+
+ """
+ # TODO: Consider moving titles closer to constants
+ be_title = {
+ constants.BE_AUTO_BALANCE: "Auto_balance",
+ constants.BE_MEMORY: "Configured_memory",
+ constants.BE_VCPUS: "VCPUs",
+ }
+
+ hv_title = {
+ constants.HV_ACPI: "ACPI",
+ constants.HV_BOOT_ORDER: "Boot_order",
+ constants.HV_CDROM_IMAGE_PATH: "CDROM_image_path",
+ constants.HV_DISK_TYPE: "Disk_type",
+ constants.HV_INITRD_PATH: "Initrd_path",
+ constants.HV_KERNEL_PATH: "Kernel_path",
+ constants.HV_NIC_TYPE: "NIC_type",
+ constants.HV_PAE: "PAE",
+ constants.HV_VNC_BIND_ADDRESS: "VNC_bind_address",
+ }
+
+ fields = [
+ # Filled parameters
+ (_MakeField("hvparams", "HypervisorParameters", constants.QFT_OTHER),
+ IQ_CONFIG, lambda ctx, _: (constants.QRFS_NORMAL, ctx.inst_hvparams)),
+ (_MakeField("beparams", "BackendParameters", constants.QFT_OTHER),
+ IQ_CONFIG, lambda ctx, _: (constants.QRFS_NORMAL, ctx.inst_beparams)),
+ (_MakeField("vcpus", "LegacyVCPUs", constants.QFT_NUMBER), IQ_CONFIG,
+ lambda ctx, _: (constants.QRFS_NORMAL,
+ ctx.inst_beparams[constants.BE_VCPUS])),
+
+ # Unfilled parameters
+ (_MakeField("custom_hvparams", "CustomHypervisorParameters",
+ constants.QFT_OTHER),
+ IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL, inst.hvparams)),
+ (_MakeField("custom_beparams", "CustomBackendParameters",
+ constants.QFT_OTHER),
+ IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL, inst.beparams)),
+ (_MakeField("custom_nicparams", "CustomNicParameters",
+ constants.QFT_OTHER),
+ IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL,
+ [nic.nicparams for nic in inst.nics])),
+ ]
+
+ # HV params
+ def _GetInstHvParam(name):
+ return lambda ctx, _: (constants.QRFS_NORMAL,
+ ctx.inst_hvparams.get(name, None))
+
+ fields.extend([
+ # For now all hypervisor parameters are exported as QFT_OTHER
+ (_MakeField("hv/%s" % name, hv_title.get(name, "hv/%s" % name),
+ constants.QFT_OTHER),
+ IQ_CONFIG, _GetInstHvParam(name))
+ for name in constants.HVS_PARAMETERS
+ if name not in constants.HVC_GLOBALS
+ ])
+
+ # BE params
+ def _GetInstBeParam(name):
+ return lambda ctx, _: (constants.QRFS_NORMAL,
+ ctx.inst_beparams.get(name, None))
+
+ fields.extend([
+ # For now all backend parameters are exported as QFT_OTHER
+ (_MakeField("be/%s" % name, be_title.get(name, "be/%s" % name),
+ constants.QFT_OTHER),
+ IQ_CONFIG, _GetInstBeParam(name))
+ for name in constants.BES_PARAMETERS
+ ])
+
+ return fields
+
+
+_INST_SIMPLE_FIELDS = {
+ "ctime": ("CTime", constants.QFT_TIMESTAMP),
+ "disk_template": ("Disk_template", constants.QFT_TEXT),
+ "hypervisor": ("Hypervisor", constants.QFT_TEXT),
+ "mtime": ("MTime", constants.QFT_TIMESTAMP),
+ "name": ("Node", constants.QFT_TEXT),
+ # Depending on the hypervisor, the port can be None
+ "network_port": ("Network_port", constants.QFT_OTHER),
+ "os": ("OS", constants.QFT_TEXT),
+ "serial_no": ("SerialNo", constants.QFT_NUMBER),
+ "uuid": ("UUID", constants.QFT_TEXT),
+ }
+
+
+def _BuildInstanceFields():
+ """Builds list of fields for instance queries.
+
+ """
+ fields = [
+ (_MakeField("pnode", "Primary_node", constants.QFT_TEXT), IQ_CONFIG,
+ lambda ctx, inst: (constants.QRFS_NORMAL, inst.primary_node)),
+ (_MakeField("snodes", "Secondary_Nodes", constants.QFT_OTHER), IQ_CONFIG,
+ lambda ctx, inst: (constants.QRFS_NORMAL, list(inst.secondary_nodes))),
+ (_MakeField("admin_state", "Autostart", constants.QFT_BOOL), IQ_CONFIG,
+ lambda ctx, inst: (constants.QRFS_NORMAL, inst.admin_up)),
+ (_MakeField("tags", "Tags", constants.QFT_OTHER), IQ_CONFIG,
+ lambda ctx, inst: (constants.QRFS_NORMAL, list(inst.GetTags()))),
+ ]
+
+ # Add simple fields
+ fields.extend([(_MakeField(name, title, kind), IQ_CONFIG, _GetItemAttr(name))
+ for (name, (title, kind)) in _INST_SIMPLE_FIELDS.items()])
+
+ # Fields requiring talking to the node
+ fields.extend([
+ (_MakeField("oper_state", "Running", constants.QFT_BOOL), IQ_LIVE,
+ _GetInstOperState),
+ (_MakeField("oper_ram", "RuntimeMemory", constants.QFT_UNIT), IQ_LIVE,
+ _GetInstLiveData("memory")),
+ (_MakeField("oper_vcpus", "RuntimeVCPUs", constants.QFT_NUMBER), IQ_LIVE,
+ _GetInstLiveData("vcpus")),
+ (_MakeField("status", "Status", constants.QFT_TEXT), IQ_LIVE,
+ _GetInstStatus),
+ ])
+
+ fields.extend(_GetInstanceParameterFields())
+ fields.extend(_GetInstanceDiskFields())
+ fields.extend(_GetInstanceNetworkFields())
+
+ return _PrepareFieldList(fields)
+
+
#: Fields available for node queries
NODE_FIELDS = _BuildNodeFields()
+
+#: Fields available for instance queries
+INSTANCE_FIELDS = _BuildInstanceFields()
from ganeti import errors
from ganeti import query
from ganeti import objects
+from ganeti import cmdlib
import testutils
"hello", constants.QFT_BOOL, ctx, None)
+class TestInstanceQuery(unittest.TestCase):
+ def _Create(self, selected):
+ return query.Query(query.INSTANCE_FIELDS, selected)
+
+ def testSimple(self):
+ q = self._Create(["name", "be/memory", "ip"])
+ self.assertEqual(q.RequestedData(), set([query.IQ_CONFIG]))
+
+ cluster = objects.Cluster(cluster_name="testcluster",
+ hvparams=constants.HVC_DEFAULTS,
+ beparams={
+ constants.PP_DEFAULT: constants.BEC_DEFAULTS,
+ },
+ nicparams={
+ constants.PP_DEFAULT: constants.NICC_DEFAULTS,
+ })
+
+ instances = [
+ objects.Instance(name="inst1", hvparams={}, beparams={}, nics=[]),
+ objects.Instance(name="inst2", hvparams={}, nics=[],
+ beparams={
+ constants.BE_MEMORY: 512,
+ }),
+ objects.Instance(name="inst3", hvparams={}, beparams={},
+ nics=[objects.NIC(ip="192.0.2.99", nicparams={})]),
+ ]
+
+ iqd = query.InstanceQueryData(instances, cluster, None, [], [], {})
+ self.assertEqual(q.Query(iqd),
+ [[(constants.QRFS_NORMAL, "inst1"),
+ (constants.QRFS_NORMAL, 128),
+ (constants.QRFS_UNAVAIL, None),
+ ],
+ [(constants.QRFS_NORMAL, "inst2"),
+ (constants.QRFS_NORMAL, 512),
+ (constants.QRFS_UNAVAIL, None),
+ ],
+ [(constants.QRFS_NORMAL, "inst3"),
+ (constants.QRFS_NORMAL, 128),
+ (constants.QRFS_NORMAL, "192.0.2.99"),
+ ]])
+ self.assertEqual(q.OldStyleQuery(iqd),
+ [["inst1", 128, None],
+ ["inst2", 512, None],
+ ["inst3", 128, "192.0.2.99"]])
+
+ def test(self):
+ selected = query.INSTANCE_FIELDS.keys()
+ fieldidx = dict((field, idx) for idx, field in enumerate(selected))
+
+ macs = ["00:11:22:%02x:%02x:%02x" % (i % 255, i % 3, (i * 123) % 255)
+ for i in range(20)]
+
+ q = self._Create(selected)
+ self.assertEqual(q.RequestedData(),
+ set([query.IQ_CONFIG, query.IQ_LIVE, query.IQ_DISKUSAGE]))
+
+ cluster = objects.Cluster(cluster_name="testcluster",
+ hvparams=constants.HVC_DEFAULTS,
+ beparams={
+ constants.PP_DEFAULT: constants.BEC_DEFAULTS,
+ },
+ nicparams={
+ constants.PP_DEFAULT: constants.NICC_DEFAULTS,
+ },
+ os_hvp={},
+ tcpudp_port_pool=set())
+
+ offline_nodes = ["nodeoff1", "nodeoff2"]
+ bad_nodes = ["nodebad1", "nodebad2", "nodebad3"] + offline_nodes
+ nodes = ["node%s" % i for i in range(10)] + bad_nodes
+
+ instances = [
+ objects.Instance(name="inst1", hvparams={}, beparams={}, nics=[],
+ uuid="f90eccb3-e227-4e3c-bf2a-94a21ca8f9cd",
+ ctime=1291244000, mtime=1291244400, serial_no=30,
+ admin_up=True, hypervisor=constants.HT_XEN_PVM, os="linux1",
+ primary_node="node1",
+ disk_template=constants.DT_PLAIN,
+ disks=[]),
+ objects.Instance(name="inst2", hvparams={}, nics=[],
+ uuid="73a0f8a7-068c-4630-ada2-c3440015ab1a",
+ ctime=1291211000, mtime=1291211077, serial_no=1,
+ admin_up=True, hypervisor=constants.HT_XEN_HVM, os="deb99",
+ primary_node="node5",
+ disk_template=constants.DT_DISKLESS,
+ disks=[],
+ beparams={
+ constants.BE_MEMORY: 512,
+ }),
+ objects.Instance(name="inst3", hvparams={}, beparams={},
+ uuid="11ec8dff-fb61-4850-bfe0-baa1803ff280",
+ ctime=1291011000, mtime=1291013000, serial_no=1923,
+ admin_up=False, hypervisor=constants.HT_KVM, os="busybox",
+ primary_node="node6",
+ disk_template=constants.DT_DRBD8,
+ disks=[],
+ nics=[
+ objects.NIC(ip="192.0.2.99", mac=macs.pop(),
+ nicparams={
+ constants.NIC_LINK: constants.DEFAULT_BRIDGE,
+ }),
+ objects.NIC(ip=None, mac=macs.pop(), nicparams={}),
+ ]),
+ objects.Instance(name="inst4", hvparams={}, beparams={},
+ uuid="68dab168-3ef5-4c9d-b4d3-801e0672068c",
+ ctime=1291244390, mtime=1291244395, serial_no=25,
+ admin_up=False, hypervisor=constants.HT_XEN_PVM, os="linux1",
+ primary_node="nodeoff2",
+ disk_template=constants.DT_DRBD8,
+ disks=[],
+ nics=[
+ objects.NIC(ip="192.0.2.1", mac=macs.pop(),
+ nicparams={
+ constants.NIC_LINK: constants.DEFAULT_BRIDGE,
+ }),
+ objects.NIC(ip="192.0.2.2", mac=macs.pop(), nicparams={}),
+ objects.NIC(ip="192.0.2.3", mac=macs.pop(),
+ nicparams={
+ constants.NIC_MODE: constants.NIC_MODE_ROUTED,
+ }),
+ objects.NIC(ip="192.0.2.4", mac=macs.pop(),
+ nicparams={
+ constants.NIC_MODE: constants.NIC_MODE_BRIDGED,
+ constants.NIC_LINK: "eth123",
+ }),
+ ]),
+ objects.Instance(name="inst5", hvparams={}, nics=[],
+ uuid="0e3dca12-5b42-4e24-98a2-415267545bd0",
+ ctime=1231211000, mtime=1261200000, serial_no=3,
+ admin_up=True, hypervisor=constants.HT_XEN_HVM, os="deb99",
+ primary_node="nodebad2",
+ disk_template=constants.DT_DISKLESS,
+ disks=[],
+ beparams={
+ constants.BE_MEMORY: 512,
+ }),
+ objects.Instance(name="inst6", hvparams={}, nics=[],
+ uuid="72de6580-c8d5-4661-b902-38b5785bb8b3",
+ ctime=7513, mtime=11501, serial_no=13390,
+ admin_up=False, hypervisor=constants.HT_XEN_HVM, os="deb99",
+ primary_node="node7",
+ disk_template=constants.DT_DISKLESS,
+ disks=[],
+ beparams={
+ constants.BE_MEMORY: 768,
+ }),
+ ]
+
+ disk_usage = dict((inst.name,
+ cmdlib._ComputeDiskSize(inst.disk_template,
+ [{"size": disk.size}
+ for disk in inst.disks]))
+ for inst in instances)
+
+ inst_bridges = {
+ "inst3": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE],
+ "inst4": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE,
+ None, "eth123"],
+ }
+
+ live_data = {
+ "inst2": {
+ "vcpus": 3,
+ },
+ "inst4": {
+ "memory": 123,
+ },
+ "inst6": {
+ "memory": 768,
+ },
+ }
+
+ iqd = query.InstanceQueryData(instances, cluster, disk_usage,
+ offline_nodes, bad_nodes, live_data)
+ result = q.Query(iqd)
+ self.assertEqual(len(result), len(instances))
+ self.assert_(compat.all(len(row) == len(selected)
+ for row in result))
+
+ assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
+ "Offline nodes not included in bad nodes"
+
+ tested_status = set()
+
+ for (inst, row) in zip(instances, result):
+ assert inst.primary_node in nodes
+
+ self.assertEqual(row[fieldidx["name"]],
+ (constants.QRFS_NORMAL, inst.name))
+
+ if inst.primary_node in offline_nodes:
+ exp_status = "ERROR_nodeoffline"
+ elif inst.primary_node in bad_nodes:
+ exp_status = "ERROR_nodedown"
+ elif inst.name in live_data:
+ if inst.admin_up:
+ exp_status = "running"
+ else:
+ exp_status = "ERROR_up"
+ elif inst.admin_up:
+ exp_status = "ERROR_down"
+ else:
+ exp_status = "ADMIN_down"
+
+ self.assertEqual(row[fieldidx["status"]],
+ (constants.QRFS_NORMAL, exp_status))
+
+ (_, status) = row[fieldidx["status"]]
+ tested_status.add(status)
+
+ for (field, livefield) in [("oper_ram", "memory"),
+ ("oper_vcpus", "vcpus")]:
+ if inst.primary_node in bad_nodes:
+ exp = (constants.QRFS_NODATA, None)
+ elif inst.name in live_data:
+ value = live_data[inst.name].get(livefield, None)
+ if value is None:
+ exp = (constants.QRFS_UNAVAIL, None)
+ else:
+ exp = (constants.QRFS_NORMAL, value)
+ else:
+ exp = (constants.QRFS_UNAVAIL, None)
+
+ self.assertEqual(row[fieldidx[field]], exp)
+
+ bridges = inst_bridges.get(inst.name, [])
+ self.assertEqual(row[fieldidx["nic.bridges"]],
+ (constants.QRFS_NORMAL, bridges))
+ if bridges:
+ self.assertEqual(row[fieldidx["bridge"]],
+ (constants.QRFS_NORMAL, bridges[0]))
+ else:
+ self.assertEqual(row[fieldidx["bridge"]],
+ (constants.QRFS_UNAVAIL, None))
+
+ for i in range(constants.MAX_NICS):
+ if i < len(bridges) and bridges[i] is not None:
+ exp = (constants.QRFS_NORMAL, bridges[i])
+ else:
+ exp = (constants.QRFS_UNAVAIL, None)
+ self.assertEqual(row[fieldidx["nic.bridge/%s" % i]], exp)
+
+ if inst.primary_node in bad_nodes:
+ exp = (constants.QRFS_NODATA, None)
+ else:
+ exp = (constants.QRFS_NORMAL, inst.name in live_data)
+ self.assertEqual(row[fieldidx["oper_state"]], exp)
+
+ usage = disk_usage[inst.name]
+ if usage is None:
+ usage = 0
+ self.assertEqual(row[fieldidx["disk_usage"]],
+ (constants.QRFS_NORMAL, usage))
+
+ self.assertEqual(row[fieldidx["sda_size"]], row[fieldidx["disk.size/0"]])
+ self.assertEqual(row[fieldidx["sdb_size"]], row[fieldidx["disk.size/1"]])
+
+ # Ensure all possible status' have been tested
+ self.assertEqual(tested_status,
+ set(["ERROR_nodeoffline", "ERROR_nodedown",
+ "running", "ERROR_up", "ERROR_down",
+ "ADMIN_down"]))
+
+
if __name__ == "__main__":
testutils.GanetiTestProgram()