Revision 1c8addc6

b/lib/query.py
38 38
 NQ_LIVE,
39 39
 NQ_GROUP) = range(1, 5)
40 40

  
41
(IQ_CONFIG,
42
 IQ_LIVE,
43
 IQ_DISKUSAGE) = range(100, 103)
44

  
41 45

  
42 46
FIELD_NAME_RE = re.compile(r"^[a-z0-9/._]+$")
43 47
TITLE_RE = re.compile(r"^[^\s]+$")
......
438 442
  return _PrepareFieldList(fields)
439 443

  
440 444

  
445
class InstanceQueryData:
446
  """Data container for instance data queries.
447

  
448
  """
449
  def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes,
450
               live_data):
451
    """Initializes this class.
452

  
453
    @param instances: List of instance objects
454
    @param cluster: Cluster object
455
    @type disk_usage: dict; instance name as key
456
    @param disk_usage: Per-instance disk usage
457
    @type offline_nodes: list of strings
458
    @param offline_nodes: List of offline nodes
459
    @type bad_nodes: list of strings
460
    @param bad_nodes: List of faulty nodes
461
    @type live_data: dict; instance name as key
462
    @param live_data: Per-instance live data
463

  
464
    """
465
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
466
           "Offline nodes not included in bad nodes"
467
    assert not (set(live_data.keys()) & set(bad_nodes)), \
468
           "Found live data for bad or offline nodes"
469

  
470
    self.instances = instances
471
    self.cluster = cluster
472
    self.disk_usage = disk_usage
473
    self.offline_nodes = offline_nodes
474
    self.bad_nodes = bad_nodes
475
    self.live_data = live_data
476

  
477
    # Used for individual rows
478
    self.inst_hvparams = None
479
    self.inst_beparams = None
480
    self.inst_nicparams = None
481

  
482
  def __iter__(self):
483
    """Iterate over all instances.
484

  
485
    This function has side-effects and only one instance of the resulting
486
    generator should be used at a time.
487

  
488
    """
489
    for inst in self.instances:
490
      self.inst_hvparams = self.cluster.FillHV(inst, skip_globals=True)
491
      self.inst_beparams = self.cluster.FillBE(inst)
492
      self.inst_nicparams = [self.cluster.SimpleFillNIC(nic.nicparams)
493
                             for nic in inst.nics]
494

  
495
      yield inst
496

  
497

  
498
def _GetInstOperState(ctx, inst):
499
  """Get instance's operational status.
500

  
501
  @type ctx: L{InstanceQueryData}
502
  @type inst: L{objects.Instance}
503
  @param inst: Instance object
504

  
505
  """
506
  if inst.primary_node in ctx.bad_nodes:
507
    return (constants.QRFS_NODATA, None)
508
  else:
509
    return (constants.QRFS_NORMAL, bool(ctx.live_data.get(inst.name)))
510

  
511

  
512
def _GetInstLiveData(name):
513
  """Build function for retrieving live data.
514

  
515
  @type name: string
516
  @param name: Live data field name
517

  
518
  """
519
  def fn(ctx, inst):
520
    """Get live data for an instance.
521

  
522
    @type ctx: L{InstanceQueryData}
523
    @type inst: L{objects.Instance}
524
    @param inst: Instance object
525

  
526
    """
527
    if (inst.primary_node in ctx.bad_nodes or
528
        inst.primary_node in ctx.offline_nodes):
529
      return (constants.QRFS_NODATA, None)
530

  
531
    if inst.name in ctx.live_data:
532
      data = ctx.live_data[inst.name]
533
      if name in data:
534
        return (constants.QRFS_NORMAL, data[name])
535

  
536
    return (constants.QRFS_UNAVAIL, None)
537

  
538
  return fn
539

  
540

  
541
def _GetInstStatus(ctx, inst):
542
  """Get instance status.
543

  
544
  @type ctx: L{InstanceQueryData}
545
  @type inst: L{objects.Instance}
546
  @param inst: Instance object
547

  
548
  """
549
  if inst.primary_node in ctx.offline_nodes:
550
    return (constants.QRFS_NORMAL, "ERROR_nodeoffline")
551

  
552
  if inst.primary_node in ctx.bad_nodes:
553
    return (constants.QRFS_NORMAL, "ERROR_nodedown")
554

  
555
  if bool(ctx.live_data.get(inst.name)):
556
    if inst.admin_up:
557
      return (constants.QRFS_NORMAL, "running")
558
    else:
559
      return (constants.QRFS_NORMAL, "ERROR_up")
560

  
561
  if inst.admin_up:
562
    return (constants.QRFS_NORMAL, "ERROR_down")
563

  
564
  return (constants.QRFS_NORMAL, "ADMIN_down")
565

  
566

  
567
def _GetInstDiskSize(index):
568
  """Build function for retrieving disk size.
569

  
570
  @type index: int
571
  @param index: Disk index
572

  
573
  """
574
  def fn(_, inst):
575
    """Get size of a disk.
576

  
577
    @type inst: L{objects.Instance}
578
    @param inst: Instance object
579

  
580
    """
581
    try:
582
      return (constants.QRFS_NORMAL, inst.disks[index].size)
583
    except IndexError:
584
      return (constants.QRFS_UNAVAIL, None)
585

  
586
  return fn
587

  
588

  
589
def _GetInstNic(index, cb):
590
  """Build function for calling another function with an instance NIC.
591

  
592
  @type index: int
593
  @param index: NIC index
594
  @type cb: callable
595
  @param cb: Callback
596

  
597
  """
598
  def fn(ctx, inst):
599
    """Call helper function with instance NIC.
600

  
601
    @type ctx: L{InstanceQueryData}
602
    @type inst: L{objects.Instance}
603
    @param inst: Instance object
604

  
605
    """
606
    try:
607
      nic = inst.nics[index]
608
    except IndexError:
609
      return (constants.QRFS_UNAVAIL, None)
610

  
611
    return cb(ctx, index, nic)
612

  
613
  return fn
614

  
615

  
616
def _GetInstNicIp(ctx, _, nic): # pylint: disable-msg=W0613
617
  """Get a NIC's IP address.
618

  
619
  @type ctx: L{InstanceQueryData}
620
  @type nic: L{objects.NIC}
621
  @param nic: NIC object
622

  
623
  """
624
  if nic.ip is None:
625
    return (constants.QRFS_UNAVAIL, None)
626
  else:
627
    return (constants.QRFS_NORMAL, nic.ip)
628

  
629

  
630
def _GetInstNicBridge(ctx, index, _):
631
  """Get a NIC's bridge.
632

  
633
  @type ctx: L{InstanceQueryData}
634
  @type index: int
635
  @param index: NIC index
636

  
637
  """
638
  assert len(ctx.inst_nicparams) >= index
639

  
640
  nicparams = ctx.inst_nicparams[index]
641

  
642
  if nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
643
    return (constants.QRFS_NORMAL, nicparams[constants.NIC_LINK])
644
  else:
645
    return (constants.QRFS_UNAVAIL, None)
646

  
647

  
648
def _GetInstAllNicBridges(ctx, inst):
649
  """Get all network bridges for an instance.
650

  
651
  @type ctx: L{InstanceQueryData}
652
  @type inst: L{objects.Instance}
653
  @param inst: Instance object
654

  
655
  """
656
  assert len(ctx.inst_nicparams) == len(inst.nics)
657

  
658
  result = []
659

  
660
  for nicp in ctx.inst_nicparams:
661
    if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
662
      result.append(nicp[constants.NIC_LINK])
663
    else:
664
      result.append(None)
665

  
666
  assert len(result) == len(inst.nics)
667

  
668
  return (constants.QRFS_NORMAL, result)
669

  
670

  
671
def _GetInstNicParam(name):
672
  """Build function for retrieving a NIC parameter.
673

  
674
  @type name: string
675
  @param name: Parameter name
676

  
677
  """
678
  def fn(ctx, index, _):
679
    """Get a NIC's bridge.
680

  
681
    @type ctx: L{InstanceQueryData}
682
    @type inst: L{objects.Instance}
683
    @param inst: Instance object
684
    @type nic: L{objects.NIC}
685
    @param nic: NIC object
686

  
687
    """
688
    assert len(ctx.inst_nicparams) >= index
689
    return (constants.QRFS_NORMAL, ctx.inst_nicparams[index][name])
690

  
691
  return fn
692

  
693

  
694
def _GetInstanceNetworkFields():
695
  """Get instance fields involving network interfaces.
696

  
697
  @return: List of field definitions used as input for L{_PrepareFieldList}
698

  
699
  """
700
  nic_mac_fn = lambda ctx, _, nic: (constants.QRFS_NORMAL, nic.mac)
701
  nic_mode_fn = _GetInstNicParam(constants.NIC_MODE)
702
  nic_link_fn = _GetInstNicParam(constants.NIC_LINK)
703

  
704
  fields = [
705
    # First NIC (legacy)
706
    (_MakeField("ip", "IP_address", constants.QFT_TEXT), IQ_CONFIG,
707
     _GetInstNic(0, _GetInstNicIp)),
708
    (_MakeField("mac", "MAC_address", constants.QFT_TEXT), IQ_CONFIG,
709
     _GetInstNic(0, nic_mac_fn)),
710
    (_MakeField("bridge", "Bridge", constants.QFT_TEXT), IQ_CONFIG,
711
     _GetInstNic(0, _GetInstNicBridge)),
712
    (_MakeField("nic_mode", "NIC_Mode", constants.QFT_TEXT), IQ_CONFIG,
713
     _GetInstNic(0, nic_mode_fn)),
714
    (_MakeField("nic_link", "NIC_Link", constants.QFT_TEXT), IQ_CONFIG,
715
     _GetInstNic(0, nic_link_fn)),
716

  
717
    # All NICs
718
    (_MakeField("nic.count", "NICs", constants.QFT_NUMBER), IQ_CONFIG,
719
     lambda ctx, inst: (constants.QRFS_NORMAL, len(inst.nics))),
720
    (_MakeField("nic.macs", "NIC_MACs", constants.QFT_OTHER), IQ_CONFIG,
721
     lambda ctx, inst: (constants.QRFS_NORMAL, [nic.mac for nic in inst.nics])),
722
    (_MakeField("nic.ips", "NIC_IPs", constants.QFT_OTHER), IQ_CONFIG,
723
     lambda ctx, inst: (constants.QRFS_NORMAL, [nic.ip for nic in inst.nics])),
724
    (_MakeField("nic.modes", "NIC_modes", constants.QFT_OTHER), IQ_CONFIG,
725
     lambda ctx, inst: (constants.QRFS_NORMAL,
726
                        [nicp[constants.NIC_MODE]
727
                         for nicp in ctx.inst_nicparams])),
728
    (_MakeField("nic.links", "NIC_links", constants.QFT_OTHER), IQ_CONFIG,
729
     lambda ctx, inst: (constants.QRFS_NORMAL,
730
                        [nicp[constants.NIC_LINK]
731
                         for nicp in ctx.inst_nicparams])),
732
    (_MakeField("nic.bridges", "NIC_bridges", constants.QFT_OTHER), IQ_CONFIG,
733
     _GetInstAllNicBridges),
734
    ]
735

  
736
  # NICs by number
737
  for i in range(constants.MAX_NICS):
738
    fields.extend([
739
      (_MakeField("nic.ip/%s" % i, "NicIP/%s" % i, constants.QFT_TEXT),
740
       IQ_CONFIG, _GetInstNic(i, _GetInstNicIp)),
741
      (_MakeField("nic.mac/%s" % i, "NicMAC/%s" % i, constants.QFT_TEXT),
742
       IQ_CONFIG, _GetInstNic(i, nic_mac_fn)),
743
      (_MakeField("nic.mode/%s" % i, "NicMode/%s" % i, constants.QFT_TEXT),
744
       IQ_CONFIG, _GetInstNic(i, nic_mode_fn)),
745
      (_MakeField("nic.link/%s" % i, "NicLink/%s" % i, constants.QFT_TEXT),
746
       IQ_CONFIG, _GetInstNic(i, nic_link_fn)),
747
      (_MakeField("nic.bridge/%s" % i, "NicBridge/%s" % i, constants.QFT_TEXT),
748
       IQ_CONFIG, _GetInstNic(i, _GetInstNicBridge)),
749
      ])
750

  
751
  return fields
752

  
753

  
754
def _GetInstDiskUsage(ctx, inst):
755
  """Get disk usage for an instance.
756

  
757
  @type ctx: L{InstanceQueryData}
758
  @type inst: L{objects.Instance}
759
  @param inst: Instance object
760

  
761
  """
762
  usage = ctx.disk_usage[inst.name]
763

  
764
  if usage is None:
765
    usage = 0
766

  
767
  return (constants.QRFS_NORMAL, usage)
768

  
769

  
770
def _GetInstanceDiskFields():
771
  """Get instance fields involving disks.
772

  
773
  @return: List of field definitions used as input for L{_PrepareFieldList}
774

  
775
  """
776
  fields = [
777
    (_MakeField("disk_usage", "DiskUsage", constants.QFT_UNIT), IQ_DISKUSAGE,
778
     _GetInstDiskUsage),
779
    (_MakeField("sda_size", "LegacyDisk/0", constants.QFT_UNIT), IQ_CONFIG,
780
     _GetInstDiskSize(0)),
781
    (_MakeField("sdb_size", "LegacyDisk/1", constants.QFT_UNIT), IQ_CONFIG,
782
     _GetInstDiskSize(1)),
783
    (_MakeField("disk.count", "Disks", constants.QFT_NUMBER), IQ_CONFIG,
784
     lambda ctx, inst: (constants.QRFS_NORMAL, len(inst.disks))),
785
    (_MakeField("disk.sizes", "Disk_sizes", constants.QFT_OTHER), IQ_CONFIG,
786
     lambda ctx, inst: (constants.QRFS_NORMAL,
787
                        [disk.size for disk in inst.disks])),
788
    ]
789

  
790
  # Disks by number
791
  fields.extend([
792
    (_MakeField("disk.size/%s" % i, "Disk/%s" % i, constants.QFT_UNIT),
793
     IQ_CONFIG, _GetInstDiskSize(i))
794
    for i in range(constants.MAX_DISKS)
795
    ])
796

  
797
  return fields
798

  
799

  
800
def _GetInstanceParameterFields():
801
  """Get instance fields involving parameters.
802

  
803
  @return: List of field definitions used as input for L{_PrepareFieldList}
804

  
805
  """
806
  # TODO: Consider moving titles closer to constants
807
  be_title = {
808
    constants.BE_AUTO_BALANCE: "Auto_balance",
809
    constants.BE_MEMORY: "Configured_memory",
810
    constants.BE_VCPUS: "VCPUs",
811
    }
812

  
813
  hv_title = {
814
    constants.HV_ACPI: "ACPI",
815
    constants.HV_BOOT_ORDER: "Boot_order",
816
    constants.HV_CDROM_IMAGE_PATH: "CDROM_image_path",
817
    constants.HV_DISK_TYPE: "Disk_type",
818
    constants.HV_INITRD_PATH: "Initrd_path",
819
    constants.HV_KERNEL_PATH: "Kernel_path",
820
    constants.HV_NIC_TYPE: "NIC_type",
821
    constants.HV_PAE: "PAE",
822
    constants.HV_VNC_BIND_ADDRESS: "VNC_bind_address",
823
    }
824

  
825
  fields = [
826
    # Filled parameters
827
    (_MakeField("hvparams", "HypervisorParameters", constants.QFT_OTHER),
828
     IQ_CONFIG, lambda ctx, _: (constants.QRFS_NORMAL, ctx.inst_hvparams)),
829
    (_MakeField("beparams", "BackendParameters", constants.QFT_OTHER),
830
     IQ_CONFIG, lambda ctx, _: (constants.QRFS_NORMAL, ctx.inst_beparams)),
831
    (_MakeField("vcpus", "LegacyVCPUs", constants.QFT_NUMBER), IQ_CONFIG,
832
     lambda ctx, _: (constants.QRFS_NORMAL,
833
                     ctx.inst_beparams[constants.BE_VCPUS])),
834

  
835
    # Unfilled parameters
836
    (_MakeField("custom_hvparams", "CustomHypervisorParameters",
837
                constants.QFT_OTHER),
838
     IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL, inst.hvparams)),
839
    (_MakeField("custom_beparams", "CustomBackendParameters",
840
                constants.QFT_OTHER),
841
     IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL, inst.beparams)),
842
    (_MakeField("custom_nicparams", "CustomNicParameters",
843
                constants.QFT_OTHER),
844
     IQ_CONFIG, lambda ctx, inst: (constants.QRFS_NORMAL,
845
                                   [nic.nicparams for nic in inst.nics])),
846
    ]
847

  
848
  # HV params
849
  def _GetInstHvParam(name):
850
    return lambda ctx, _: (constants.QRFS_NORMAL,
851
                           ctx.inst_hvparams.get(name, None))
852

  
853
  fields.extend([
854
    # For now all hypervisor parameters are exported as QFT_OTHER
855
    (_MakeField("hv/%s" % name, hv_title.get(name, "hv/%s" % name),
856
                constants.QFT_OTHER),
857
     IQ_CONFIG, _GetInstHvParam(name))
858
    for name in constants.HVS_PARAMETERS
859
    if name not in constants.HVC_GLOBALS
860
    ])
861

  
862
  # BE params
863
  def _GetInstBeParam(name):
864
    return lambda ctx, _: (constants.QRFS_NORMAL,
865
                           ctx.inst_beparams.get(name, None))
866

  
867
  fields.extend([
868
    # For now all backend parameters are exported as QFT_OTHER
869
    (_MakeField("be/%s" % name, be_title.get(name, "be/%s" % name),
870
                constants.QFT_OTHER),
871
     IQ_CONFIG, _GetInstBeParam(name))
872
    for name in constants.BES_PARAMETERS
873
    ])
874

  
875
  return fields
876

  
877

  
878
_INST_SIMPLE_FIELDS = {
879
  "ctime": ("CTime", constants.QFT_TIMESTAMP),
880
  "disk_template": ("Disk_template", constants.QFT_TEXT),
881
  "hypervisor": ("Hypervisor", constants.QFT_TEXT),
882
  "mtime": ("MTime", constants.QFT_TIMESTAMP),
883
  "name": ("Node", constants.QFT_TEXT),
884
  # Depending on the hypervisor, the port can be None
885
  "network_port": ("Network_port", constants.QFT_OTHER),
886
  "os": ("OS", constants.QFT_TEXT),
887
  "serial_no": ("SerialNo", constants.QFT_NUMBER),
888
  "uuid": ("UUID", constants.QFT_TEXT),
889
  }
890

  
891

  
892
def _BuildInstanceFields():
893
  """Builds list of fields for instance queries.
894

  
895
  """
896
  fields = [
897
    (_MakeField("pnode", "Primary_node", constants.QFT_TEXT), IQ_CONFIG,
898
     lambda ctx, inst: (constants.QRFS_NORMAL, inst.primary_node)),
899
    (_MakeField("snodes", "Secondary_Nodes", constants.QFT_OTHER), IQ_CONFIG,
900
     lambda ctx, inst: (constants.QRFS_NORMAL, list(inst.secondary_nodes))),
901
    (_MakeField("admin_state", "Autostart", constants.QFT_BOOL), IQ_CONFIG,
902
     lambda ctx, inst: (constants.QRFS_NORMAL, inst.admin_up)),
903
    (_MakeField("tags", "Tags", constants.QFT_OTHER), IQ_CONFIG,
904
     lambda ctx, inst: (constants.QRFS_NORMAL, list(inst.GetTags()))),
905
    ]
906

  
907
  # Add simple fields
908
  fields.extend([(_MakeField(name, title, kind), IQ_CONFIG, _GetItemAttr(name))
909
                 for (name, (title, kind)) in _INST_SIMPLE_FIELDS.items()])
910

  
911
  # Fields requiring talking to the node
912
  fields.extend([
913
    (_MakeField("oper_state", "Running", constants.QFT_BOOL), IQ_LIVE,
914
     _GetInstOperState),
915
    (_MakeField("oper_ram", "RuntimeMemory", constants.QFT_UNIT), IQ_LIVE,
916
     _GetInstLiveData("memory")),
917
    (_MakeField("oper_vcpus", "RuntimeVCPUs", constants.QFT_NUMBER), IQ_LIVE,
918
     _GetInstLiveData("vcpus")),
919
    (_MakeField("status", "Status", constants.QFT_TEXT), IQ_LIVE,
920
     _GetInstStatus),
921
    ])
922

  
923
  fields.extend(_GetInstanceParameterFields())
924
  fields.extend(_GetInstanceDiskFields())
925
  fields.extend(_GetInstanceNetworkFields())
926

  
927
  return _PrepareFieldList(fields)
928

  
929

  
441 930
#: Fields available for node queries
442 931
NODE_FIELDS = _BuildNodeFields()
932

  
933
#: Fields available for instance queries
934
INSTANCE_FIELDS = _BuildInstanceFields()
b/test/ganeti.query_unittest.py
30 30
from ganeti import errors
31 31
from ganeti import query
32 32
from ganeti import objects
33
from ganeti import cmdlib
33 34

  
34 35
import testutils
35 36

  
......
458 459
                      "hello", constants.QFT_BOOL, ctx, None)
459 460

  
460 461

  
462
class TestInstanceQuery(unittest.TestCase):
463
  def _Create(self, selected):
464
    return query.Query(query.INSTANCE_FIELDS, selected)
465

  
466
  def testSimple(self):
467
    q = self._Create(["name", "be/memory", "ip"])
468
    self.assertEqual(q.RequestedData(), set([query.IQ_CONFIG]))
469

  
470
    cluster = objects.Cluster(cluster_name="testcluster",
471
      hvparams=constants.HVC_DEFAULTS,
472
      beparams={
473
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
474
        },
475
      nicparams={
476
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
477
        })
478

  
479
    instances = [
480
      objects.Instance(name="inst1", hvparams={}, beparams={}, nics=[]),
481
      objects.Instance(name="inst2", hvparams={}, nics=[],
482
        beparams={
483
          constants.BE_MEMORY: 512,
484
        }),
485
      objects.Instance(name="inst3", hvparams={}, beparams={},
486
        nics=[objects.NIC(ip="192.0.2.99", nicparams={})]),
487
      ]
488

  
489
    iqd = query.InstanceQueryData(instances, cluster, None, [], [], {})
490
    self.assertEqual(q.Query(iqd),
491
      [[(constants.QRFS_NORMAL, "inst1"),
492
        (constants.QRFS_NORMAL, 128),
493
        (constants.QRFS_UNAVAIL, None),
494
       ],
495
       [(constants.QRFS_NORMAL, "inst2"),
496
        (constants.QRFS_NORMAL, 512),
497
        (constants.QRFS_UNAVAIL, None),
498
       ],
499
       [(constants.QRFS_NORMAL, "inst3"),
500
        (constants.QRFS_NORMAL, 128),
501
        (constants.QRFS_NORMAL, "192.0.2.99"),
502
       ]])
503
    self.assertEqual(q.OldStyleQuery(iqd),
504
      [["inst1", 128, None],
505
       ["inst2", 512, None],
506
       ["inst3", 128, "192.0.2.99"]])
507

  
508
  def test(self):
509
    selected = query.INSTANCE_FIELDS.keys()
510
    fieldidx = dict((field, idx) for idx, field in enumerate(selected))
511

  
512
    macs = ["00:11:22:%02x:%02x:%02x" % (i % 255, i % 3, (i * 123) % 255)
513
            for i in range(20)]
514

  
515
    q = self._Create(selected)
516
    self.assertEqual(q.RequestedData(),
517
                     set([query.IQ_CONFIG, query.IQ_LIVE, query.IQ_DISKUSAGE]))
518

  
519
    cluster = objects.Cluster(cluster_name="testcluster",
520
      hvparams=constants.HVC_DEFAULTS,
521
      beparams={
522
        constants.PP_DEFAULT: constants.BEC_DEFAULTS,
523
        },
524
      nicparams={
525
        constants.PP_DEFAULT: constants.NICC_DEFAULTS,
526
        },
527
      os_hvp={},
528
      tcpudp_port_pool=set())
529

  
530
    offline_nodes = ["nodeoff1", "nodeoff2"]
531
    bad_nodes = ["nodebad1", "nodebad2", "nodebad3"] + offline_nodes
532
    nodes = ["node%s" % i for i in range(10)] + bad_nodes
533

  
534
    instances = [
535
      objects.Instance(name="inst1", hvparams={}, beparams={}, nics=[],
536
        uuid="f90eccb3-e227-4e3c-bf2a-94a21ca8f9cd",
537
        ctime=1291244000, mtime=1291244400, serial_no=30,
538
        admin_up=True, hypervisor=constants.HT_XEN_PVM, os="linux1",
539
        primary_node="node1",
540
        disk_template=constants.DT_PLAIN,
541
        disks=[]),
542
      objects.Instance(name="inst2", hvparams={}, nics=[],
543
        uuid="73a0f8a7-068c-4630-ada2-c3440015ab1a",
544
        ctime=1291211000, mtime=1291211077, serial_no=1,
545
        admin_up=True, hypervisor=constants.HT_XEN_HVM, os="deb99",
546
        primary_node="node5",
547
        disk_template=constants.DT_DISKLESS,
548
        disks=[],
549
        beparams={
550
          constants.BE_MEMORY: 512,
551
        }),
552
      objects.Instance(name="inst3", hvparams={}, beparams={},
553
        uuid="11ec8dff-fb61-4850-bfe0-baa1803ff280",
554
        ctime=1291011000, mtime=1291013000, serial_no=1923,
555
        admin_up=False, hypervisor=constants.HT_KVM, os="busybox",
556
        primary_node="node6",
557
        disk_template=constants.DT_DRBD8,
558
        disks=[],
559
        nics=[
560
          objects.NIC(ip="192.0.2.99", mac=macs.pop(),
561
                      nicparams={
562
                        constants.NIC_LINK: constants.DEFAULT_BRIDGE,
563
                        }),
564
          objects.NIC(ip=None, mac=macs.pop(), nicparams={}),
565
          ]),
566
      objects.Instance(name="inst4", hvparams={}, beparams={},
567
        uuid="68dab168-3ef5-4c9d-b4d3-801e0672068c",
568
        ctime=1291244390, mtime=1291244395, serial_no=25,
569
        admin_up=False, hypervisor=constants.HT_XEN_PVM, os="linux1",
570
        primary_node="nodeoff2",
571
        disk_template=constants.DT_DRBD8,
572
        disks=[],
573
        nics=[
574
          objects.NIC(ip="192.0.2.1", mac=macs.pop(),
575
                      nicparams={
576
                        constants.NIC_LINK: constants.DEFAULT_BRIDGE,
577
                        }),
578
          objects.NIC(ip="192.0.2.2", mac=macs.pop(), nicparams={}),
579
          objects.NIC(ip="192.0.2.3", mac=macs.pop(),
580
                      nicparams={
581
                        constants.NIC_MODE: constants.NIC_MODE_ROUTED,
582
                        }),
583
          objects.NIC(ip="192.0.2.4", mac=macs.pop(),
584
                      nicparams={
585
                        constants.NIC_MODE: constants.NIC_MODE_BRIDGED,
586
                        constants.NIC_LINK: "eth123",
587
                        }),
588
          ]),
589
      objects.Instance(name="inst5", hvparams={}, nics=[],
590
        uuid="0e3dca12-5b42-4e24-98a2-415267545bd0",
591
        ctime=1231211000, mtime=1261200000, serial_no=3,
592
        admin_up=True, hypervisor=constants.HT_XEN_HVM, os="deb99",
593
        primary_node="nodebad2",
594
        disk_template=constants.DT_DISKLESS,
595
        disks=[],
596
        beparams={
597
          constants.BE_MEMORY: 512,
598
        }),
599
      objects.Instance(name="inst6", hvparams={}, nics=[],
600
        uuid="72de6580-c8d5-4661-b902-38b5785bb8b3",
601
        ctime=7513, mtime=11501, serial_no=13390,
602
        admin_up=False, hypervisor=constants.HT_XEN_HVM, os="deb99",
603
        primary_node="node7",
604
        disk_template=constants.DT_DISKLESS,
605
        disks=[],
606
        beparams={
607
          constants.BE_MEMORY: 768,
608
        }),
609
      ]
610

  
611
    disk_usage = dict((inst.name,
612
                       cmdlib._ComputeDiskSize(inst.disk_template,
613
                                               [{"size": disk.size}
614
                                                for disk in inst.disks]))
615
                      for inst in instances)
616

  
617
    inst_bridges = {
618
      "inst3": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE],
619
      "inst4": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE,
620
                None, "eth123"],
621
      }
622

  
623
    live_data = {
624
      "inst2": {
625
        "vcpus": 3,
626
        },
627
      "inst4": {
628
        "memory": 123,
629
        },
630
      "inst6": {
631
        "memory": 768,
632
        },
633
      }
634

  
635
    iqd = query.InstanceQueryData(instances, cluster, disk_usage,
636
                                  offline_nodes, bad_nodes, live_data)
637
    result = q.Query(iqd)
638
    self.assertEqual(len(result), len(instances))
639
    self.assert_(compat.all(len(row) == len(selected)
640
                            for row in result))
641

  
642
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
643
           "Offline nodes not included in bad nodes"
644

  
645
    tested_status = set()
646

  
647
    for (inst, row) in zip(instances, result):
648
      assert inst.primary_node in nodes
649

  
650
      self.assertEqual(row[fieldidx["name"]],
651
                       (constants.QRFS_NORMAL, inst.name))
652

  
653
      if inst.primary_node in offline_nodes:
654
        exp_status = "ERROR_nodeoffline"
655
      elif inst.primary_node in bad_nodes:
656
        exp_status = "ERROR_nodedown"
657
      elif inst.name in live_data:
658
        if inst.admin_up:
659
          exp_status = "running"
660
        else:
661
          exp_status = "ERROR_up"
662
      elif inst.admin_up:
663
        exp_status = "ERROR_down"
664
      else:
665
        exp_status = "ADMIN_down"
666

  
667
      self.assertEqual(row[fieldidx["status"]],
668
                       (constants.QRFS_NORMAL, exp_status))
669

  
670
      (_, status) = row[fieldidx["status"]]
671
      tested_status.add(status)
672

  
673
      for (field, livefield) in [("oper_ram", "memory"),
674
                                 ("oper_vcpus", "vcpus")]:
675
        if inst.primary_node in bad_nodes:
676
          exp = (constants.QRFS_NODATA, None)
677
        elif inst.name in live_data:
678
          value = live_data[inst.name].get(livefield, None)
679
          if value is None:
680
            exp = (constants.QRFS_UNAVAIL, None)
681
          else:
682
            exp = (constants.QRFS_NORMAL, value)
683
        else:
684
          exp = (constants.QRFS_UNAVAIL, None)
685

  
686
        self.assertEqual(row[fieldidx[field]], exp)
687

  
688
      bridges = inst_bridges.get(inst.name, [])
689
      self.assertEqual(row[fieldidx["nic.bridges"]],
690
                       (constants.QRFS_NORMAL, bridges))
691
      if bridges:
692
        self.assertEqual(row[fieldidx["bridge"]],
693
                         (constants.QRFS_NORMAL, bridges[0]))
694
      else:
695
        self.assertEqual(row[fieldidx["bridge"]],
696
                         (constants.QRFS_UNAVAIL, None))
697

  
698
      for i in range(constants.MAX_NICS):
699
        if i < len(bridges) and bridges[i] is not None:
700
          exp = (constants.QRFS_NORMAL, bridges[i])
701
        else:
702
          exp = (constants.QRFS_UNAVAIL, None)
703
        self.assertEqual(row[fieldidx["nic.bridge/%s" % i]], exp)
704

  
705
      if inst.primary_node in bad_nodes:
706
        exp = (constants.QRFS_NODATA, None)
707
      else:
708
        exp = (constants.QRFS_NORMAL, inst.name in live_data)
709
      self.assertEqual(row[fieldidx["oper_state"]], exp)
710

  
711
      usage = disk_usage[inst.name]
712
      if usage is None:
713
        usage = 0
714
      self.assertEqual(row[fieldidx["disk_usage"]],
715
                       (constants.QRFS_NORMAL, usage))
716

  
717
      self.assertEqual(row[fieldidx["sda_size"]], row[fieldidx["disk.size/0"]])
718
      self.assertEqual(row[fieldidx["sdb_size"]], row[fieldidx["disk.size/1"]])
719

  
720
    # Ensure all possible status' have been tested
721
    self.assertEqual(tested_status,
722
                     set(["ERROR_nodeoffline", "ERROR_nodedown",
723
                          "running", "ERROR_up", "ERROR_down",
724
                          "ADMIN_down"]))
725

  
726

  
461 727
if __name__ == "__main__":
462 728
  testutils.GanetiTestProgram()

Also available in: Unified diff