[PATCH] update proxy support

Brian C. Lane bcl at redhat.com
Fri Jun 29 23:23:11 UTC 2012


From: "Brian C. Lane" <bcl at redhat.com>

cmdline proxy strings use the standard form:
[protocol://][username[:password]@]host[:port]

proxy url strings are stored in the ksdata, either in method.proxy for
the global proxy or per repo in repo.proxy

ProxyString handles parsing the proxy url and returning it in various
forms useful for urlgrabber, yum and the UI.

--proxyAuth has been removed. It has no meaning with noloader.
Also fix a typo with sslverify in the repo configuration file.
---
 anaconda                           |   16 ++----
 pyanaconda/iutil.py                |  104 ++++++++++++++++++++++++++++++++++++
 pyanaconda/packaging/__init__.py   |   15 ++++--
 pyanaconda/packaging/yumpayload.py |   54 ++++++++++++++-----
 pyanaconda/ui/gui/spokes/source.py |   61 +++++++++++----------
 5 files changed, 191 insertions(+), 59 deletions(-)

diff --git a/anaconda b/anaconda
index c613d4e..b324539 100755
--- a/anaconda
+++ b/anaconda
@@ -233,7 +233,6 @@ def parseOptions(argv=None, cmdline=None):
     op.add_option("--noipv4", action="store_true", default=False)
     op.add_option("--noipv6", action="store_true", default=False)
     op.add_option("--proxy")
-    op.add_option("--proxyAuth")
 
     # Method of operation
     op.add_option("--autostep", action="store_true", default=False)
@@ -781,15 +780,6 @@ if __name__ == "__main__":
     if opts.proxy:
         anaconda.proxy = opts.proxy
 
-        if opts.proxyAuth:
-            filename = opts.proxyAuth
-            ret = open(filename, "r").readlines()
-            os.unlink(filename)
-
-            anaconda.proxyUsername = ret[0].rstrip()
-            if len(ret) == 2:
-                anaconda.proxyPassword = ret[1].rstrip()
-
     if opts.updateSrc:
         anaconda.updateSrc = opts.updateSrc
 
@@ -873,6 +863,10 @@ if __name__ == "__main__":
     # scripts.  Add those to the ksdata now.
     kickstart.appendPostScripts(ksdata)
 
+    # cmdline flags override kickstart settings
+    ksdata.method.proxy = anaconda.proxy
+    ksdata.method.noverifyssl = flags.noverifyssl
+
     # set ksdata.method based on anaconda.method if it isn't already set
     if anaconda.methodstr and not ksdata.method.method:
         if anaconda.methodstr.startswith("cdrom"):
@@ -901,8 +895,6 @@ if __name__ == "__main__":
         elif anaconda.methodstr.startswith("http") or \
              anaconda.methodstr.startswith("ftp"):
             ksdata.method.method = "url"
-            ksdata.method.noverifyssl = flags.noverifyssl
-            ksdata.method.proxy = anaconda.proxy    # FIXME: username/password
             ksdata.method.url = anaconda.methodstr
 
     # setup keyboard layout from the command line option and let
diff --git a/pyanaconda/iutil.py b/pyanaconda/iutil.py
index 398ef77..1228946 100644
--- a/pyanaconda/iutil.py
+++ b/pyanaconda/iutil.py
@@ -28,6 +28,7 @@ import os.path
 import errno
 import subprocess
 import threading
+import re
 
 from flags import flags
 from constants import *
@@ -1081,3 +1082,106 @@ def dracut_eject(device):
     except Exception, e:
         log.error("Error writing dracut shutdown eject hook for %s: %s" % (device, e))
 
+class ProxyStringError(Exception):
+    pass
+
+class ProxyString(object):
+    """ Handle a proxy url
+    """
+    def __init__(self, url=None, protocol="http://", host=None, port="3128",
+                 username=None, password=None):
+        """ Initialize with either url
+        ([protocol://][username[:password]@]host[:port]) or pass host and
+        optionally:
+
+        protocol    http, https, ftp
+        host        hostname without protocol
+        port        port number (defaults to 3128)
+        username    username
+        password    password
+
+        The str() of the object is the full proxy url
+
+        ProxyString.url is the full url including username:password@
+        ProxyString.noauth_url is the url without username:password@
+        """
+        self.url = url
+        self.protocol = protocol
+        self.host = host
+        self.port = str(port)
+        self.username = username
+        self.password = password
+        self.proxy_auth = ""
+        self.noauth_url = None
+
+        if url:
+            self.parse_url()
+        elif not host:
+            raise ProxyStringError("No host url")
+        else:
+            self.parse_components()
+
+    def parse_url(self):
+        """ Parse the proxy url into its component pieces
+        """
+        # NOTE: If this changes, update tests/regex/proxy.py
+        #
+        # proxy=[protocol://][username[:password]@]host[:port][path]
+        # groups
+        # 1 = protocol
+        # 2 = username:password@
+        # 3 = username
+        # 4 = password
+        # 5 = hostname
+        # 6 = port
+        # 7 = extra
+        pattern = re.compile("([A-Za-z]+://)?(([A-Za-z0-9]+)(:[^:@]+)?@)?([^:/]+)(:[0-9]+)?(/.*)?")
+        m = pattern.match(self.url)
+        if not m:
+            raise ProxyStringError("malformed url, cannot parse it.")
+
+        # If no protocol was given default to http.
+        if m.group(1):
+            self.protocol = m.group(1)
+        else:
+            self.protocol = "http://"
+
+        if m.group(3):
+            self.username = m.group(3)
+
+        if m.group(4):
+            # Skip the leading colon
+            self.password = m.group(4)[1:]
+
+        if m.group(5):
+            self.host = m.group(5)
+            if m.group(6):
+                # Skip the leading colon
+                self.port = m.group(6)[1:]
+        else:
+            raise ProxyStringError("url has no host component")
+
+        self.parse_components()
+
+    def parse_components(self):
+        """ Parse the components of a proxy url into url and noauth_url
+        """
+        if self.username or self.password:
+            self.proxy_auth = "%s:%s@" % (self.username or "",
+                                          self.password or "")
+
+        self.url = self.protocol + self.proxy_auth + self.host + ":" + self.port
+        self.noauth_url = self.protocol + self.host + ":" + self.port
+
+    @property
+    def dict(self):
+        """ return a dict of all the elements of the proxy string
+        url, noauth_url, protocol, host, port, username, password
+        """
+        components = ["url", "noauth_url", "protocol", "host", "port",
+                      "username", "password"]
+        return dict([(k, getattr(self, k)) for k in components]) 
+
+    def __str__(self):
+        return self.url
+
diff --git a/pyanaconda/packaging/__init__.py b/pyanaconda/packaging/__init__.py
index 8b56268..ca23f53 100644
--- a/pyanaconda/packaging/__init__.py
+++ b/pyanaconda/packaging/__init__.py
@@ -43,6 +43,7 @@ from pyanaconda.constants import *
 from pyanaconda.flags import flags
 
 from pyanaconda import iutil
+from pyanaconda.iutil import ProxyString, ProxyStringError
 
 from pykickstart.parser import Group
 
@@ -118,8 +119,9 @@ def get_mount_device(mountpoint):
 class Payload(object):
     """ Payload is an abstract class for OS install delivery methods. """
     def __init__(self, data):
+        """ data is a kickstart.AnacondaKSHandler class
+        """
         self.data = data
-        self.proxy = None
         self._kernelVersionList = []
 
     def setup(self, storage):
@@ -378,9 +380,14 @@ class Payload(object):
                                                                     version))
 
         proxies = {}
-        if self.proxy:
-            proxies = {"http": self.proxy,
-                       "https": self.proxy}
+        if self.data.method.proxy:
+            try:
+                proxy = ProxyString(self.data.method.proxy)
+                proxies = {"http": proxy.url,
+                           "https": proxy.url}
+            except ProxyStringError as e:
+                log.info("Failed to parse proxy for _getReleaseVersion %s: %s" \
+                         % (self.data.method.proxy, e))
 
         treeinfo = self._getTreeInfo(url, not flags.noverifyssl, proxies)
         if treeinfo:
diff --git a/pyanaconda/packaging/yumpayload.py b/pyanaconda/packaging/yumpayload.py
index 1482125..9ab8d32 100644
--- a/pyanaconda/packaging/yumpayload.py
+++ b/pyanaconda/packaging/yumpayload.py
@@ -61,6 +61,7 @@ from pyanaconda.constants import *
 from pyanaconda.flags import flags
 
 from pyanaconda import iutil
+from pyanaconda.iutil import ProxyString, ProxyStringError
 from pyanaconda import isys
 from pyanaconda.network import hasActiveNetDev
 
@@ -100,7 +101,6 @@ class YumPayload(PackagePayload):
         PackagePayload.__init__(self, data)
 
         self.install_device = None
-        self.proxy = None                           # global proxy
         self._root_dir = "/tmp/yum.root"
         self._repos_dir = "/etc/yum.repos.d,/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anaconda.repos.d"
         self._yum = None
@@ -142,8 +142,7 @@ class YumPayload(PackagePayload):
 
         self._resetYum(root=root)
 
-    def setup(self, storage, proxy=None):
-        self.proxy = proxy
+    def setup(self, storage):
         self._writeYumConfig()
         self._setup = True
 
@@ -201,9 +200,17 @@ reposdir=%s
         if flags.noverifyssl:
             buf += "sslverify=0\n"
 
-        if self.proxy:
-            # FIXME: include proxy_username, proxy_password
-            buf += "proxy=%s\n" % proxy
+        if self.data.method.proxy:
+            try:
+                proxy = ProxyString(self.data.method.proxy)
+                buf += "proxy=%s\n" % (proxy.noauth_url,)
+                if proxy.username:
+                    buf += "proxy_username=%s\n" % (proxy.username,)
+                if proxy.password:
+                    buf += "proxy_password=%s\n" % (proxy.password,)
+            except ProxyStringError as e:
+                log.error("Failed to parse proxy for _writeYumConfig %s: %s" \
+                          % (self.data.method.proxy, e))
 
         open("/tmp/anaconda-yum.conf", "w").write(buf)
 
@@ -250,10 +257,19 @@ reposdir=%s
                 # kickstart repo modifiers
                 if ks_repo:
                     if ks_repo.noverifyssl:
-                        f.write("verifyssl=0\n")
+                        f.write("sslverify=0\n")
 
                     if ks_repo.proxy:
-                        f.write("proxy=%s\n" % ks_repo.proxy)
+                        try:
+                            proxy = ProxyString(ks_repo.proxy)
+                            f.write("proxy=%s\n" % (proxy.noauth_url,))
+                            if proxy.username:
+                                f.write("proxy_username=%s\n" % (proxy.username,))
+                            if proxy_password:
+                                f.write("proxy_password=%s\n" % (proxy.password,))
+                        except ProxyStringError as e:
+                            log.error("Failed to parse proxy for _writeInstallConfig %s: %s" \
+                                      % (self.data.method.proxy, e))
 
                     if ks_repo.cost:
                         f.write("cost=%d\n" % ks_repo.cost)
@@ -485,7 +501,6 @@ reposdir=%s
         method = self.data.method
         sslverify = True
         url = None
-        proxy = None
 
         if method.method == "harddrive":
             if method.biospart:
@@ -527,7 +542,6 @@ reposdir=%s
         elif method.method == "url":
             url = method.url
             sslverify = not (method.noverifyssl or flags.noverifyssl)
-            proxy = method.proxy or self.proxy
         elif method.method == "cdrom" or not method.method:
             # cdrom or no method specified -- check for media
             device = opticalInstallMedia(storage.devicetree)
@@ -545,7 +559,7 @@ reposdir=%s
             self._yumCacheDirHack()
             try:
                 self._addYumRepo(BASE_REPO_NAME, url,
-                                 proxy=proxy, sslverify=sslverify)
+                                 proxyurl=method.proxy, sslverify=sslverify)
             except MetadataError as e:
                 log.error("base repo (%s/%s) not valid -- removing it"
                           % (method.method, url))
@@ -565,13 +579,13 @@ reposdir=%s
         if self._repoNeedsNetwork(repo) and not hasActiveNetDev():
             raise NoNetworkError
 
-        proxy = repo.proxy or self.proxy
+        proxy = repo.proxy or self.data.method.proxy
         sslverify = not (flags.noverifyssl or repo.noverifyssl)
 
         # this repo is already in ksdata, so we only add it to yum here
         self._addYumRepo(repo.name, url, repo.mirrorlist, cost=repo.cost,
                          exclude=repo.excludepkgs, includepkgs=repo.includepkgs,
-                         proxy=proxy, sslverify=sslverify)
+                         proxyurl=proxy, sslverify=sslverify)
 
         # TODO: enable addons via treeinfo
 
@@ -599,7 +613,7 @@ reposdir=%s
             except RepoMDError:
                 log.error("failed to get groups for repo %s" % yumrepo.id)
 
-    def _addYumRepo(self, name, baseurl, mirrorlist=None, **kwargs):
+    def _addYumRepo(self, name, baseurl, mirrorlist=None, proxyurl=None, **kwargs):
         """ Add a yum repo to the YumBase instance. """
         from yum.Errors import RepoError
 
@@ -607,6 +621,18 @@ reposdir=%s
         if name in self._yum.repos.repos:
             self._yum.repos.delete(name)
 
+        if proxyurl:
+            try:
+                proxy = ProxyString(proxyurl)
+                kwargs["proxy"] = proxy.noauth_url
+                if proxy.username:
+                    kwargs["proxy_username"] = proxy.username
+                if proxy.password:
+                    kwargs["proxy_password"] = proxy.password
+            except ProxyStringError as e:
+                log.error("Failed to parse proxy for _addYumRepo %s: %s" \
+                          % (proxyurl, e))
+
         log.debug("adding yum repo %s with baseurl %s and mirrorlist %s"
                     % (name, baseurl, mirrorlist))
         with _yum_lock:
diff --git a/pyanaconda/ui/gui/spokes/source.py b/pyanaconda/ui/gui/spokes/source.py
index 9a87c03..7752439 100644
--- a/pyanaconda/ui/gui/spokes/source.py
+++ b/pyanaconda/ui/gui/spokes/source.py
@@ -24,6 +24,9 @@ import gettext
 _ = lambda x: gettext.ldgettext("anaconda", x)
 N_ = lambda x: x
 
+import logging
+log = logging.getLogger("anaconda")
+
 import os.path
 
 from gi.repository import AnacondaWidgets, GLib, Gtk
@@ -33,6 +36,7 @@ from pyanaconda.ui.gui import UIObject, communication
 from pyanaconda.ui.gui.spokes import NormalSpoke
 from pyanaconda.ui.gui.categories.software import SoftwareCategory
 from pyanaconda.ui.gui.utils import enlightbox, gdk_threaded
+from pyanaconda.iutil import ProxyString, ProxyStringError
 
 __all__ = ["SourceSpoke"]
 
@@ -57,15 +61,23 @@ class ProxyDialog(UIObject):
             self.window.destroy()
             return
 
-        proxy = self._proxyURLEntry.get_text()
+        url = self._proxyURLEntry.get_text()
+        if self._authCheck.get_active():
+            username = self._proxyPasswordEntry.get_text()
+            password = self._proxyPasswordEntry.get_text()
+        else:
+            username = None
+            password = None
 
-        if self._authCheck.get_active() and self._proxyUsernameEntry.get_text():
-            if self._proxyPasswordEntry.get_text():
-                proxy = self._proxyUsernameEntry.get_text() + ":" + self._proxyPasswordEntry.get_text() + "@" + proxy
-            else:
-                proxy = self._proxyUsernameEntry.get_text() + "@" + proxy
+        try:
+            proxy = ProxyString(url=url, username=username, password=password)
+            self.data.method.proxy = proxy.url
+        except ProxyStringError as e:
+            log.error("Failed to parse proxy for ProxyDialog Add - %s:%s@%s: %s" \
+                      % (username, password, url, e))
+            # TODO - tell the user they entered an invalid proxy and let them retry
+            self.data.method.proxy = ""
 
-        self.data.method.proxy = proxy
         self.window.destroy()
 
     def on_proxy_enable_toggled(self, button, *args):
@@ -95,28 +107,19 @@ class ProxyDialog(UIObject):
             self.on_proxy_auth_toggled(self._authCheck)
             return
 
-        # proxy=[protocol://][username[:password]@]host[:port][path]
-        pattern = re.compile("([A-Za-z]+://)?(([A-Za-z0-9]+)(:[^:@]+)?@)?([^:/]+)(:[0-9]+)?(/.*)?")
-        m = pattern.match(self.data.method.proxy)
-
-        if m and m.group(3):
-            self._proxyUsernameEntry.set_text(m.group(3))
-        if m and m.group(4):
-            # Skip the leading colon.
-            self._proxyPasswordEntry.set_text(m.group(4)[1:])
-        if m and m.group(5):
-            # If both a host and port was found, just paste them
-            # together using the colon at the beginning of the port
-            # match as a separator.  Otherwise, just use the host.
-            if m.group(6):
-                proxy = m.group(5) + m.group(6)
-            else:
-                proxy = m.group(5)
-
-            self._proxyURLEntry.set_text(proxy)
+        try:
+            proxy = ProxyString(self.data.method.proxy)
+            if proxy.username:
+                self._proxyUsernameEntry.set_text(proxy.username)
+            if proxy.password:
+                self._proxyPasswordEntry.set_text(proxy.password)
+            self._proxyURLEntry.set_text(proxy.noauth_url)
+        except ProxyStringError as e:
+            log.error("Failed to parse proxy for ProxyDialog.refresh %s: %s" % (self.data.method.proxy, e))
+            return
 
-        self._proxyCheck.set_active(self.data.method.proxy != "")
-        self._authCheck.set_active("@" in self.data.method.proxy)
+        self._proxyCheck.set_active(True)
+        self._authCheck.set_active(bool(proxy.username or proxy.password))
         self.on_proxy_enable_toggled(self._proxyCheck)
         self.on_proxy_auth_toggled(self._authCheck)
 
@@ -816,5 +819,5 @@ class SourceSpoke(NormalSpoke):
 
         # Only allow the proxy button to be clicked if a proxy makes sense for
         # the currently selected protocol.
-        proxyButton.set_sensitive(self._http_active())
+        proxyButton.set_sensitive(self._http_active() or self._mirror_active())
         nfsOptsBox.set_visible(self._nfs_active())
-- 
1.7.7.6



More information about the anaconda-patches mailing list