Revision 255dcebd

b/daemons/ganeti-noded
608 608
    """Query detailed information about existing OSes.
609 609

  
610 610
    """
611
    return True, [os_obj.ToDict() for os_obj in backend.DiagnoseOS()]
611
    return backend.DiagnoseOS()
612 612

  
613 613
  @staticmethod
614 614
  def perspective_os_get(params):
......
616 616

  
617 617
    """
618 618
    name = params[0]
619
    try:
620
      os_obj = backend.OSFromDisk(name)
621
    except errors.InvalidOS, err:
622
      os_obj = objects.OS.FromInvalidOS(err)
623
    return os_obj.ToDict()
619
    os_obj = backend.OSFromDisk(name)
620
    return True, os_obj.ToDict()
624 621

  
625 622
  # hooks -----------------------
626 623

  
b/lib/backend.py
718 718
  @return: the success of the operation
719 719

  
720 720
  """
721
  try:
722
    inst_os = OSFromDisk(instance.os)
723
  except errors.InvalidOS, err:
724
    os_name, os_dir, os_err = err.args
725
    if os_dir is None:
726
      return (False, "Can't find OS '%s': %s" % (os_name, os_err))
727
    else:
728
      return (False, "Error parsing OS '%s' in directory %s: %s" %
729
              (os_name, os_dir, os_err))
721
  inst_os = OSFromDisk(instance.os)
722

  
730 723

  
731 724
  create_env = OSEnvironment(instance)
732 725
  if reinstall:
......
1530 1523
  try:
1531 1524
    st = os.stat(api_file)
1532 1525
  except EnvironmentError, err:
1533
    raise errors.InvalidOS(name, os_dir, "'ganeti_api_version' file not"
1534
                           " found (%s)" % _ErrnoOrStr(err))
1526
    return False, ("Required file 'ganeti_api_version' file not"
1527
                   " found under path %s: %s" % (os_dir, _ErrnoOrStr(err)))
1535 1528

  
1536 1529
  if not stat.S_ISREG(stat.S_IFMT(st.st_mode)):
1537
    raise errors.InvalidOS(name, os_dir, "'ganeti_api_version' file is not"
1538
                           " a regular file")
1530
    return False, ("File 'ganeti_api_version' file at %s is not"
1531
                   " a regular file" % os_dir)
1539 1532

  
1540 1533
  try:
1541 1534
    f = open(api_file)
......
1544 1537
    finally:
1545 1538
      f.close()
1546 1539
  except EnvironmentError, err:
1547
    raise errors.InvalidOS(name, os_dir, "error while reading the"
1548
                           " API version (%s)" % _ErrnoOrStr(err))
1540
    return False, ("Error while reading the API version file at %s: %s" %
1541
                   (api_file, _ErrnoOrStr(err)))
1549 1542

  
1550 1543
  api_versions = [version.strip() for version in api_versions]
1551 1544
  try:
1552 1545
    api_versions = [int(version) for version in api_versions]
1553 1546
  except (TypeError, ValueError), err:
1554
    raise errors.InvalidOS(name, os_dir,
1555
                           "API version is not integer (%s)" % str(err))
1547
    return False, ("API version(s) can't be converted to integer: %s" %
1548
                   str(err))
1556 1549

  
1557
  return api_versions
1550
  return True, api_versions
1558 1551

  
1559 1552

  
1560 1553
def DiagnoseOS(top_dirs=None):
......
1565 1558
      search (if not given defaults to
1566 1559
      L{constants.OS_SEARCH_PATH})
1567 1560
  @rtype: list of L{objects.OS}
1568
  @return: an OS object for each name in all the given
1569
      directories
1561
  @return: a list of tuples (name, path, status, diagnose)
1562
      for all (potential) OSes under all search paths, where:
1563
          - name is the (potential) OS name
1564
          - path is the full path to the OS
1565
          - status True/False is the validity of the OS
1566
          - diagnose is the error message for an invalid OS, otherwise empty
1570 1567

  
1571 1568
  """
1572 1569
  if top_dirs is None:
......
1581 1578
        logging.exception("Can't list the OS directory %s", dir_name)
1582 1579
        break
1583 1580
      for name in f_names:
1584
        try:
1585
          os_inst = OSFromDisk(name, base_dir=dir_name)
1586
          result.append(os_inst)
1587
        except errors.InvalidOS, err:
1588
          result.append(objects.OS.FromInvalidOS(err))
1581
        os_path = os.path.sep.join([dir_name, name])
1582
        status, os_inst = _TryOSFromDisk(name, base_dir=dir_name)
1583
        if status:
1584
          diagnose = ""
1585
        else:
1586
          diagnose = os_inst
1587
        result.append((name, os_path, status, diagnose))
1589 1588

  
1590
  return result
1589
  return True, result
1591 1590

  
1592 1591

  
1593
def OSFromDisk(name, base_dir=None):
1592
def _TryOSFromDisk(name, base_dir=None):
1594 1593
  """Create an OS instance from disk.
1595 1594

  
1596 1595
  This function will return an OS instance if the given name is a
......
1600 1599
  @type base_dir: string
1601 1600
  @keyword base_dir: Base directory containing OS installations.
1602 1601
                     Defaults to a search in all the OS_SEARCH_PATH dirs.
1603
  @rtype: L{objects.OS}
1604
  @return: the OS instance if we find a valid one
1605
  @raise errors.InvalidOS: if we don't find a valid OS
1602
  @rtype: tuple
1603
  @return: success and either the OS instance if we find a valid one,
1604
      or error message
1606 1605

  
1607 1606
  """
1608 1607
  if base_dir is None:
1609 1608
    os_dir = utils.FindFile(name, constants.OS_SEARCH_PATH, os.path.isdir)
1610 1609
    if os_dir is None:
1611
      raise errors.InvalidOS(name, None, "OS dir not found in search path")
1610
      return False, "Directory for OS %s not found in search path" % name
1612 1611
  else:
1613 1612
    os_dir = os.path.sep.join([base_dir, name])
1614 1613

  
1615
  api_versions = _OSOndiskVersion(name, os_dir)
1614
  status, api_versions = _OSOndiskVersion(name, os_dir)
1615
  if not status:
1616
    # push the error up
1617
    return status, api_versions
1616 1618

  
1617 1619
  if constants.OS_API_VERSION not in api_versions:
1618
    raise errors.InvalidOS(name, os_dir, "API version mismatch"
1619
                           " (found %s want %s)"
1620
                           % (api_versions, constants.OS_API_VERSION))
1620
    return False, ("API version mismatch for path '%s': found %s, want %s." %
1621
                   (os_dir, api_versions, constants.OS_API_VERSION))
1621 1622

  
1622 1623
  # OS Scripts dictionary, we will populate it with the actual script names
1623 1624
  os_scripts = dict.fromkeys(constants.OS_SCRIPTS)
......
1628 1629
    try:
1629 1630
      st = os.stat(os_scripts[script])
1630 1631
    except EnvironmentError, err:
1631
      raise errors.InvalidOS(name, os_dir, "'%s' script missing (%s)" %
1632
                             (script, _ErrnoOrStr(err)))
1632
      return False, ("Script '%s' under path '%s' is missing (%s)" %
1633
                     (script, os_dir, _ErrnoOrStr(err)))
1633 1634

  
1634 1635
    if stat.S_IMODE(st.st_mode) & stat.S_IXUSR != stat.S_IXUSR:
1635
      raise errors.InvalidOS(name, os_dir, "'%s' script not executable" %
1636
                             script)
1636
      return False, ("Script '%s' under path '%s' is not executable" %
1637
                     (script, os_dir))
1637 1638

  
1638 1639
    if not stat.S_ISREG(stat.S_IFMT(st.st_mode)):
1639
      raise errors.InvalidOS(name, os_dir, "'%s' is not a regular file" %
1640
                             script)
1640
      return False, ("Script '%s' under path '%s' is not a regular file" %
1641
                     (script, os_dir))
1642

  
1643
  os_obj = objects.OS(name=name, path=os_dir, status=constants.OS_VALID_STATUS,
1644
                      create_script=os_scripts[constants.OS_SCRIPT_CREATE],
1645
                      export_script=os_scripts[constants.OS_SCRIPT_EXPORT],
1646
                      import_script=os_scripts[constants.OS_SCRIPT_IMPORT],
1647
                      rename_script=os_scripts[constants.OS_SCRIPT_RENAME],
1648
                      api_versions=api_versions)
1649
  return True, os_obj
1650

  
1651

  
1652
def OSFromDisk(name, base_dir=None):
1653
  """Create an OS instance from disk.
1654

  
1655
  This function will return an OS instance if the given name is a
1656
  valid OS name. Otherwise, it will raise an appropriate
1657
  L{RPCFail} exception, detailing why this is not a valid OS.
1658

  
1659
  This is just a wrapper over L{_TryOSFromDisk}, which doesn't raise
1660
  an exception but returns true/false status data.
1661

  
1662
  @type base_dir: string
1663
  @keyword base_dir: Base directory containing OS installations.
1664
                     Defaults to a search in all the OS_SEARCH_PATH dirs.
1665
  @rtype: L{objects.OS}
1666
  @return: the OS instance if we find a valid one
1667
  @raise RPCFail: if we don't find a valid OS
1668

  
1669
  """
1670
  status, payload = _TryOSFromDisk(name, base_dir)
1671

  
1672
  if not status:
1673
    _Fail(payload)
1641 1674

  
1675
  return payload
1642 1676

  
1643
  return objects.OS(name=name, path=os_dir, status=constants.OS_VALID_STATUS,
1644
                    create_script=os_scripts[constants.OS_SCRIPT_CREATE],
1645
                    export_script=os_scripts[constants.OS_SCRIPT_EXPORT],
1646
                    import_script=os_scripts[constants.OS_SCRIPT_IMPORT],
1647
                    rename_script=os_scripts[constants.OS_SCRIPT_RENAME],
1648
                    api_versions=api_versions)
1649 1677

  
1650 1678
def OSEnvironment(instance, debug=0):
1651 1679
  """Calculate the environment for an os script.
b/lib/cmdlib.py
1801 1801

  
1802 1802
    @rtype: dict
1803 1803
    @return: a dictionary with osnames as keys and as value another map, with
1804
        nodes as keys and list of OS objects as values, eg::
1804
        nodes as keys and tuples of (path, status, diagnose) as values, eg::
1805 1805

  
1806
          {"debian-etch": {"node1": [<object>,...],
1807
                           "node2": [<object>,]}
1806
          {"debian-etch": {"node1": [(/usr/lib/..., True, ""),
1807
                                     (/srv/..., False, "invalid api")],
1808
                           "node2": [(/srv/..., True, "")]}
1808 1809
          }
1809 1810

  
1810 1811
    """
......
1817 1818
    for node_name, nr in rlist.items():
1818 1819
      if nr.RemoteFailMsg() or not nr.payload:
1819 1820
        continue
1820
      for os_serialized in nr.payload:
1821
        os_obj = objects.OS.FromDict(os_serialized)
1822
        if os_obj.name not in all_os:
1821
      for name, path, status, diagnose in nr.payload:
1822
        if name not in all_os:
1823 1823
          # build a list of nodes for this os containing empty lists
1824 1824
          # for each node in node_list
1825
          all_os[os_obj.name] = {}
1825
          all_os[name] = {}
1826 1826
          for nname in good_nodes:
1827
            all_os[os_obj.name][nname] = []
1828
        all_os[os_obj.name][node_name].append(os_obj)
1827
            all_os[name][nname] = []
1828
        all_os[name][node_name].append((path, status, diagnose))
1829 1829
    return all_os
1830 1830

  
1831 1831
  def Exec(self, feedback_fn):
......
1842 1842
        if field == "name":
1843 1843
          val = os_name
1844 1844
        elif field == "valid":
1845
          val = utils.all([osl and osl[0] for osl in os_data.values()])
1845
          val = utils.all([osl and osl[0][1] for osl in os_data.values()])
1846 1846
        elif field == "node_status":
1847
          # this is just a copy of the dict
1847 1848
          val = {}
1848
          for node_name, nos_list in os_data.iteritems():
1849
            val[node_name] = [(v.status, v.path) for v in nos_list]
1849
          for node_name, nos_list in os_data.items():
1850
            val[node_name] = nos_list
1850 1851
        else:
1851 1852
          raise errors.ParameterError(field)
1852 1853
        row.append(val)
......
3156 3157
        raise errors.OpPrereqError("Primary node '%s' is unknown" %
3157 3158
                                   self.op.pnode)
3158 3159
      result = self.rpc.call_os_get(pnode.name, self.op.os_type)
3159
      result.Raise()
3160
      if not isinstance(result.data, objects.OS):
3160
      msg = result.RemoteFailMsg()
3161
      if msg:
3161 3162
        raise errors.OpPrereqError("OS '%s' not in supported OS list for"
3162
                                   " primary node"  % self.op.os_type)
3163
                                   " primary node %s: %s"  %
3164
                                   (self.op.os_type, pnode.pname, msg))
3163 3165

  
3164 3166
    self.instance = instance
3165 3167

  
......
4843 4845

  
4844 4846
    # os verification
4845 4847
    result = self.rpc.call_os_get(pnode.name, self.op.os_type)
4846
    result.Raise()
4847
    if not isinstance(result.data, objects.OS):
4848
    msg = result.RemoteFailMsg()
4849
    if msg:
4848 4850
      raise errors.OpPrereqError("OS '%s' not in supported os list for"
4849
                                 " primary node"  % self.op.os_type)
4851
                                 " primary node %s: %s"  %
4852
                                 (self.op.os_type, pnode.name, msg))
4850 4853

  
4851 4854
    _CheckNicsBridgesExist(self, self.nics, self.pnode.name)
4852 4855

  
b/scripts/gnt-os
64 64
  return 0
65 65

  
66 66

  
67
def _OsStatus(status, diagnose):
68
  """Beautifier function for OS status.
69

  
70
  @type status: boolean
71
  @param status: is the OS valid
72
  @type diagnose: string
73
  @param diagnose: the error message for invalid OSes
74
  @rtype: string
75
  @return: a formatted status
76

  
77
  """
78
  if status:
79
    return "valid"
80
  else:
81
    return "invalid - %s" % diagnose
82

  
67 83
def DiagnoseOS(opts, args):
68 84
  """Analyse all OSes on this cluster.
69 85

  
......
91 107
    for node_name, node_info in node_data.iteritems():
92 108
      nodes_hidden[node_name] = []
93 109
      if node_info: # at least one entry in the per-node list
94
        first_os_status, first_os_path = node_info.pop(0)
95
        first_os_msg = ("%s (path: %s)" %
96
                        (first_os_status, first_os_path))
97
        if first_os_status == constants.OS_VALID_STATUS:
110
        first_os_path, first_os_status, first_os_msg = node_info.pop(0)
111
        first_os_msg = ("%s (path: %s)" % (_OsStatus(first_os_status,
112
                                                     first_os_msg),
113
                                           first_os_path))
114
        if first_os_status:
98 115
          nodes_valid[node_name] = first_os_msg
99 116
        else:
100 117
          nodes_bad[node_name] = first_os_msg
101
        for hstatus, hpath in node_info:
118
        for hpath, hstatus, hmsg in node_info:
102 119
          nodes_hidden[node_name].append("    [hidden] path: %s, status: %s" %
103
                                         (hpath, hstatus))
120
                                         (hpath, _OsStatus(hstatus, hmsg)))
104 121
      else:
105 122
        nodes_bad[node_name] = "OS not found"
106 123

  

Also available in: Unified diff