From: "Brian C. Lane" <bcl(a)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