Print this page
305 http_proxy value needs more checking for valid url syntax
2341 Client should be more conservative about closing sockets
4235 Misleading "node name" unknown messages when http_proxy set incorrectly
4495 Want ability to cancel individual file downloads
8902 Need a transport that downloads files by GET
9508 Captive portal test only run during catalog refresh
9613 Implicit refresh should raise InvalidDepotResponseException
9615 EOL clientside search v0
9629 EOL clientside support for filelist
9630 Hostile depot should live with other depot code
9631 HTTPS transport should be more rigorous in verification
9670 More specific error exceptions requested from search
9686 network operations should use accept-encoding when appropriate
9715 The info() operation should use the activity_lock

Split Close
Expand all
Collapse all
          --- old/src/client.py
          +++ new/src/client.py
↓ open down ↓ 67 lines elided ↑ open up ↑
  68   68  import pkg.client.progress as progress
  69   69  import pkg.client.publisher as publisher
  70   70  import pkg.fmri as fmri
  71   71  import pkg.misc as misc
  72   72  
  73   73  from pkg.client import global_settings
  74   74  from pkg.client.debugvalues import DebugValues
  75   75  from pkg.client.history import (RESULT_CANCELED, RESULT_FAILED_BAD_REQUEST,
  76   76      RESULT_FAILED_CONFIGURATION, RESULT_FAILED_TRANSPORT, RESULT_FAILED_UNKNOWN,
  77   77      RESULT_FAILED_OUTOFMEMORY)
  78      -from pkg.client.filelist import FileListRetrievalError
  79      -from pkg.client.retrieve import (CatalogRetrievalError,
  80      -    DatastreamRetrievalError, ManifestRetrievalError)
  81   78  from pkg.misc import EmptyI, msg, emsg, PipeError
  82   79  
  83   80  CLIENT_API_VERSION = 15
  84   81  PKG_CLIENT_NAME = "pkg"
  85   82  
  86   83  def error(text, cmd=None):
  87   84          """Emit an error message prefixed by the command name """
  88   85  
  89   86          if cmd:
  90   87                  text = "%s: %s" % (cmd, text)
↓ open down ↓ 476 lines elided ↑ open up ↑
 567  564                          if not noexecute:
 568  565                                  return 1
 569  566                  else:
 570  567                          raise RuntimeError("Catalog refresh failed during"
 571  568                              " image-update.")
 572  569          except api_errors.BEException, e:
 573  570                  error(_(e))
 574  571                  return 1
 575  572          except (api_errors.CertificateError,
 576  573              api_errors.PlanCreationException,
 577      -            api_errors.NetworkUnavailableException,
 578  574              api_errors.PermissionsException), e:
 579  575                  # Prepend a newline because otherwise the exception will
 580  576                  # be printed on the same line as the spinner.
 581  577                  error("\n" + str(e))
 582  578                  return 1
 583  579          except api_errors.IpkgOutOfDateException:
 584  580                  msg(_("WARNING: pkg(5) appears to be out of date, and should " \
 585  581                      "be updated before\nrunning image-update.\n"))
 586  582                  msg(_("Please update pkg(5) using 'pfexec pkg install " \
 587  583                      "SUNWipkg' and then retry\nthe image-update."))
↓ open down ↓ 4 lines elided ↑ open up ↑
 592  588          if noexecute:
 593  589                  return 0
 594  590  
 595  591          ret_code = 0
 596  592  
 597  593          # Exceptions which happen here are printed in the above level, with
 598  594          # or without some extra decoration done here.
 599  595          # XXX would be nice to kick the progress tracker.
 600  596          try:
 601  597                  api_inst.prepare()
 602      -        except misc.TransportException:
      598 +        except api_errors.TransportError, e:
 603  599                  # move past the progress tracker line.
 604  600                  msg("\n")
 605      -                raise
      601 +                if verbose:
      602 +                        e.verbose = True
      603 +                raise e
 606  604          except KeyboardInterrupt:
 607  605                  raise
 608  606          except api_errors.PermissionsException, e:
 609  607                  # Prepend a newline because otherwise the exception will
 610  608                  # be printed on the same line as the spinner.
 611  609                  error("\n" + str(e))
 612  610                  return 1
 613  611          except:
 614  612                  error(_("\nAn unexpected error happened while preparing for " \
 615  613                      "image-update:"))
↓ open down ↓ 113 lines elided ↑ open up ↑
 729  727          except api_errors.CatalogRefreshException, e:
 730  728                  if display_catalog_failures(e) == 0:
 731  729                          if not noexecute:
 732  730                                  return 1
 733  731                  else:
 734  732                          error(_("Catalog refresh failed during install."),
 735  733                              cmd="install")
 736  734                          return 1
 737  735          except (api_errors.CertificateError,
 738  736              api_errors.PlanCreationException,
 739      -            api_errors.NetworkUnavailableException,
 740  737              api_errors.PermissionsException), e:
 741  738                  # Prepend a newline because otherwise the exception will
 742  739                  # be printed on the same line as the spinner.
 743  740                  error("\n" + str(e), cmd="install")
 744  741                  return 1
 745  742          except api_errors.InventoryException, e:
 746  743                  error(_("install failed (inventory exception):\n%s") % e,
 747  744                      cmd="install")
 748  745                  return 1
 749  746          except fmri.IllegalFmri, e:
↓ open down ↓ 1 lines elided ↑ open up ↑
 751  748                  return 1
 752  749  
 753  750          if noexecute:
 754  751                  return 0
 755  752  
 756  753          # Exceptions which happen here are printed in the above level, with
 757  754          # or without some extra decoration done here.
 758  755          # XXX would be nice to kick the progress tracker.
 759  756          try:
 760  757                  api_inst.prepare()
 761      -        except misc.TransportException:
      758 +        except api_errors.TransportError, e:
 762  759                  # move past the progress tracker line.
 763  760                  msg("\n")
 764      -                raise
      761 +                if verbose:
      762 +                        e.verbose = True
      763 +                raise e
 765  764          except KeyboardInterrupt:
 766  765                  raise
 767  766          except api_errors.PermissionsException, e:
 768  767                  # Prepend a newline because otherwise the exception will
 769  768                  # be printed on the same line as the spinner.
 770  769                  error("\n" + str(e))
 771  770                  return 1
 772  771          except:
 773  772                  error(_("\nAn unexpected error happened while preparing for " \
 774  773                      "install:"))
↓ open down ↓ 97 lines elided ↑ open up ↑
 872  871                  return 1
 873  872  
 874  873          if noexecute:
 875  874                  return 0
 876  875  
 877  876          # Exceptions which happen here are printed in the above level, with
 878  877          # or without some extra decoration done here.
 879  878          # XXX would be nice to kick the progress tracker.
 880  879          try:
 881  880                  api_inst.prepare()
 882      -        except misc.TransportException:
      881 +        except api_errors.TransportError, e:
 883  882                  # move past the progress tracker line.
 884  883                  msg("\n")
 885      -                raise
      884 +                if verbose:
      885 +                        e.verbose = True
      886 +                raise e
 886  887          except api_errors.FileInUseException, e:
 887  888                  error("\n" + str(e))
 888  889                  return 1
 889  890          except KeyboardInterrupt:
 890  891                  raise
 891  892          except:
 892  893                  error(_("\nAn unexpected error happened while preparing for " \
 893  894                      "install:"))
 894  895                  raise
 895  896  
↓ open down ↓ 30 lines elided ↑ open up ↑
 926  927  def freeze(img, args):
 927  928          """Attempt to take package specified to FROZEN state, with given
 928  929          restrictions.  Package must have been in the INSTALLED state."""
 929  930          return 0
 930  931  
 931  932  def unfreeze(img, args):
 932  933          """Attempt to return package specified to INSTALLED state from FROZEN
 933  934          state."""
 934  935          return 0
 935  936  
 936      -def process_v_0_search(tup, first):
 937      -        """Transforms the tuples returned by search v1 into the four column
 938      -        output format.
 939      -
 940      -        The "tup" parameter is a four tuple with the each entry corresponding
 941      -        to a column of the output.
 942      -        
 943      -        The "first" parameter is a boolean stating whether this is the first
 944      -        time this function has been called.  This controls the printing of the
 945      -        header information."""
 946      -
 947      -        try:
 948      -                index, mfmri, action, value = tup
 949      -        except ValueError:
 950      -                error(_("The server returned a malformed result.\n"
 951      -                    "The problematic structure: %r") % (tup,))
 952      -                return False
 953      -        if first:
 954      -                msg("%-10s %-9s %-25s %s" %
 955      -                    ("INDEX", "ACTION", "VALUE", "PACKAGE"))
 956      -        msg("%-10s %-9s %-25s %s" % (index, action, value,
 957      -            fmri.PkgFmri(str(mfmri)).get_short_fmri()))
 958      -        return True
 959      -
 960  937  def __convert_output(a_str, match):
 961  938          """Converts a string to a three tuple with the information to fill
 962  939          the INDEX, ACTION, and VALUE columns.
 963  940  
 964  941          The "a_str" parameter is the string representation of an action.
 965  942          
 966  943          The "match" parameter is a string whose precise interpretation is given
 967  944          below.
 968  945  
 969  946          For most action types, match defines which attribute the query matched
↓ open down ↓ 43 lines elided ↑ open up ↑
1013  990                              e)
1014  991                          return False
1015  992                  msg("%-10s %-9s %-25s %s" %
1016  993                      (out1, out2, out3,
1017  994                      fmri.PkgFmri(str(pfmri)).get_short_fmri()))
1018  995          else:
1019  996                  pfmri = tup
1020  997                  if first:
1021  998                          msg("%s" % ("PACKAGE"))
1022  999                  pub_name = ''
1023      -                if pub is not None and "prefix" in pub:
     1000 +                # If pub is not None, it's either a RepositoryURI or a Publisher
     1001 +                # object.  If it's a Publisher, it has a prefix.  Otherwise,
     1002 +                # use the uri.
     1003 +                if pub is not None and hasattr(pub, "prefix"):
1024 1004                          pub_name = " (%s)" % pub.prefix
     1005 +                elif pub is not None and hasattr(pub, "uri"):
     1006 +                        pub_name = " (%s)" % pub.uri
1025 1007                  msg("%s%s" %
1026 1008                      (fmri.PkgFmri(str(pfmri)).get_short_fmri(), pub_name))
1027 1009          return True
1028 1010  
1029 1011  def search(img_dir, args):
1030 1012          """Search for the given query."""
1031 1013  
1032 1014          opts, pargs = getopt.getopt(args, "alprs:I")
1033 1015  
1034 1016          local = remote = case_sensitive = False
↓ open down ↓ 58 lines elided ↑ open up ↑
1093 1075  
1094 1076                  for raw_value in itertools.chain(*searches):
1095 1077                          try:
1096 1078                                  query_num, pub, (v, return_type, tmp) = \
1097 1079                                      raw_value
1098 1080                          except ValueError, e:
1099 1081                                  error(_("The server returned a malformed "
1100 1082                                      "result:%r") % (raw_value,))
1101 1083                                  bad_res = True
1102 1084                                  continue
1103      -                        if v == 0:
1104      -                                ret = process_v_0_search(tmp, first)
1105      -                        else:
1106      -                                ret = process_v_1_search(tmp, first,
1107      -                                    return_type, pub)
     1085 +                        ret = process_v_1_search(tmp, first,
     1086 +                            return_type, pub)
1108 1087                          good_res |= ret
1109 1088                          bad_res |= not ret
1110 1089                          first = False
1111 1090          except (api_errors.IncorrectIndexFileHash,
1112 1091              api_errors.InconsistentIndexException):
1113 1092                  error(_("The search index appears corrupted.  Please "
1114 1093                      "rebuild the index with 'pkg rebuild-index'."))
1115 1094                  return 1
1116 1095          except api_errors.ProblematicSearchServers, e:
1117 1096                  error(e)
↓ open down ↓ 488 lines elided ↑ open up ↑
1606 1585                                      (urlparse.urlsplit(
1607 1586                                          pub["origin"])[1].split(":")[0],
1608 1587                                      err.args[0][1]))
1609 1588                          else:
1610 1589                                  if isinstance(err.args[0], socket.timeout):
1611 1590                                          emsg("    %s: %s" % \
1612 1591                                              (pub["origin"], "timeout"))
1613 1592                                  else:
1614 1593                                          emsg("    %s: %s" % \
1615 1594                                              (pub["origin"], err.args[0][1]))
1616      -                elif isinstance(err, CatalogRetrievalError) and \
1617      -                    isinstance(err.exc, EnvironmentError) and \
1618      -                    err.exc.errno == errno.EACCES:
1619      -                        if err.prefix:
1620      -                                emsg("   ", _("Could not update catalog "
1621      -                                     "for '%s' due to insufficient "
1622      -                                     "permissions.") % err.prefix)
1623      -                        else:
1624      -                                emsg("   ", _("Could not update a catalog "
1625      -                                     "due to insufficient permissions."))
1626      -
1627      -                        emsg("   ", _("Please try the command again "
1628      -                        "using pfexec, or otherwise increase \n    your "
1629      -                        "permissions."))
1630 1595                  else:
1631 1596                          emsg("   ", err)
1632 1597  
1633 1598          if cre.message:
1634 1599                  emsg(cre.message)
1635 1600  
1636 1601          return succeeded
1637 1602  
1638 1603  def publisher_refresh(img_dir, args):
1639 1604          """Update metadata for the image's publishers."""
↓ open down ↓ 14 lines elided ↑ open up ↑
1654 1619  
1655 1620          try:
1656 1621                  # The user explicitly requested this refresh, so set the
1657 1622                  # refresh to occur immediately.
1658 1623                  api_inst.refresh(full_refresh=full_refresh, immediate=True,
1659 1624                      pubs=pargs)
1660 1625          except api_errors.PublisherError, e:
1661 1626                  error(e)
1662 1627                  error(_("'pkg publisher' will show a list of publishers."))
1663 1628                  return 1
1664      -        except (api_errors.PermissionsException,
1665      -            api_errors.NetworkUnavailableException), e:
     1629 +        except (api_errors.PermissionsException), e:
1666 1630                  # Prepend a newline because otherwise the exception will
1667 1631                  # be printed on the same line as the spinner.
1668 1632                  error("\n" + str(e))
1669 1633                  return 1
1670 1634          except api_errors.CatalogRefreshException, e:
1671 1635                  if display_catalog_failures(e) == 0:
1672 1636                          return 1
1673 1637                  else:
1674 1638                          return 3
1675 1639          else:
↓ open down ↓ 606 lines elided ↑ open up ↑
2282 2246                  return 1
2283 2247          except api_errors.InvalidDepotResponseException, e:
2284 2248                  # Ensure messages are displayed after the spinner.
2285 2249                  emsg("\n")
2286 2250                  error(_("The URI '%(pub_url)s' does not appear to point to a "
2287 2251                      "valid pkg server.\nPlease check the server's "
2288 2252                      "address and client's network configuration."
2289 2253                      "\nAdditional details:\n\n%(error)s") %
2290 2254                      { "pub_url": pub_url, "error": e },
2291 2255                      cmd="image-create")
     2256 +                print_proxy_config()
2292 2257                  return 1
2293 2258          except api_errors.CatalogRefreshException, cre:
2294 2259                  # Ensure messages are displayed after the spinner.
2295 2260                  error("", cmd="image-create")
2296 2261                  if display_catalog_failures(cre) == 0:
2297 2262                          return 1
2298 2263                  else:
2299 2264                          return 3
2300 2265          return 0
2301 2266  
↓ open down ↓ 119 lines elided ↑ open up ↑
2421 2386                                  msg("%15s: %s" % (_(field), value))
2422 2387  
2423 2388                          # Separate log entries with a blank line.
2424 2389                          msg("")
2425 2390                  else:
2426 2391                          msg("%-19s %-25s %-15s %s" % (start_time,
2427 2392                              he.operation_name, he.client_name, outcome))
2428 2393  
2429 2394          return 0
2430 2395  
     2396 +def print_proxy_config():
     2397 +        """If the user has configured http_proxy or https_proxy in the
     2398 +        environment, print out the values.  Some transport errors are
     2399 +        not debuggable without this information handy."""
     2400 +
     2401 +        http_proxy = os.environ.get("http_proxy", None)
     2402 +        https_proxy = os.environ.get("https_proxy", None)
     2403 +
     2404 +        if not http_proxy and not https_proxy:
     2405 +                return
     2406 +
     2407 +        emsg(_("\nThe following proxy configuration is set in the"
     2408 +            " environment:\n"))
     2409 +        if http_proxy:
     2410 +                emsg(_("http_proxy: %s\n") % http_proxy)
     2411 +        if https_proxy:
     2412 +                emsg(_("https_proxy: %s\n") % https_proxy)
     2413 +
     2414 +
2431 2415  # To allow exception handler access to the image.
2432 2416  __img = None
2433 2417  
2434 2418  def main_func():
2435 2419          global_settings.client_name = PKG_CLIENT_NAME
2436 2420  
2437 2421          global __img
2438 2422          __img = img = image.Image()
2439 2423  
2440 2424          misc.setlocale(locale.LC_ALL, "", error)
↓ open down ↓ 24 lines elided ↑ open up ↑
2465 2449          if pargs:
2466 2450                  subcommand = pargs.pop(0)
2467 2451                  if subcommand == "help":
2468 2452                          show_usage = True
2469 2453  
2470 2454          if show_usage:
2471 2455                  usage(retcode=0)
2472 2456          elif not subcommand:
2473 2457                  usage()
2474 2458  
2475      -        socket.setdefaulttimeout(
2476      -            int(os.environ.get("PKG_CLIENT_TIMEOUT", "30"))) # in seconds
2477      -
2478      -        # Override default PKG_TIMEOUT_MAX if a value has been specified
2479      -        # in the environment.
     2459 +        # Override default PKG_TIMEOUT_MAX and PKG_CLIENT_TIMEOUT 
     2460 +        # if a value has been specified in the environment.
2480 2461          global_settings.PKG_TIMEOUT_MAX = int(os.environ.get("PKG_TIMEOUT_MAX",
2481 2462              global_settings.PKG_TIMEOUT_MAX))
2482 2463  
     2464 +        global_settings.PKG_CLIENT_TIMEOUT = int(os.environ.get(
     2465 +            "PKG_CLIENT_TIMEOUT", global_settings.PKG_CLIENT_TIMEOUT))
     2466 +
     2467 +        # This call only affects sockets created by Python.  The transport
     2468 +        # framework must set the timeout value internally.
     2469 +        socket.setdefaulttimeout(global_settings.PKG_TIMEOUT_MAX) # in seconds
     2470 +
2483 2471          if subcommand == "image-create":
2484 2472                  if "mydir" in locals():
2485 2473                          usage(_("-R not allowed for %s subcommand") %
2486 2474                                subcommand)
2487 2475                  try:
2488 2476                          ret = image_create(img, pargs)
2489 2477                  except getopt.GetoptError, e:
2490 2478                          usage(_("illegal %s option -- %s") % \
2491 2479                              (subcommand, e.opt))
2492 2480                  return ret
↓ open down ↓ 137 lines elided ↑ open up ↑
2630 2618          except api_errors.CertificateError, __e:
2631 2619                  if __img:
2632 2620                          __img.history.abort(RESULT_FAILED_CONFIGURATION)
2633 2621                  error(__e)
2634 2622                  __ret = 1
2635 2623          except api_errors.PublisherError, __e:
2636 2624                  if __img:
2637 2625                          __img.history.abort(RESULT_FAILED_BAD_REQUEST)
2638 2626                  error(__e)
2639 2627                  __ret = 1
2640      -        except misc.TransportException, __e:
     2628 +        except api_errors.TransportError, __e:
2641 2629                  if __img:
2642 2630                          __img.history.abort(RESULT_FAILED_TRANSPORT)
2643      -                error(_("\nMaximum number of network retries exceeded during "
2644      -                    "download. Details follow:\n%s") % __e)
     2631 +                emsg(_("\nErrors were encountered while attempting to retrieve"
     2632 +                    " package or file data for\nthe requested operation."))
     2633 +                emsg(_("Details follow:\n\n%s") % __e)
     2634 +                print_proxy_config()
2645 2635                  __ret = 1
2646      -        except (ManifestRetrievalError,
2647      -            DatastreamRetrievalError, FileListRetrievalError), __e:
2648      -                if __img:
2649      -                        __img.history.abort(RESULT_FAILED_TRANSPORT)
2650      -                error(_("An error was encountered while attempting to retrieve"
2651      -                    " package or file data for the requested operation."))
2652      -                error(__e)
2653      -                __ret = 1
2654 2636          except api_errors.InvalidDepotResponseException, __e:
2655 2637                  if __img:
2656 2638                          __img.history.abort(RESULT_FAILED_TRANSPORT)
2657      -                error(_("\nUnable to contact a valid package depot. "
     2639 +                emsg(_("\nUnable to contact a valid package depot. "
2658 2640                      "This may be due to a problem with the server, "
2659 2641                      "network misconfiguration, or an incorrect pkg client "
2660 2642                      "configuration.  Please check your network settings and "
2661 2643                      "attempt to contact the server using a web browser."))
2662      -                error(_("\nAdditional details:\n\n%s") % __e)
     2644 +                emsg(_("\nAdditional details:\n\n%s") % __e)
     2645 +                print_proxy_config()
2663 2646                  __ret = 1 
2664 2647          except history.HistoryLoadException, __e:
2665 2648                  # Since a history related error occurred, discard all
2666 2649                  # information about the current operation(s) in progress.
2667 2650                  if __img:
2668 2651                          __img.history.clear()
2669 2652                  error(_("An error was encountered while attempting to load "
2670 2653                      "history information\nabout past client operations."))
2671 2654                  error(__e)
2672 2655                  __ret = 1
↓ open down ↓ 42 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX