data/ui/addrepo.glade | 1 + pyanaconda/__init__.py | 3 +- pyanaconda/backend.py | 9 +- pyanaconda/dispatch.py | 5 +- pyanaconda/gui.py | 2 +- pyanaconda/image.py | 45 -- pyanaconda/installclass.py | 5 +- pyanaconda/iutil.py | 13 + pyanaconda/iw/task_gui.py | 624 ++++++++--------------- pyanaconda/kickstart.py | 8 +- pyanaconda/text.py | 2 +- pyanaconda/textw/task_text.py | 1 - pyanaconda/upgrade.py | 1 + pyanaconda/yuminstall.py | 1112 +++++++++++++++++++++++------------------ 14 files changed, 885 insertions(+), 946 deletions(-)
This patchset aims to reorganize our repository editing UI flow so that:
- we don't need hacks to reset repos/sacks of YumBase object when going back, rather use new YumBase object see [1] - we can handle some errors during configuration more gracefuly see [2], [3] with screencasts - we can offer a NFSISO (this one is in the patches) and HD for base/installation repo in editing UI quite easily more generally speaking: - the code is more comprehensible, maintainable, and less prone to fallouts rooted in using yum internals in ways not expected by yum (yes, hacks) - base/install repository is handled the same way as other where possible
The patchset does not: - change UI layout, I think it needs proper design - abstracts repository/packages source completely, but moves things in this direction
The trick is to move tasksel step (repo editing + task selection) before reposetup step and introduce new repocollect step preceeding tasksel in which all repo specifications are collected (from method, ks, dd, /etc/anaconda.repos.d) into objects of new class RepoSpec stored in anaconda.repos list. RepoSpec objects can be used in UI (and elsewhere, e.g. in writeconfig) instead of AnacondaYumRepo (child of YumRepository) objects, using of whose made UI and logic interwined to the point of unmaintenablity. It also alows to remove stuff added into our AnacondaYumRepo (keeping only refernce to RepoSpec there).
RepoSpec should go to its own module repos.py as it should be backend independent. For now, I keep it in yuminstall.py together with YumRepoSpec which is inherited from RepoSpec.
There is one problem I don't address in this series of patches: The tasksel step/screen combines repo editing and task selection which is unfortunate because task selection requires all repositories to be set. The appropriate steps would be:
edit repositories (UI) -> setup repositories (nonUI) -> select tasks (UI),
but the first and the third are on the same screen in present UI and threfore I have to call non-UI step from the UI screen which I wasn't able to do without hack. Making task selection a separate step or adding it to group/package selection screen (where it belongs from the yum backend point of view, not sure if also from user point of view) would make things cleaner and allow to avoid the UI flow hacking (see (*) in steps 2) and 3) in "Patched flow" below) You can see in [4] how the repositories are setup after each modification in repo list.
Another important change is that all the repository setup (network enablement, mounting, repo setup, sack setup, groups setup) is done in a loop over all enabled RepoSpecs which allows for better handling of errors in UI.
Some overwhelming details:
Present flow:
1) "reposetup" step - create ayum (YumBackend object) - add AnacondaYumRepo objects for required repos (mehtod, ks repo=, dd) and add them to ayum object - mounting of isos (for cdrom, nfsiso, hdiso) - network enablement if needed - repo setup, sack setup, group setup - editing of invalid repos in dialog - no way to disable repo at this point, in case of fail only Edit/Exit is offered 2) UI "tasksel" step - edit repos - working directly YumRepository objects (the ayum.repos is the only store of repos we have), so all setup from step 1) has to be done here too - select tasks (note that changes in repos can make task selection list obsolete in theory) 3) "basepkgsel" step - group/package selection based on installclass and ks - hacky resetting of groups/sack information needed for going back in UI (from 4, from dialog in 5) 4) UI "group-selection" step - you can go back to 2) 5) "postselection" - create transaction - in case of conflicts you can go back to 4) from a dialog 6) "writeconfig" - write anaconda-ks.cfg using ayum.repos (AnacondaYumRepository objects)
Patched flow:
1) "repocollect" step - initialze RepoSpec objects storing specs of required repos (from method, ks, DD, /etc/anaconda.repos.d, addon repos for RHEL) 2) UI "repotasksel" step (formerly "tasksel", keeping its UI layout) - edit repos - works on RepoSpec objects - select tasks - after a repo is edited/enabled/disabled, step 3) has to be called so that task list can be updated 3) "reposetup" step - is skipped if we did it as a part of 2) in UI This could be removed if task selection was separated from repo editing (*) - create ayum (YumBackend object) - for each repo RepoSpec: - enable network if needed - mount repo (nfsiso, hd, cdrom) - add corresponding YumRepository object to ayum - set cache for repository - do repository setup - do sack setup - do group setup if any of the above fails, we know what repo was the cause of fail, store failure info in its RepoSpec object, go back to 2), mark failed repo in the list (see [4]) 4) "basepkgsel" step - group selection based on installclass and ks - no reset on the way back is needed as in UI step 2), we start with new ayum object 5) UI "group-selection" - you can go back to 2) 6) "postselection" - create transaction - in case of conflicts you can go back to 5) from a dialog 7) "writeconfig" - write anaconda-ks.cfg using anaconda.repos (RepoSpec objects)
[1] https://bugzilla.redhat.com/show_bug.cgi?id=622376#c5 screencasts: - the bug: http://rvykydal.fedorapeople.org/repoUI/example3.ogg - fixed with patches: http://rvykydal.fedorapeople.org/repoUI/example3patched.ogg
[2] if failing nfs repo= is used in kickstart - choosing Continue lead to fatal fail: http://rvykydal.fedorapeople.org/repoUI/example1.ogg - choosing Edit and Cancel lead to fatal fail: http://rvykydal.fedorapeople.org/repoUI/example1b.ogg ... also the true cause (nfs mount error) is hidden - with the patch [Edit] means going to repo UI screen where the failing repo can be disabled: http://rvykydal.fedorapeople.org/repoUI/example1patched.ogg
[3] network requiring repository set by boot param repo=, no network needed till repository steps - failing to enable network for base repository is fatal: http://rvykydal.fedorapeople.org/repoUI/example3.ogg - with patch, you can fall back to media (non network) repo: http://rvykydal.fedorapeople.org/repoUI/example3patched.ogg
The step is renamed to repotasksel as it is for editing/selection for repositories and selection of tasks. The latter should probably go into its own UI screen or to group selection screen as it requires repos to be set up to obtain group information. This can be done later, for now I don't change the UI screens.
The main reason for the change of order is simply that repos should be set up after their editing/selection. This way we can get rid of mixing of additional repo setup stuff with repo editing UI, and handle errors in repo specification more gracefuly (like disabling the repo). --- pyanaconda/dispatch.py | 2 +- pyanaconda/gui.py | 2 +- pyanaconda/installclass.py | 4 ++-- pyanaconda/kickstart.py | 8 ++++---- pyanaconda/text.py | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/pyanaconda/dispatch.py b/pyanaconda/dispatch.py index 39aa2d9..94f70f2 100644 --- a/pyanaconda/dispatch.py +++ b/pyanaconda/dispatch.py @@ -95,8 +95,8 @@ installSteps = [ ("upgbootloader", ), ("bootloadersetup", bootloaderSetupChoices, ), ("bootloader", ), + ("repotasksel", ), ("reposetup", doBackendSetup, ), - ("tasksel", ), ("basepkgsel", doBasePackageSelect, ), ("group-selection", ), ("postselection", doPostSelection, ), diff --git a/pyanaconda/gui.py b/pyanaconda/gui.py index 1853b2d..52b47a0 100755 --- a/pyanaconda/gui.py +++ b/pyanaconda/gui.py @@ -79,7 +79,7 @@ stepToClass = { "network" : ("network_gui", "NetworkWindow"), "timezone" : ("timezone_gui", "TimezoneWindow"), "accounts" : ("account_gui", "AccountWindow"), - "tasksel": ("task_gui", "TaskWindow"), + "repotasksel": ("task_gui", "TaskWindow"), "group-selection": ("package_gui", "GroupSelectionWindow"), "install" : ("progress_gui", "InstallProgressWindow"), "complete" : ("congrats_gui", "CongratulationWindow"), diff --git a/pyanaconda/installclass.py b/pyanaconda/installclass.py index 3c6a579..920c710 100644 --- a/pyanaconda/installclass.py +++ b/pyanaconda/installclass.py @@ -94,9 +94,9 @@ class BaseInstallClass(object): "network", "timezone", "accounts", + "repotasksel", "reposetup", "basepkgsel", - "tasksel", "postselection", "reipl", "install", @@ -125,7 +125,7 @@ class BaseInstallClass(object):
# allow backends to disable interactive package selection if not anaconda.backend.supportsPackageSelection: - dispatch.skipStep("tasksel", skip = 1, permanent=1) + dispatch.skipStep("repotasksel", skip = 1, permanent=1) dispatch.skipStep("group-selection", skip = 1, permanent=1)
# allow install classes to turn off the upgrade diff --git a/pyanaconda/kickstart.py b/pyanaconda/kickstart.py index 4bafb27..04607a9 100644 --- a/pyanaconda/kickstart.py +++ b/pyanaconda/kickstart.py @@ -1472,18 +1472,18 @@ def setSteps(anaconda):
# If the package section included anything, skip group selection. if ksdata.upgrade.upgrade: - ksdata.skipSteps.extend(["tasksel", "group-selection"]) + ksdata.skipSteps.extend(["repotasksel", "group-selection"])
# Special check for this, since it doesn't make any sense. if ksdata.packages.seen: warnings.warn("Ignoring contents of %packages section due to upgrade.") elif havePackages(ksdata.packages): - ksdata.skipSteps.extend(["tasksel", "group-selection"]) + ksdata.skipSteps.extend(["repotasksel", "group-selection"]) else: if ksdata.packages.seen: - ksdata.skipSteps.extend(["tasksel", "group-selection"]) + ksdata.skipSteps.extend(["repotasksel", "group-selection"]) else: - ksdata.showSteps.extend(["tasksel", "group-selection"]) + ksdata.showSteps.extend(["repotasksel", "group-selection"])
for n in ksdata.skipSteps: dispatch.skipStep(n) diff --git a/pyanaconda/text.py b/pyanaconda/text.py index 3e52978..835421d 100644 --- a/pyanaconda/text.py +++ b/pyanaconda/text.py @@ -60,7 +60,7 @@ stepToClasses = { "network" : ("network_text", "HostnameWindow"), "timezone" : ("timezone_text", "TimezoneWindow"), "accounts" : ("userauth_text", "RootPasswordWindow"), - "tasksel": ("task_text", "TaskWindow"), + "repotasksel": ("task_text", "TaskWindow"), "install" : ("progress_text", "setupForInstall"), "complete" : ("complete_text", "FinishedWindow"), }
The class is called RepoSpec. It should be backend independent, so perhaps it will be moved to backend.py or even (new) repos.py. It will be used instead of working directly (e.g. in UI steps) with extended YumRepository objects (AnacondaYumRepository) making going back in UI possible without hacks (by using new YumBackend object).
Compared to YumRepository, it can hold anconda specific urls (nfs, cdrom, nfsiso), repository type (method, kickstart, UI, driver disk, addon), and info about setup status and cause of fail. --- pyanaconda/__init__.py | 1 + pyanaconda/yuminstall.py | 207 +++++++++++++++++++++++++++++++++++----------- 2 files changed, 158 insertions(+), 50 deletions(-)
diff --git a/pyanaconda/__init__.py b/pyanaconda/__init__.py index 5c07edc..9d94519 100644 --- a/pyanaconda/__init__.py +++ b/pyanaconda/__init__.py @@ -86,6 +86,7 @@ class Anaconda(object): self.upgradeSwapInfo = None self._users = None self.mehConfig = None + self.repos = []
# *sigh* we still need to be able to write this out self.xdriver = None diff --git a/pyanaconda/yuminstall.py b/pyanaconda/yuminstall.py index 11f014a..a7e5385 100644 --- a/pyanaconda/yuminstall.py +++ b/pyanaconda/yuminstall.py @@ -91,6 +91,159 @@ def size_string (size):
return to_unicode(retval)
+ +# TODORV: move into a module? +class RepoSpec(object): + + def __init__(self): + self.url = "" + self.mirrorlist = False + self.name = "" + self.cost = None + self.excludepkgs = [] + self.includepkgs = [] + self.proxy = "" + self.proxy_username = "" + self.proxy_password = "" + self.ignoregroups = None + self.sslverify = True + self.baseurl = "" + self.mediaid = None + self.media_device = None + + self.enabled = False + + self.setup = "?" + self.setup_fail_comment = "" + + self.addon_repos = None + + # this should ideally go away, maybe use inheritance + self.type = "" + + @property + def id(self): + id = "%s-%s" % (self.type, self.name.replace(" ", "")) + if self.type == "method": + id += "-%s" % productStamp + return id + + + # TODORV: remove from AnacondaYumRepo? + def needsNetwork(self): + return (self.url.startswith("http") or + self.url.startswith("ftp:") or + self.url.startswith("nfs:") or + self.url.startswith("nfsiso:")) + + def isIsoRepo(self): + return (self.url.startswith("cdrom:") or + self.url.startswith("hd:") or + self.url.startswith("nfsiso:")) + + def setProxy(self, obj): + """ + Set the proxy settings from a string in obj.proxy + If the string includes un/pw use those, otherwise set the un/pw from + obj.proxyUsername and obj.proxyPassword + """ + # This is the same pattern as from loader/urls.c:splitProxyParam + # except that the POSIX classes have been replaced with character + # ranges + # NOTE: If this changes, update tests/regex/proxy.py + # + # proxy=[protocol://][username[:password]@]host[:port][path] + pattern = re.compile("([A-Za-z]+://)?(([A-Za-z0-9]+)(:[^:@]+)?@)?([^:/]+)(:[0-9]+)?(/.*)?") + + m = pattern.match(obj.proxy) + + 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) + + # yum also requires a protocol. If none was given, + # default to http. + if m.group(1): + proxy = m.group(1) + proxy + else: + proxy = "http://" + proxy + + # Set the repo proxy. NOTE: yum immediately parses this and + # raises an error if it isn't correct + self.proxy = proxy + + if m and m.group(3): + self.proxy_username = m.group(3) + elif getattr(obj, "proxyUsername", None): + self.proxy_username = obj.proxyUsername + + if m and m.group(4): + # Skip the leading colon. + self.proxy_password = m.group(4)[1:] + elif getattr(obj, "proxyPassword", None): + self.proxy_password = obj.proxyPassword + +class YumRepoSpec(RepoSpec): + def __init__(self, yumrepo=None): + RepoSpec.__init__(self) + self.yumrepo = yumrepo + if yumrepo: + self.initFromYumRepo(yumrepo) + + def initYumRepo(self, yumrepo): + if self.mirrorlist: + yumrepo.mirrorlist = self.url + else: + yumrepo.baseurl = self.baseurl + yumrepo.name = self.name + if self.cost is not None: + yumrepo.cost = self.cost + if self.excludepkgs: + yumrepo.exclude = self.excludepkgs + if self.includepkgs: + yumrepo.includepkgs = self.includepkgs + if self.sslverify is not None: + yumrepo.sslverify = self.sslverify + if self.mediaid is not None: + yumrepo.mediaid = self.mediaid + if self.proxy: + yumrepo.proxy = self.proxy + if self.proxy_username: + yumrepo.proxy_username = self.proxy_username + if self.proxy_password: + yumrepo.proxy_password = self.proxy_password + + + def initFromYumRepo(self, yumrepo): + if self.baseurl: + self.url = yumrepo.baseurl[0] + else: + self.url = yumrepo.mirrorlist + self.mirrorlist = True + self.name = yumrepo.name + self.cost = yumrepo.cost + self.excludepkgs = yumrepo.exclude + self.includepkgs = yumrepo.includepkgs + self.proxy = yumrepo.proxy + self.proxy_username = yumrepo.proxy_username + self.proxy_password = yumrepo.proxy_password + self.sslverify = yumrepo.sslverify + self.enabled = yumrepo.enabled + self.mediaid = yumrepo.mediaid + +def isValidRepoURL(url): + return (url.startswith("hd:") or + url.startswith("nfsiso:") or + url.startswith("http:") or + url.startswith("ftp:") or + url.startswith("cdrom:") or + url.startswith("nfs:")) + class AnacondaCallback:
def __init__(self, ayum, anaconda, instLog, modeText): @@ -300,53 +453,6 @@ class AnacondaYumRepo(YumRepository): anacondaBaseURLs = property(_getAnacondaBaseURLs, _setAnacondaBaseURLs, doc="Extends AnacondaYum.baseurl to store non-yum urls:")
- def setProxy(self, obj): - """ - Set the proxy settings from a string in obj.proxy - If the string includes un/pw use those, otherwise set the un/pw from - obj.proxyUsername and obj.proxyPassword - """ - # This is the same pattern as from loader/urls.c:splitProxyParam - # except that the POSIX classes have been replaced with character - # ranges - # NOTE: If this changes, update tests/regex/proxy.py - # - # proxy=[protocol://][username[:password]@]host[:port][path] - pattern = re.compile("([A-Za-z]+://)?(([A-Za-z0-9]+)(:[^:@]+)?@)?([^:/]+)(:[0-9]+)?(/.*)?") - - m = pattern.match(obj.proxy) - - 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) - - # yum also requires a protocol. If none was given, - # default to http. - if m.group(1): - proxy = m.group(1) + proxy - else: - proxy = "http://" + proxy - - # Set the repo proxy. NOTE: yum immediately parses this and - # raises an error if it isn't correct - self.proxy = proxy - - if m and m.group(3): - self.proxy_username = m.group(3) - elif getattr(obj, "proxyUsername", None): - self.proxy_username = obj.proxyUsername - - if m and m.group(4): - # Skip the leading colon. - self.proxy_password = m.group(4)[1:] - elif getattr(obj, "proxyPassword", None): - self.proxy_password = obj.proxyPassword -
class YumSorter(yum.YumBase): def _transactionDataFactory(self): @@ -1895,9 +2001,10 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon rc = True return rc
- def writeKS(self, f): - for repo in self.ayum.repos.listEnabled(): - if repo.name == "Installation Repo": + def writeKS(self, f, repos): + for repo in repos: + if (repo.type not in ["kickstart", "UI"] or + not repo.enabled): continue
line = "repo --name="%s" " % (repo.name or repo.repoid)
On Thu, 2010-09-30 at 16:26 +0200, Radek Vykydal wrote:
The class is called RepoSpec. It should be backend independent, so perhaps it will be moved to backend.py or even (new) repos.py. It will be used instead of working directly (e.g. in UI steps) with extended YumRepository objects (AnacondaYumRepository) making going back in UI possible without hacks (by using new YumBackend object).
Compared to YumRepository, it can hold anconda specific urls (nfs, cdrom, nfsiso), repository type (method, kickstart, UI, driver disk, addon), and info about setup status and cause of fail.
Doesn't this mean you're going to be constantly having to add code whenever we add functionality or options to the yum repo objects?
-sv
In this step preceeding repotasksel (repo UI) all non-UI repo requirements should be collected (from method, ks, driverdisc, /etc/anaconda/repos.d config). This used to be done as part of reposetup - partly in configBaseURL (for method), partly in doConfigSetup (for other repos), we need to move it before repo UI. Another part of configBaseURL - mounting of nfs, nfsiso, media was separated so that it can be done in reposetup step for each repo (RepoSpec.mount() method).
A tricky part here is obtaining specifications of repos from /etc/anaconda.repos.d for which temporary AnacondaYumBase object is created in the repocollect step. --- pyanaconda/backend.py | 3 + pyanaconda/dispatch.py | 3 +- pyanaconda/installclass.py | 1 + pyanaconda/upgrade.py | 1 + pyanaconda/yuminstall.py | 450 ++++++++++++++++++++----------------------- 5 files changed, 216 insertions(+), 242 deletions(-)
diff --git a/pyanaconda/backend.py b/pyanaconda/backend.py index 747252d..f7c9078 100644 --- a/pyanaconda/backend.py +++ b/pyanaconda/backend.py @@ -264,6 +264,9 @@ class AnacondaBackend:
def complete(self, anaconda): pass +def doCollectRepos(anaconda): + if anaconda.backend.doCollectRepos(anaconda) == DISPATCH_BACK: + return DISPATCH_BACK
def doBackendSetup(anaconda): if anaconda.backend.doBackendSetup(anaconda) == DISPATCH_BACK: diff --git a/pyanaconda/dispatch.py b/pyanaconda/dispatch.py index 94f70f2..ad7e7c4 100644 --- a/pyanaconda/dispatch.py +++ b/pyanaconda/dispatch.py @@ -42,7 +42,7 @@ from upgrade import findRootParts, queryUpgradeContinue from installmethod import doMethodComplete from kickstart import runPostScripts
-from backend import doPostSelection, doBackendSetup, doBasePackageSelect +from backend import doPostSelection, doBackendSetup, doBasePackageSelect, doCollectRepos from backend import doPreInstall, doPostInstall, doInstall from backend import writeConfiguration
@@ -95,6 +95,7 @@ installSteps = [ ("upgbootloader", ), ("bootloadersetup", bootloaderSetupChoices, ), ("bootloader", ), + ("repocollect", doCollectRepos, ), ("repotasksel", ), ("reposetup", doBackendSetup, ), ("basepkgsel", doBasePackageSelect, ), diff --git a/pyanaconda/installclass.py b/pyanaconda/installclass.py index 920c710..5344933 100644 --- a/pyanaconda/installclass.py +++ b/pyanaconda/installclass.py @@ -94,6 +94,7 @@ class BaseInstallClass(object): "network", "timezone", "accounts", + "repocollect", "repotasksel", "reposetup", "basepkgsel", diff --git a/pyanaconda/upgrade.py b/pyanaconda/upgrade.py index 1978409..82c0737 100644 --- a/pyanaconda/upgrade.py +++ b/pyanaconda/upgrade.py @@ -332,6 +332,7 @@ def setSteps(anaconda): "upgrademigratefs", "enablefilesystems", "upgradecontinue", + "repocollect", "reposetup", "upgbootloader", "checkdeps", diff --git a/pyanaconda/yuminstall.py b/pyanaconda/yuminstall.py index a7e5385..1fcf7f8 100644 --- a/pyanaconda/yuminstall.py +++ b/pyanaconda/yuminstall.py @@ -236,6 +236,100 @@ class YumRepoSpec(RepoSpec): self.enabled = yumrepo.enabled self.mediaid = yumrepo.mediaid
+ def mount(self, ayum): + # TODORV: failure handling + if self.url.startswith("hd:"): + if self.url.count(":") == 2: + (device, path) = self.url[3:].split(":") + else: + (device, fstype, path) = self.url[3:].split(":") + + ayum.isodir = "/mnt/isodir/%s" % path + + # This takes care of mounting /mnt/isodir first. + ayum._switchImage(1) + ayum.mediagrabber = ayum.mediaHandler + elif self.url.startswith("http") or self.url.startswith("ftp:"): + self.baseurl = self.url + elif self.url.startswith("cdrom:"): + # When/if supported (KS, UI), parse device + if not self.media_device: + cdr = scanForMedia(ayum.tree, ayum.anaconda.storage) + if cdr: + self.media_device = cdr + log.info("found installation media on %s" % cdr) + else: + ayum.anaconda.intf.messageWindow(_("No Media Found"), + _("No installation media was found. " + "Please insert a disc into your drive " + "and try again.")) + self.setup_fail_comment = _("No Media Found") + self.setup = "failed" + return False + ayum.anaconda.mediaDevice = self.media_device + ayum._switchCD(1) + ayum.mediagrabber = ayum.mediaHandler + self.baseurl = "file://%s" % ayum.tree + elif self.url.startswith("nfs:"): + if self.type == "method": + dest = ayum.tree + else: + dest = tempfile.mkdtemp("", self.name.replace(" ", ""), "/mnt") + + (opts, server, path) = iutil.parseNfsUrl(self.url) + try: + isys.mount(server+":"+path, dest, "nfs", options=opts) + except Exception as e: + log.error("error mounting NFS repo %s: %s" % (self.name, e)) + raise + self.baseurl = "file://%s" % dest + + # This really should be fixed in loader instead but for now see + # if there's images and if so go with this being an NFSISO + # install instead. + if self.type == "method": + images = findIsoImages(ayum.tree, ayum.anaconda.intf.messageWindow) + if images != {}: + isys.umount(ayum.tree, removeDir=False) + self.url = "nfsiso:%s" % self.url[4:] + # TODORV - fix properly? SPOT? + # "nfsiso:" case pasted + ayum.isodir = "/mnt/isodir" + ayum._switchImage(1) + ayum.mediagrabber = ayum.mediaHandler + self.baseurl = "file://%s" % ayum.tree + elif self.url.startswith("nfsiso:"): + ayum.isodir = "/mnt/isodir" + ayum._switchImage(1) + ayum.mediagrabber = ayum.mediaHandler + self.baseurl = "file://%s" % ayum.tree + else: + log.info("repo %s has unsupported url: %s" % (self.name, + self.url)) + if self.isIsoRepo(): + self.mediaid = getMediaId(ayum.tree) + log.info("set mediaid of repo %s to: %s" % (self.id, self.mediaid)) + + return True + + def dirCleanup(self): + if not self.yumrepo: + return False + cachedir = self.yumrepo.getAttribute('cachedir') + if os.path.isdir(cachedir): + # Remove also cached metadata only for non-network repos + # or installation repos or repos that has not been used (enabled). + # TODORV: count also addon installation repos in when implemented + if (not (self.url.startswith("http") or self.url.startswith("ftp")) + or self.type == "method" + or not self.enabled): + shutil.rmtree(cachedir) + else: + if os.path.exists("%s/headers" % cachedir): + shutil.rmtree("%s/headers" % cachedir) + if os.path.exists("%s/packages" % cachedir): + shutil.rmtree("%s/packages" % cachedir) + def isValidRepoURL(url): return (url.startswith("hd:") or url.startswith("nfsiso:") or @@ -464,6 +558,8 @@ class AnacondaYum(YumSorter): self.anaconda = anaconda self._timestamp = None
+ self.readReposFromConfig = False + self.repoIDcounter = itertools.count()
# Only needed for hard drive and nfsiso installs. @@ -586,117 +682,6 @@ class AnacondaYum(YumSorter): discImages=self._discImages) self.currentMedia = discnum
- def configBaseURL(self): - # We only have a methodstr if method= or repo= was passed to - # anaconda. No source for this base repo (the CD media, NFS, - # whatever) is mounted yet since loader only mounts the source - # for the stage2 image. We need to set up the source mount - # now. - if flags.cmdline.has_key("preupgrade"): - path = "/var/cache/yum/preupgrade" - self.anaconda.methodstr = "hd::%s" % path - self._baseRepoURL = "file:///mnt/sysimage/%s" % path - elif self.anaconda.methodstr: - m = self.anaconda.methodstr - - if m.startswith("hd:"): - if m.count(":") == 2: - (device, path) = m[3:].split(":") - else: - (device, fstype, path) = m[3:].split(":") - - self.isodir = "/mnt/isodir/%s" % path - - # This takes care of mounting /mnt/isodir first. - self._switchImage(1) - self.mediagrabber = self.mediaHandler - elif m.startswith("nfsiso:"): - self.isodir = "/mnt/isodir" - - # Calling _switchImage takes care of mounting /mnt/isodir first. - if not network.hasActiveNetDev(): - if not self.anaconda.intf.enableNetwork(): - self._baseRepoURL = None - return - - urlgrabber.grabber.reset_curl_obj() - - self._switchImage(1) - self.mediagrabber = self.mediaHandler - elif m.startswith("http") or m.startswith("ftp:"): - self._baseRepoURL = m - elif m.startswith("nfs:"): - if not network.hasActiveNetDev(): - if not self.anaconda.intf.enableNetwork(): - self._baseRepoURL = None - - urlgrabber.grabber.reset_curl_obj() - - (opts, server, path) = iutil.parseNfsUrl(m) - isys.mount(server+":"+path, self.tree, "nfs", options=opts) - - # This really should be fixed in loader instead but for now see - # if there's images and if so go with this being an NFSISO - # install instead. - images = findIsoImages(self.tree, self.anaconda.intf.messageWindow) - if images != {}: - isys.umount(self.tree, removeDir=False) - self.anaconda.methodstr = "nfsiso:%s" % m[4:] - self.configBaseURL() - return - elif m.startswith("cdrom:"): - self._switchCD(1) - self.mediagrabber = self.mediaHandler - self._baseRepoURL = "file://%s" % self.tree - else: - # No methodstr was given. In order to find an installation source, - # we should first check to see if there's a CD/DVD with packages - # on it, and then default to the mirrorlist URL. The user can - # always change the repo with the repo editor later. - cdr = scanForMedia(self.tree, self.anaconda.storage) - if cdr: - self.mediagrabber = self.mediaHandler - self.anaconda.mediaDevice = cdr - self.currentMedia = 1 - log.info("found installation media on %s" % cdr) - else: - # No CD with media on it and no repo=/method= parameter, so - # default to using whatever's enabled in /etc/yum.repos.d/ - self._baseRepoURL = None - - def configBaseRepo(self, root='/'): - # Create the "base" repo object, assuming there is one. Otherwise we - # just skip all this and use the defaults from /etc/yum.repos.d. - if not self._baseRepoURL: - return - - # add default repos - anacondabaseurl = (self.anaconda.methodstr or - "cdrom:%s" % (self.anaconda.mediaDevice)) - anacondabasepaths = self.anaconda.instClass.getPackagePaths(anacondabaseurl) - for (name, uri) in self.anaconda.instClass.getPackagePaths(self._baseRepoURL).items(): - rid = name.replace(" ", "") - - repo = AnacondaYumRepo("anaconda-%s-%s" % (rid, productStamp)) - repo.baseurl = uri - repo.anacondaBaseURLs = anacondabasepaths[name] - - repo.name = name - repo.cost = 100 - - if self.anaconda.mediaDevice or self.isodir: - repo.mediaid = getMediaId(self.tree) - log.info("set mediaid of repo %s to: %s" % (rid, repo.mediaid)) - - if self.anaconda.proxy: - repo.setProxy(self.anaconda) - - if flags.noverifyssl: - repo.sslverify = False - - repo.enable() - self.repos.add(repo) - def mediaHandler(self, *args, **kwargs): mediaid = kwargs["mediaid"] discnum = kwargs["discnum"] @@ -719,8 +704,8 @@ class AnacondaYum(YumSorter): text=kwargs["text"], range=kwargs["range"], copy_local=1) return kwargs["local"]
- # XXX: This is straight out of yum, but we need to override it here in - # order to use our own repo class. + # We need to have more control (esp. in UI) over using repos from config + # At some point, after repo selection, we don't want to add them at all. def readRepoConfig(self, parser, section): '''Parse an INI file section for a repository.
@@ -728,41 +713,10 @@ class AnacondaYum(YumSorter): @param section: INI file section to read. @return: YumRepository instance. ''' - repo = AnacondaYumRepo(section) - repo.populate(parser, section, self.conf) - - # Ensure that the repo name is set - if not repo.name: - repo.name = section - self.logger.error(_('Repository %r is missing name in configuration, ' - 'using id') % section) - - # Set attributes not from the config file - repo.yumvar.update(self.conf.yumvar) - repo.cfg = parser - - if "-source" in repo.id or "-debuginfo" in repo.id: - name = repo.name - del(repo) - raise RepoError, "Repo %s contains -source or -debuginfo, excluding" % name - - # this is a little hard-coded, but it's effective - if not BETANAG and ("rawhide" in repo.id or "development" in repo.id): - name = repo.name - del(repo) - raise RepoError, "Excluding devel repo %s for non-devel anaconda" % name - - if BETANAG and not repo.enabled: - name = repo.name - del(repo) - raise RepoError, "Excluding disabled repo %s for prerelease" % name - - # If repo=/method= was passed in, we want to default these extra - # repos to off. - if self._baseRepoURL: - repo.enabled = False - - return repo + if not self.readReposFromConfig: + raise RepoError, "Intentionally ignoring .repo configuration file" + else: + return super(AnacondaYum,self).readRepoConfig(parser, section)
# We need to make sure $releasever gets set up before .repo files are # read. Since there's no redhat-release package in /mnt/sysimage (and @@ -827,99 +781,6 @@ class AnacondaYum(YumSorter): else: YumSorter._getConfig(self, fn=fn, root=root, enabled_plugins=["whiteout", "blacklist"]) - self.configBaseRepo(root=root) - - extraRepos = [] - - ddArch = os.uname()[4] - - #Add the Driver disc repos to Yum - for d in glob.glob(DD_RPMS): - dirname = os.path.basename(d) - rid = "anaconda-%s" % dirname - - repo = AnacondaYumRepo(rid) - repo.baseurl = [ "file://%s" % d ] - repo.name = "Driver Disk %s" % dirname.split("-")[1] - repo.enable() - extraRepos.append(repo) - - if self.anaconda.ksdata: - for ksrepo in self.anaconda.ksdata.repo.repoList: - anacondaBaseURLs = [ksrepo.baseurl] - - # yum doesn't understand nfs:// and doesn't want to. We need - # to first do the mount, then translate it into a file:// that - # yum does understand. - # "nfs:" and "nfs://" prefixes are accepted in ks repo --baseurl - if ksrepo.baseurl and ksrepo.baseurl.startswith("nfs:"): - if not network.hasActiveNetDev() and not self.anaconda.intf.enableNetwork(): - self.anaconda.intf.messageWindow(_("No Network Available"), - _("Some of your software repositories require " - "networking, but there was an error enabling the " - "network on your system."), - type="custom", custom_icon="error", - custom_buttons=[_("_Exit installer")]) - sys.exit(1) - - urlgrabber.grabber.reset_curl_obj() - - dest = tempfile.mkdtemp("", ksrepo.name.replace(" ", ""), "/mnt") - - # handle "nfs://" prefix - if ksrepo.baseurl[4:6] == '//': - ksrepo.baseurl = ksrepo.baseurl.replace('//', '', 1) - anacondaBaseURLs = [ksrepo.baseurl] - try: - isys.mount(ksrepo.baseurl[4:], dest, "nfs") - except Exception as e: - log.error("error mounting NFS repo: %s" % e) - - ksrepo.baseurl = "file://%s" % dest - - repo = AnacondaYumRepo(ksrepo.name) - repo.mirrorlist = ksrepo.mirrorlist - repo.name = ksrepo.name - - if not ksrepo.baseurl: - repo.baseurl = [] - else: - repo.baseurl = [ ksrepo.baseurl ] - repo.anacondaBaseURLs = anacondaBaseURLs - - if ksrepo.cost: - repo.cost = ksrepo.cost - - if ksrepo.excludepkgs: - repo.exclude = ksrepo.excludepkgs - - if ksrepo.includepkgs: - repo.includepkgs = ksrepo.includepkgs - - if ksrepo.noverifyssl: - repo.sslverify = False - - if ksrepo.proxy: - repo.setProxy(ksrepo) - - repo.enable() - extraRepos.append(repo) - - for repo in extraRepos: - try: - self.repos.add(repo) - log.info("added repository %s with URL %s" % (repo.name, repo.mirrorlist or repo.baseurl)) - except: - log.warning("ignoring duplicate repository %s with URL %s" % (repo.name, repo.mirrorlist or repo.baseurl)) - - self.repos.setCacheDir(self.conf.cachedir) - - if os.path.exists("%s/boot/upgrade/install.img" % self.anaconda.rootPath): - log.info("REMOVING stage2 image from %s /boot/upgrade" % self.anaconda.rootPath ) - try: - os.unlink("%s/boot/upgrade/install.img" % self.anaconda.rootPath) - except: - log.warning("failed to clean /boot/upgrade")
def downloadHeader(self, po): while True: @@ -1281,6 +1142,113 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon # unhappy (#496961) iutil.resetRpmDb(anaconda.rootPath)
+ def doCollectRepos(self, anaconda): + """from methodstr, KS, /etc/yumrepos.d, addon (RHEL)""" + if anaconda.dir == DISPATCH_BACK: + return DISPATCH_BACK + + anaconda.repos = [] + + # base install repo + rs = YumRepoSpec() + + # special preupgrade case + if flags.cmdline.has_key("preupgrade"): + path = "/var/cache/yum/preupgrade" + rs.url = "hd::%s" % path + rs.baseurl = "file:///mnt/sysimage/%s" % path + rs.type = "preupgrade" + self.anaconda.repos.append(rs) + rs.name = "Preupgrade Repo" + else: + # method=/repo= - base installation repository + m = self.anaconda.methodstr + if not m: + cdr = scanForMedia(self.tree, self.anaconda.storage) + if cdr: + log.info("found installation media on %s" % cdr) + rs.media_device = cdr + m = "cdrom://" + else: + m = "" + if isValidRepoURL(m): + rs.url = m + rs.name = "Installation Repo" + rs.type = "method" + rs.cost = 100 + rs.enabled = True + if self.anaconda.proxy: + rs.setProxy(self.anaconda) + if flags.noverifyssl: + rs.sslverify = False + self.anaconda.repos.append(rs) + + # DD repos + for d in glob.glob(DD_RPMS): + rs = YumRepoSpec() + dirname = os.path.basename(d) + rs.url = "file://%s" + rs.name = "Driver Disk %s" % dirname.split("-")[1] + rs.type = "driverdisc" + rs.enabled = True + self.anaconda.repos.append(rs) + + # ks repos + if self.anaconda.ksdata: + for ksrepo in self.anaconda.ksdata.repo.repoList: + rs = YumRepoSpec() + baseurl = ksrepo.baseurl or ksrepo.mirrorlist or "" + # change "nfs://" prefix to "nfs:" + if baseurl.startswith("nfs://"): + baseurl = baseurl.replace('//', '', 1) + if isValidRepoURL(baseurl): + rs.url = baseurl + if ksrepo.mirrorlist: + rs.mirrorlist = True + rs.type = "kickstart" + rs.name = ksrepo.name + rs.enabled = True + if ksrepo.excludepkgs: + rs.excludepkgs = ksrepo.excludepkgs + if ksrepo.includepkgs: + rs.includepkgs = ksrepo.includepkgs + if ksrepo.noverifyssl: + rs.sslverify = False + if ksrepo.proxy: + rs.setProxy(ksrepo) + else: + log.info("Kickstart repository with invalid url %s ignored" + % baseurl) + self.anaconda.repos.append(rs) + + # addon repos (rhel only?) + + # config repos (.repo) + ayum = AnacondaYum(anaconda) + ayum.readReposFromConfig = True + ayum.doConfigSetup() + for repo in ayum.repos.repos.values(): + if "-source" in repo.id or "-debuginfo" in repo.id: + log.info("Repo %s contains -source or -debuginfo, excluding" % + repo.name) + continue + if not BETANAG and ("rawhide" in repo.id or "development" in repo.id): + log.info("Excluding devel repo %s for non-devel anaconda" % + repo.name) + continue + if BETANAG and not repo.enabled: + log.info("Excluding disabled repo %s for prerelease" % repo.name) + continue + + # If repo=/method= was passed in, we want to default these extra + # repos to off. + if [rs for rs in self.anaconda.repos if rs.type == "method"]: + repo.enabled = False + rs = YumRepoSpec(yumrepo=repo) + rs.type = "config" + self.anaconda.repos.append(rs) + ayum.readReposFromConfig = False + def doBackendSetup(self, anaconda): if anaconda.dir == DISPATCH_BACK: return DISPATCH_BACK
All the steps of a repo setup (network, mount, repo, sack, groups) are done per repo so that we can detect fail in time, offer going to repo UI ("Edit") where the failing repo can be edited, disabled, target (media, images) can be fixed etc... --- pyanaconda/yuminstall.py | 300 ++++++++++++++++++++++++++-------------------- 1 files changed, 172 insertions(+), 128 deletions(-)
diff --git a/pyanaconda/yuminstall.py b/pyanaconda/yuminstall.py index 1fcf7f8..3b9f765 100644 --- a/pyanaconda/yuminstall.py +++ b/pyanaconda/yuminstall.py @@ -572,7 +572,7 @@ class AnacondaYum(YumSorter):
# Where is the source media mounted? This is the directory # where Packages/ is located. - self.tree = "/mnt/source" + self.tree = anaconda.backend.tree
self.macros = {}
@@ -591,28 +591,6 @@ class AnacondaYum(YumSorter): self.updates = [] self.localPackages = []
- def setup(self): - # yum doesn't understand all our method URLs, so use this for all - # except FTP and HTTP installs. - self._baseRepoURL = "file://%s" % self.tree - - while True: - try: - self.configBaseURL() - break - except SystemError, e: - self.anaconda.intf.messageWindow(_("Error Setting Up Repository"), - _("The following error occurred while setting up the " - "installation repository:\n\n%(e)s\n\nPlease provide the " - "correct information for installing %(productName)s.") - % {'e': e, 'productName': productName}) - - self.anaconda.methodstr = self.anaconda.intf.methodstrRepoWindow(self.anaconda.methodstr or "cdrom:") - - self.doConfigSetup(root=self.anaconda.rootPath) - if not self.anaconda.bootloader.doUpgradeOnly: - self.conf.installonlypkgs = [] - def _switchCD(self, discnum): if os.access("%s/.discinfo" % self.tree, os.R_OK): f = open("%s/.discinfo" % self.tree) @@ -1131,6 +1109,8 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon fd = open("/tmp/anaconda-yum.conf", "w") fd.write(buf) fd.close() + self.tree = "/mnt/source" + self.ayum = None
def complete(self, anaconda): if not anaconda.mediaDevice and os.path.ismount(self.ayum.tree): @@ -1257,139 +1237,198 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon # FIXME: make sure that the rpmdb doesn't have stale locks :/ iutil.resetRpmDb(anaconda.rootPath)
+ # TODORV move to __init__? still needed? iutil.writeRpmPlatform() + # TODORV move to __init__? anaconda.backend.freetmp(anaconda) + self.ayum = AnacondaYum(anaconda) - self.ayum.setup()
- self.ayum.doMacros() + self._removeUpgradeInstallImage(anaconda.rootPath)
- # If any enabled repositories require networking, go ahead and bring - # it up now. No need to have people wait for the timeout when we - # know this in advance. - for repo in self.ayum.repos.listEnabled(): - if repo.needsNetwork() and not network.hasActiveNetDev(): - if not anaconda.intf.enableNetwork(): - anaconda.intf.messageWindow(_("No Network Available"), - _("Some of your software repositories require " - "networking, but there was an error enabling the " - "network on your system."), - type="custom", custom_icon="error", - custom_buttons=[_("_Exit installer")]) - sys.exit(1) + self.ayum.doConfigSetup(root=anaconda.rootPath)
- urlgrabber.grabber.reset_curl_obj() + self.ayum.doMacros() + + self._resetSetupResults(anaconda.repos) + failed = True + for repo in anaconda.repos: + if not repo.enabled: + log.debug("not adding disabled repo %s with URL %s" % + (repo.id, repo.url)) + continue + else: + log.debug("adding enabled repo %s with URL %s" % + (repo.id, repo.url)) + if not self._enableNetworkIfNeeded(repo, anaconda.intf): + break + if not repo.mount(self.ayum): + break + if not self.addEnabledRepo(self.ayum, repo, anaconda.intf): + break + # We repeat this for each repo because we want to have + # repo-specific failure + self.ayum.repos.setCacheDir(self.ayum.conf.cachedir) + if not self.doRepoSetup(self.ayum, repo, anaconda.intf): + break + if not self.doSackSetup(self.ayum, repo, anaconda.intf): break + # We repeat this for each repo because we want to have + # repo-specific failure + if not self.doGroupSetup(self.ayum, repo, anaconda.intf): + break + repo.yumrepo.setFailureObj(self.ayum.urlgrabberFailureCB) + repo.yumrepo.setMirrorFailureObj((self.ayum.mirrorFailureCB, (), + {"repo": repo.yumrepo.id})) + else: + failed = False + + if not anaconda.upgrade: + anaconda.dispatch.skipStep("repotasksel", skip = 0) + if not self.anaconda.bootloader.doUpgradeOnly: + self.ayum.conf.installonlypkgs = []
- self.doRepoSetup(anaconda) - self.doSackSetup(anaconda) - self.doGroupSetup(anaconda) + if failed: + repo.setup="failed" + return DISPATCH_BACK
+ # TODORV: redundant? self.ayum.doMacros()
- def doGroupSetup(self, anaconda): + def _resetSetupResults(self, repos): + for repo in repos: + repo.setup = "?" + repo.setup_fail_comment = "" + + def _enableNetworkIfNeeded(self, repo, intf): + + if network.hasActiveNetDev() or not repo.needsNetwork(): + return True + + if not intf.enableNetwork(): + buttons = [_("_Exit installer"), _("Edit")] + rc = intf.messageWindow(_("No Network Available"), + _("Software repository %s requires " + "networking, but there was an error enabling the " + "network on your system." % repo.name), + type="custom", custom_icon="error", + custom_buttons=buttons) + if rc == 0: + sys.exit(1) + elif rc == 1: + repo.setup = "failed" + repo.setup_fail_comment = _("No Network Available") + return False + + urlgrabber.grabber.reset_curl_obj() + return True + + def addEnabledRepo(self, ayum, repo, intf): + yumrepo = AnacondaYumRepo(repo.id) + repo.initYumRepo(yumrepo) + yumrepo.enable() + repo.yumrepo = yumrepo + try: + ayum.repos.add(yumrepo) + except yum.Errors.DuplicateRepoError, e: + msg =_("The repository %s has already been added. Please " + "choose a different repository name and " + "URL.") % repo.name + intf.messageWindow(_("Error"), msg, type="ok", custom_icon="error") + repo.setup = "failed" + repo.setup_fail_comment = msg + return False + return True + + def _removeUpgradeInstallImage(self, rootpath): + if os.path.exists("%s/boot/upgrade/install.img" % rootpath): + log.info("REMOVING stage2 image from %s /boot/upgrade" % rootpath ) + try: + os.unlink("%s/boot/upgrade/install.img" % rootpath) + except: + log.warning("failed to clean /boot/upgrade") + + def doGroupSetup(self, ayum, repo, intf): while True: try: - self.ayum.doGroupSetup() + ayum.doGroupSetup() except (GroupsError, NoSuchGroup, RepoError), e: - buttons = [_("_Exit installer"), _("_Retry")] + buttons = [_("_Exit installer"), _("_Retry"), _("_Edit")] else: - break # success + return True
- rc = anaconda.intf.messageWindow(_("Error"), - _("Unable to read group information " - "from repositories. This is " - "a problem with the generation " - "of your install tree."), - type="custom", custom_icon="error", - custom_buttons = buttons) + msg = _("Unable to read group information " + "from repositories. This is " + "a problem with the generation " + "of your install tree.\n\n%s" % e) + rc = intf.messageWindow(_("Error"), + msg, + type="custom", custom_icon="error", + custom_buttons = buttons) if rc == 0: sys.exit(0) - else: - self.ayum._setGroups(None) + elif rc == 1: + ayum._setGroups(None) continue + elif rc == 2: + repo.setup = "failed" + repo.setup_fail_comment = msg + return False
- def doRepoSetup(self, anaconda, thisrepo = None, fatalerrors = True): - self.__withFuncDo(anaconda, lambda r: self.ayum.doRepoSetup(thisrepo=r.id), - thisrepo=thisrepo, fatalerrors=fatalerrors, - callback=RepoSetupPulseProgress(anaconda.intf)) + def doRepoSetup(self, ayum, repo, intf, fatalerrors = True): + return self.__withFuncDo(self.ayum.doRepoSetup, + ayum, repo, intf, fatalerrors=fatalerrors, + callback=RepoSetupPulseProgress(intf))
- def doSackSetup(self, anaconda, thisrepo = None, fatalerrors = True): - self.__withFuncDo(anaconda, lambda r: self.ayum.doSackSetup(thisrepo=r.id), - thisrepo=thisrepo, fatalerrors=fatalerrors, - callback=SackSetupProgress(anaconda.intf)) + def doSackSetup(self, ayum, repo, intf, fatalerrors = True): + return self.__withFuncDo(self.ayum.doSackSetup, + ayum, repo, intf, fatalerrors=fatalerrors, + callback=SackSetupProgress(intf))
- def __withFuncDo(self, anaconda, fn, thisrepo=None, fatalerrors=True, + # TODORV: what to do with continue? + def __withFuncDo(self, fn, ayum, repo, intf, fatalerrors=True, callback=None): - # Don't do this if we're being called as a dispatcher step (instead - # of being called when a repo is added via the UI) and we're going - # back. - if thisrepo is None and anaconda.dir == DISPATCH_BACK: - return - - # We want to call the function one repo at a time so we have some - # concept of which repo didn't set up correctly. - if thisrepo is not None: - repos = [self.ayum.repos.getRepo(thisrepo)] - else: - repos = self.ayum.repos.listEnabled() - - for repo in repos: - if callback: - callback.connect(repo) - - while True: - try: - fn(repo) - if callback: - callback.disconnect() - except RepoError, e: - if callback: - callback.disconnect() - buttons = [_("_Exit installer"), _("Edit"), _("_Retry")] - else: - break # success - - if anaconda.ksdata: - buttons.append(_("_Continue")) + if callback: + callback.connect(repo.yumrepo) + rc = -1 + while True: + try: + fn(thisrepo=repo.yumrepo.id) + if callback: + callback.disconnect() + except RepoError, e: + if callback: + callback.disconnect() + buttons = [_("_Exit installer"), _("_Continue"), _("_Edit"), _("_Retry")] + else: + repo.setup = "OK" + break # success
- if not fatalerrors: - raise RepoError, e + if not fatalerrors: + raise RepoError, e
- rc = anaconda.intf.messageWindow(_("Error"), - _("Unable to read package metadata. This may be " - "due to a missing repodata directory. Please " - "ensure that your install tree has been " - "correctly generated.\n\n%s" % e), - type="custom", custom_icon="error", - custom_buttons=buttons) - if rc == 0: - # abort - sys.exit(0) - elif rc == 1: - # edit - anaconda.intf.editRepoWindow(repo) - break - elif rc == 2: - # retry, but only if button is present - continue + rc = intf.messageWindow(_("Error"), + _("Unable to read package metadata. This may be " + "due to a missing repodata directory. Please " + "ensure that your install tree has been " + "correctly generated.\n\n%s" % e), + type="custom", custom_icon="error", + custom_buttons=buttons) + if rc == 0: + sys.exit(0) + elif rc == 1 or rc == 2: + repo.setup = "failed" + repo.setup_fail_comment = str(e) + if rc == 1: + return True else: - # continue, but only if button is present - self.ayum.repos.delete(repo.id) - break - - # if we're in kickstart the repo may have been deleted just above - try: - self.ayum.repos.getRepo(repo.id) - except RepoError: - log.debug("repo %s has been removed" % (repo.id,)) + return False + elif rc == 3: + # retry, but only if button is present continue
- repo.setFailureObj(self.ayum.urlgrabberFailureCB) - repo.setMirrorFailureObj((self.ayum.mirrorFailureCB, (), - {"repo": repo.id})) - self.ayum.repos.callback = None + return True
def getDefaultGroups(self, anaconda): langs = anaconda.instLanguage.getCurrentLangSearchList() @@ -1952,7 +1991,12 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon def groupListExists(self, grps): """Returns bool of whether all of the given groups exist.""" for gid in grps: - g = self.ayum.comps.return_group(gid) + try: + g = self.ayum.comps.return_group(gid) + # catch "No Groups Available in any repository" + except GroupsError as e: + log.debug("exception caught: %s" % e) + return False if not g: log.debug("no such group %s" % (gid,)) return False
We used to do repo setup (including mounting, network enablement) after each single repo edit. In case of repos coming from method=/repo= or ks repo= the only possibility was to edit in dialog or exit. With the patch, edit means going to repo UI screen so the repo can be also disabled.
Icons indicating result of setup was added to repo list items. (see http://rvykydal.fedorapeople.org/repoUI/example.ogg)
Originally I wanted to do all repo setup after going [Next] from repo UI screen, but there is one UI design issue which makes it impossible: The repotasksel step/screen combines repo editing and task selection which is unfortunate because task selection requires all repositories to be set up so the required steps are:
edit repositories (UI) -> setup repositories (nonUI) -> select tasks (UI)
Because the first and the third are on the same screen in present UI I have to call non-UI step from the UI screen which I wasn't able to do without some UI flow hacks (see yuminstall.py hunk). Making task selection separate step or adding it to group/package selection screen (where it belongs from the yum backend point of view, not sure if also from user point of view) would make things cleaner. --- pyanaconda/iw/task_gui.py | 620 ++++++++++++++++----------------------------- pyanaconda/yuminstall.py | 26 ++- 2 files changed, 237 insertions(+), 409 deletions(-)
diff --git a/pyanaconda/iw/task_gui.py b/pyanaconda/iw/task_gui.py index cba030d..3a6392e 100644 --- a/pyanaconda/iw/task_gui.py +++ b/pyanaconda/iw/task_gui.py @@ -21,45 +21,14 @@ import gtk import gtk.glade import gobject from pyanaconda import gui -import gzip from iw_gui import * -from pyanaconda.image import * from pyanaconda.constants import * -from pyanaconda import isys -import shutil +from pyanaconda import iutil
import gettext _ = lambda x: gettext.ldgettext("anaconda", x)
-from pyanaconda import network -from pyanaconda import iutil - -from pyanaconda.yuminstall import AnacondaYumRepo -import urlgrabber.grabber -import yum.Errors - -import logging -log = logging.getLogger("anaconda") - -def setupRepo(anaconda, repo): - if repo.needsNetwork() and not network.hasActiveNetDev(): - if not anaconda.intf.enableNetwork(): - return False - urlgrabber.grabber.reset_curl_obj() - try: - anaconda.backend.doRepoSetup(anaconda, thisrepo=repo.id, fatalerrors=False) - anaconda.backend.doSackSetup(anaconda, thisrepo=repo.id, fatalerrors=False) - log.info("added (UI) repository %s with source URL %s, id:%s" % (repo.name, repo.mirrorlist or repo.baseurl, repo.id)) - except (IOError, yum.Errors.RepoError) as e: - anaconda.intf.messageWindow(_("Error"), - _("Unable to read package metadata from repository. " - "This may be due to a missing repodata directory. " - "Please ensure that your repository has been " - "correctly generated.\n\n%s" % str(e)), - type="ok", custom_icon="error") - return False - - return True +from pyanaconda.yuminstall import RepoSpec
class RepoEditor: # Window-level callbacks @@ -73,16 +42,13 @@ class RepoEditor: pass
def on_typeComboBox_changed(self, widget, *args): - if widget.get_active() == -1: + iter = widget.get_active_iter() + if iter == None: return
- # When the combo box's value is changed, set the notebook's current - # page to match. This requires that the combo box and notebook have - # the same method types at the same indices (so, HTTP must be the - # same position on both, etc.). - self.notebook.set_current_page(widget.get_active()) + self.notebook.set_current_page(widget.get_model().get_value(iter, 3))
- if widget.get_active() == 1: + if widget.get_model().get_value(iter, 1) == "http": if self.repo: self.proxyCheckbox.set_active(self.repo.proxy is True) self.proxyTable.set_sensitive(self.repo.proxy is True) @@ -98,11 +64,10 @@ class RepoEditor: def on_mirrorlistCheckbox_toggled(self, widget, *args): pass
- def __init__(self, anaconda, repoObj): - self.anaconda = anaconda - self.backend = self.anaconda.backend - self.intf = self.anaconda.intf - self.repo = repoObj + def __init__(self, backend, intf, repoSpec): + self.backend = backend + self.intf = intf + self.repo = repoSpec
(self.dxml, self.dialog) = gui.getGladeWidget("addrepo.glade", "addRepoDialog") self.dxml.signal_autoconnect(self) @@ -128,41 +93,43 @@ class RepoEditor:
self.dialog.set_title(_("Edit Repository"))
- # Remove these until they are actually implemented - self.typeComboBox.remove_text(3) - - # Given a method string, return the index of the typeComboBox that should - # be made active in order to match. - def _methodToIndex(self, method): - mapping = {"http": 0, "ftp": 0, "https": 0, - "cdrom": 1, - "nfs": 2} -# "nfs": 2, "nfsiso": 2, -# "hd": 3} - - try: - return mapping[method.split(':')[0].lower()] - except: - return 0 - - def _addAndEnableRepo(self, repo): - try: - self.backend.ayum.repos.add(repo) - except yum.Errors.DuplicateRepoError, e: - self.intf.messageWindow(_("Error"), - _("The repository %s has already been added. Please " - "choose a different repository name and " - "URL.") % repo.name, type="ok", custom_icon="error") - return False - - repo.enable() - return True + self._setupTypeCombobox(repoSpec.type)
- def _disableAndRemoveRepo(self, repo): - repo.disable() - repo.close() - self.anaconda.backend.ayum.repos.delete(repo.id) + def _setupTypeCombobox(self, repotype): + store = gtk.ListStore(gobject.TYPE_STRING, + gobject.TYPE_STRING, + gobject.TYPE_PYOBJECT, + gobject.TYPE_INT) + self.typeComboBox.set_model(store) + store.append(["HTTP/FTP", "http", self._applyURL, 0]) + store.append(["NFS", "nfs", self._applyNfs, 2]) + if repotype == "method": + store.append(["CD/DVD", "cdrom", self._applyMedia, 1]) + #store.append(["HD", "hd", self._applyHd, 3]) + + def _urlToMethod(self, url): + d = {"http":"http", + "ftp":"http", + "cdrom":"cdrom", + "nfs":"nfs", + "hd":"hd"} + for prefix in d: + if url.startswith(prefix): + return d[prefix] + else: + return "http"
+ # Given a method string, return the iter of the typeComboBox that should + # be made active in order to match. + def _urlToIter(self, url): + method = self._urlToMethod(url) + model = self.typeComboBox.get_model() + iter = model.get_iter_first() + while iter: + if method == model.get_value(iter, 1): + return iter + iter = model.iter_next(iter) + return model.get_iter_first()
def _validURL(self, url): return len(url) > 0 and (url.startswith("http://") or @@ -171,22 +138,14 @@ class RepoEditor:
def createDialog(self):
- if self.repo: + if self.repo.url: self.nameEntry.set_text(self.repo.name) - if self.repo.anacondaBaseURLs: - url = self.repo.anacondaBaseURLs[0] - else: - url = '' - self.typeComboBox.set_active(self._methodToIndex(url)) + url = self.repo.url + self.typeComboBox.set_active_iter(self._urlToIter(url))
- if not url or url.startswith("http") or url.startswith("ftp"): - if self.repo.mirrorlist: - self.baseurlEntry.set_text(self.repo.mirrorlist) - self.mirrorlistCheckbox.set_active(True) - else: - self.baseurlEntry.set_text(url) - - self.mirrorlistCheckbox.set_active(False) + if url.startswith("http") or url.startswith("ftp"): + self.baseurlEntry.set_text(url) + self.mirrorlistCheckbox.set_active(self.repo.mirrorlist)
if self.repo.proxy: self.proxyCheckbox.set_active(True) @@ -213,7 +172,8 @@ class RepoEditor: (device, fstype, path) = m.split(":")
# find device in self.partitionComboBox and select it - self.directoryChooser.set_current_folder("%s%s" % (self.anaconda.backend.ayum.isodir, path)) + # TODORV isodir is a problem + self.directoryChooser.set_current_folder("%s%s" % (self.backend.ayum.isodir, path)) else: self.baseurlEntry.set_text(url)
@@ -230,7 +190,15 @@ class RepoEditor:
self.dialog.show_all()
- def _applyURL(self, repo): + def _applyURL(self): + repourl = self.baseurlEntry.get_text() + repourl.strip() + if not self._validURL(repourl): + self.intf.messageWindow(_("Invalid Repository URL"), + _("You must provide an HTTP, HTTPS, " + "or FTP URL to a repository.")) + return False + if self.proxyCheckbox.get_active(): proxy = self.proxyEntry.get_text() proxy.strip() @@ -241,55 +209,24 @@ class RepoEditor: "or FTP URL to a proxy.")) return False
- repo.proxy = proxy - # with empty string yum would create invalid proxy string - repo.proxy_username = self.usernameEntry.get_text() or None - repo.proxy_password = self.passwordEntry.get_text() or None + self.repo.proxy = proxy + self.repo.proxy_username = self.usernameEntry.get_text() + self.repo.proxy_password = self.passwordEntry.get_text()
- repourl = self.baseurlEntry.get_text() - repourl.strip() - if not self._validURL(repourl): - self.intf.messageWindow(_("Invalid Repository URL"), - _("You must provide an HTTP, HTTPS, " - "or FTP URL to a repository.")) - return False
+ self.repo.url = repourl if self.mirrorlistCheckbox.get_active(): - repo.baseurl = [] - repo.mirrorlist = repourl - else: - repo.baseurl = [repourl] - repo.mirrorlist = None - repo.anacondaBaseURLs = repo.baseurl - - repo.name = self.nameEntry.get_text() + self.repo.mirrorlist = True + self.repo.name = self.nameEntry.get_text()
return True
- def _applyMedia(self, repo): - # FIXME works only if storage has detected format of cdrom drive - ayum = self.anaconda.backend.ayum - cdr = scanForMedia(ayum.tree, self.anaconda.storage) - if not cdr: - self.intf.messageWindow(_("No Media Found"), - _("No installation media was found. " - "Please insert a disc into your drive " - "and try again.")) - return False - - log.info("found installation media on %s" % cdr) - repo.name = self.nameEntry.get_text() - repo.anacondaBaseURLs = ["cdrom://%s:%s" % (cdr, self.anaconda.backend.ayum.tree)] - repo.baseurl = "file://%s" % ayum.tree - ayum.mediagrabber = ayum.mediaHandler - self.anaconda.mediaDevice = cdr - ayum.currentMedia = 1 - repo.mediaid = getMediaId(ayum.tree) - log.info("set mediaid of repo %s to: %s" % (repo.name, repo.mediaid)) - + def _applyMedia(self): + self.repo.name = self.nameEntry.get_text() + self.repo.url = "cdrom://" return True
- def _applyNfs(self, repo): + def _applyNfs(self): server = self.nfsServerEntry.get_text() server.strip()
@@ -299,221 +236,64 @@ class RepoEditor: options = self.nfsOptionsEntry.get_text() options.strip()
- repo.name = self.nameEntry.get_text() - if not server or not path: self.intf.messageWindow(_("Error"), _("Please enter an NFS server and path.")) return False
- if not network.hasActiveNetDev(): - if not self.anaconda.intf.enableNetwork(): - self.intf.messageWindow(_("No Network Available"), - _("Some of your software repositories require " - "networking, but there was an error enabling the " - "network on your system.")) - return False - urlgrabber.grabber.reset_curl_obj() - - import tempfile - dest = tempfile.mkdtemp("", repo.name.replace(" ", ""), "/mnt") - - try: - isys.mount("%s:%s" % (server, path), dest, "nfs", options=options) - except Exception as e: - self.intf.messageWindow(_("Error Setting Up Repository"), - _("The following error occurred while setting up the " - "repository:\n\n%s") % e) - return False - - repo.baseurl = "file://%s" % dest - repo.anacondaBaseURLs = ["nfs:%s:%s:%s" % (options,server,path)] + self.repo.name = self.nameEntry.get_text() + self.repo.url = "nfs:%s:%s:%s" % (options,server,path) return True
- def _applyHd(self, repo): + def _applyHd(self): return True
def run(self): - applyFuncs = [ self._applyURL, self._applyMedia, self._applyNfs, - self._applyHd ]
+ repo = None while True: rc = self.dialog.run() if rc in [gtk.RESPONSE_CANCEL, gtk.RESPONSE_DELETE_EVENT]: break - - reponame = self.nameEntry.get_text() - reponame.strip() - if len(reponame) == 0: - self.intf.messageWindow(_("Invalid Repository Name"), - _("You must provide a repository name.")) - continue - - # Always create a new repo object here instead of attempting to - # somehow expire the metadata and refetch. We'll just have to make - # sure that if we're just editing the repo, we grab all the - # attributes from the old one before deleting it. - if self.repo: - # use temporary id so that we don't get Duplicate Repo error - # when adding - newRepoObj = AnacondaYumRepo("UIedited_%s" % - self.anaconda.backend.ayum.repoIDcounter.next()) - newRepoObj.cost = self.repo.cost - removeOld = True else: - newRepoObj = AnacondaYumRepo(reponame.replace(" ", "")) - removeOld = False - - # corresponds to self.repos.setCacheDir in AnacondaYum.doConfigSetup - newRepoObj.basecachedir = self.anaconda.backend.ayum.conf.cachedir - - type = self.typeComboBox.get_active() - if (not applyFuncs[type](newRepoObj) or - not self._addAndEnableRepo(newRepoObj)): - continue - if not setupRepo(self.anaconda, newRepoObj): - self._disableAndRemoveRepo(newRepoObj) - continue - - if removeOld: - try: - os.unlink("%s/cachecookie" % self.repo.cachedir) - os.unlink("%s/repomd.xml" % self.repo.cachedir) - except: - pass - - self.repo.disable() - self.repo.close() - self.anaconda.backend.ayum.repos.delete(self.repo.id) - log.info("deleted (UI) repository %s with source URL %s, id:%s" - % (self.repo.name, self.repo.mirrorlist or self.repo.baseurl, self.repo.id)) - try: - shutil.rmtree(self.repo.cachedir) - except Exception as e: - log.warning("error removing cachedir for %s: %s" %(self.repo, e)) - pass - - if (newRepoObj.enablegroups or - (removeOld and self.repo.enablegroups)): - # update groups information - try: - self.anaconda.backend.ayum.doGroupSetup() - except Exception as e: - log.debug("unable to reset group information after UI repo edit: %s" - % e) - else: - log.info("group information reset after UI repo edit") - - self.repo = newRepoObj - break - - self.dialog.hide() - return rc - -class RepoMethodstrEditor(RepoEditor): - def __init__(self, anaconda, methodstr): - # Create temporary repo to store methodstr needed for - # createDialog parent method. - temprepo = AnacondaYumRepo("UITmpMethodstrRepo") - temprepo.name = "Installation Repo" - temprepo.anacondaBaseURLs = [methodstr] - RepoEditor.__init__(self, anaconda, temprepo) - - def createDialog(self): - RepoEditor.createDialog(self) - - # Hide a bunch of stuff that doesn't apply when we're just prompting - # for enough information to form a methodstr. - self.nameEntry.set_sensitive(False) - self.mirrorlistCheckbox.hide() - self.proxyCheckbox.hide() - self.proxyTable.hide() - - def _applyURL(self): - repourl = self.baseurlEntry.get_text() - repourl.strip() - if not self._validURL(repourl): - self.intf.messageWindow(_("Invalid Repository URL"), - _("You must provide an HTTP, HTTPS, " - "or FTP URL to a repository.")) - return False - - return repourl - - def _applyMedia(self): - cdr = scanForMedia(self.anaconda.backend.ayum.tree, self.anaconda.storage) - if not cdr: - self.intf.messageWindow(_("No Media Found"), - _("No installation media was found. " - "Please insert a disc into your drive " - "and try again.")) - return False - - self.anaconda.backend.ayum.mediagrabber = self.anaconda.backend.ayum.mediaHandler - self.anaconda.backend.ayum.anaconda.mediaDevice = cdr - self.anaconda.backend.ayum.currentMedia = 1 - log.info("found installation media on %s" % cdr) - return "cdrom://%s:%s" % (cdr, self.anaconda.backend.ayum.tree) - - def _applyNfs(self): - server = self.nfsServerEntry.get_text() - server.strip() - - path = self.nfsPathEntry.get_text() - path.strip() - - options = self.nfsOptionsEntry.get_text() - options.strip() - - if not server or not path: - self.intf.messageWindow(_("Error"), - _("Please enter an NFS server and path.")) - return False - - return "nfs:%s:%s:%s" % (options, server, path) - - def _applyHd(self): - return None - - def run(self): - applyFuncs = [ self._applyURL, self._applyMedia, self._applyNfs, - self._applyHd ] - - while True: - rc = self.dialog.run() - if rc in [gtk.RESPONSE_CANCEL, gtk.RESPONSE_DELETE_EVENT]: - rc = None - break - - type = self.typeComboBox.get_active() - retval = applyFuncs[type]() - if not retval: - continue - - rc = retval - break - - self.dialog.hide() - return rc + active = self.typeComboBox.get_active_iter() + apply = self.typeComboBox.get_model().get_value(active, 2) + if apply(): + repo = self.repo + break + self.dialog.destroy() + return repo
class RepoCreator(RepoEditor): - def __init__(self, anaconda): - RepoEditor.__init__(self, anaconda, None) + def __init__(self, backend, intf): + repo = RepoSpec() + repo.type = "UI" + RepoEditor.__init__(self, backend, intf, repo) self.dialog.set_title(_("Add Repository"))
class TaskWindow(InstallWindow): def getNext(self): - if not self._anyRepoEnabled(): - self.anaconda.intf.messageWindow(_("No Software Repos Enabled"), + repos = self._getRepos() + + if not [repo for repo in repos if repo.enabled]: + self.intf.messageWindow(_("No Software Repos Enabled"), _("You must have at least one software repository enabled to " "continue installation.")) raise gui.StayOnScreen
+ if [repo for repo in repos if repo.enabled and not repo.setup == "OK"]: + self.intf.messageWindow(_("Enabled Repo Not Set Up"), + _("All enabled repos must be set up to " + "continue installation.")) + raise gui.StayOnScreen + if self.xml.get_widget("customRadio").get_active(): self.dispatch.skipStep("group-selection", skip = 0) else: self.dispatch.skipStep("group-selection", skip = 1)
+ self.anaconda.repos = repos + tasks = self.xml.get_widget("taskList").get_model() for (cb, task, grps) in filter(lambda x: not x[0], tasks): map(lambda g: setattr(self.backend.ayum.comps.return_group(g), @@ -522,42 +302,35 @@ class TaskWindow(InstallWindow): map(lambda g: setattr(self.backend.ayum.comps.return_group(g), "default", True), grps)
- def _editRepo(self, *args): - repo = None + def _editRepo(self, widget, repolist):
- # If we were passed an extra argument, it's the repo store and we - # are editing an existing repo as opposed to adding a new one. - if len(args) > 1: - (model, iter) = args[1].get_selection().get_selected() - if iter: - repo = model.get_value(iter, 2) - else: - return + (model, iter) = repolist.get_selection().get_selected() + if iter: + repo = model.get_value(iter, 3) else: return
- if repo.needsNetwork() and not network.hasActiveNetDev(): - if not self.anaconda.intf.enableNetwork(): - return gtk.RESPONSE_CANCEL - - urlgrabber.grabber.reset_curl_obj() - - dialog = RepoEditor(self.anaconda, repo) + dialog = RepoEditor(self.backend, self.intf, repo) dialog.createDialog() - dialog.run() - - model.set_value(iter, 0, dialog.repo.isEnabled()) - model.set_value(iter, 1, dialog.repo.name) - model.set_value(iter, 2, dialog.repo) - - def _addRepo(self, *args): - dialog = RepoCreator(self.anaconda) + if dialog.run(): + repo.setup = "?" + model.set_value(iter, 0, repo.enabled) + model.set_value(iter, 1, self._resultPixbuf(repo.setup)) + model.set_value(iter, 2, repo.name) + if repo.enabled: + self._updateTaskStore() + + def _addRepo(self, widget, repos, repolist): + dialog = RepoCreator(self.backend, self.intf) dialog.createDialog() - if dialog.run() in [gtk.RESPONSE_CANCEL, gtk.RESPONSE_DELETE_EVENT]: - return gtk.RESPONSE_CANCEL - - s = self.xml.get_widget("repoList").get_model() - s.append([dialog.repo.isEnabled(), dialog.repo.name, dialog.repo]) + newRepo = dialog.run() + if newRepo: + newRepo.enabled = True + s = repolist.get_model() + repos.append(newRepo) + s.append([newRepo.enabled, self._resultPixbuf(newRepo.setup), + newRepo.name, newRepo]) + self._updateTaskStore()
def _taskToggled(self, button, path, store): # First, untoggle everything in the store. @@ -567,44 +340,64 @@ class TaskWindow(InstallWindow): # Then, enable the one that was clicked. store[path][0] = True
- def _anyRepoEnabled(self): - model = self.rs.get_model() - iter = model.get_iter_first() + def _repoToggled(self, button, row, store): + i = store.get_iter(int(row)) + wasChecked = store.get_value(i, 0) + repo = store.get_value(i, 3) + repo.enabled = not wasChecked + store.set_value(i, 0, not wasChecked) + self._updateTaskStore()
- while True: - if model.get_value(iter, 0): - return True + def _getRepos(self): + repos = [] + model = self.rl.get_model() + iter = model.get_iter_first() + while iter: + repos.append(model.get_value(iter, 3)) + iter = model.iter_next(iter) + return repos
+ def _updateSetupResults(self, repos): + model = self.rl.get_model() + iter = model.get_iter_first() + while iter: + repo = model.get_value(iter, 3) + model.set_value(iter, 0, repo.enabled) + model.set_value(iter, 1, self._resultPixbuf(repo.setup)) iter = model.iter_next(iter) - if not iter: - return False
- return False + def _updateTaskStore(self): + """Update task list for new repos selection""" + # This should go away when task selection is + # moved to groups selection screen + store = self.tl.get_model() + store.clear() + + self.anaconda.repos = self._getRepos() + rc = self.backend.doBackendSetup(self.anaconda, + called_from_repotasksel=True) + self._updateSetupResults(self.anaconda.repos) + if rc == DISPATCH_BACK: + self.tl.set_sensitive(False) + return False + else: + self.tl.set_sensitive(True)
- def _repoToggled(self, button, row, store): - i = store.get_iter(int(row)) - wasChecked = store.get_value(i, 0) - repo = store.get_value(i, 2) + anyEnabled = False
- if not wasChecked: - if repo.needsNetwork() and not network.hasActiveNetDev(): - if not self.anaconda.intf.enableNetwork(): - return + for (txt, grps) in self.tasks: + if not self.backend.groupListExists(grps): + continue
- urlgrabber.grabber.reset_curl_obj() + enabled = self.backend.groupListDefault(grps) + store.append([not anyEnabled and enabled, _(txt), grps])
- repo.enable() - if not setupRepo(self.anaconda, repo): - repo.disable() - repo.close() - return - else: - repo.disable() - repo.close() + if enabled: + anyEnabled = True
- store.set_value(i, 0, not wasChecked) + return True
- def _createTaskStore(self): + def _createTaskList(self): store = gtk.ListStore(gobject.TYPE_BOOLEAN, gobject.TYPE_STRING, gobject.TYPE_PYOBJECT) @@ -622,37 +415,36 @@ class TaskWindow(InstallWindow): col.set_clickable(False) tl.append_column(col)
- anyEnabled = False - - for (txt, grps) in self.tasks: - if not self.backend.groupListExists(grps): - continue - - enabled = self.backend.groupListDefault(grps) - store.append([not anyEnabled and enabled, _(txt), grps]) - - if enabled: - anyEnabled = True - return tl
def __sortRepos(self, store, aIter, bIter): - aStr = store.get_value(aIter, 1) - bStr = store.get_value(bIter, 1) + aRepo = store.get_value(aIter, 3) + bRepo = store.get_value(bIter, 3)
- if aStr == "Installation Repo": + if bRepo is None: + return -1 + elif aRepo is None: + return 1 + elif aRepo.type == "method": return -1 - elif bStr == "Installation Repo": + elif bRepo.type == "method": return 1 - elif aStr < bStr or bStr is None: + elif aRepo.type == "config": return -1 - elif aStr > bStr or aStr is None: + elif bRepo.type == "config": return 1 + elif aRepo.name <= bRepo.name: + return -1 else: - return aStr == bStr + return 1 + + def _resultPixbuf(self, result): + return self.rl.render_icon(self.setup_icons[result], + size = gtk.ICON_SIZE_MENU)
- def _createRepoStore(self): + def _createRepoList(self, repos): store = gtk.ListStore(gobject.TYPE_BOOLEAN, + gtk.gdk.Pixbuf, gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)
@@ -664,26 +456,32 @@ class TaskWindow(InstallWindow): cbr.connect("toggled", self._repoToggled, store) tl.append_column(col)
- col = gtk.TreeViewColumn('Text', gtk.CellRendererText(), text = 1) + col = gtk.TreeViewColumn('Set Up', gtk.CellRendererPixbuf(), pixbuf = 1) col.set_clickable(False) tl.append_column(col)
- for (reponame, repo) in self.repos.repos.items(): - store.append([repo.isEnabled(), repo.name, repo]) + col = gtk.TreeViewColumn('Name', gtk.CellRendererText(), text = 2) + col.set_clickable(False) + tl.append_column(col)
store.set_sort_column_id(1, gtk.SORT_ASCENDING) store.set_sort_func(1, self.__sortRepos)
return tl
+ def getScreen (self, anaconda): self.intf = anaconda.intf self.dispatch = anaconda.dispatch self.backend = anaconda.backend self.anaconda = anaconda
+ self.setup_icons = {"?" : gtk.STOCK_DISCARD, + "OK" : gtk.STOCK_YES, + "failed": gtk.STOCK_NO} + self.tasks = anaconda.instClass.tasks - self.repos = anaconda.backend.ayum.repos + self.repos = anaconda.repos
(self.xml, vbox) = gui.getGladeWidget("tasksel.glade", "taskBox")
@@ -700,14 +498,24 @@ class TaskWindow(InstallWindow): else: self.xml.get_widget("customRadio").set_active(False)
- self.ts = self._createTaskStore() - self.rs = self._createRepoStore() + self.tl = self._createTaskList() + # TODORV: we may need separate list for base repo with addons + self.rl = self._createRepoList(self.repos)
- if len(self.ts.get_model()) == 0: + for repo in self.repos: + self.rl.get_model().append([repo.enabled, + self._resultPixbuf(repo.setup), + repo.name, repo]) + + if len(self.tl.get_model()) == 0: self.xml.get_widget("cbVBox").hide() self.xml.get_widget("mainLabel").hide()
- self.xml.get_widget("addRepoButton").connect("clicked", self._addRepo) - self.xml.get_widget("editRepoButton").connect("clicked", self._editRepo, self.rs) + self.xml.get_widget("addRepoButton").connect("clicked", self._addRepo, + self.repos, self.rl) + self.xml.get_widget("editRepoButton").connect("clicked", self._editRepo, + self.rl) + + self._updateTaskStore()
return vbox diff --git a/pyanaconda/yuminstall.py b/pyanaconda/yuminstall.py index 3b9f765..551cf0d 100644 --- a/pyanaconda/yuminstall.py +++ b/pyanaconda/yuminstall.py @@ -1229,9 +1229,29 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon self.anaconda.repos.append(rs) ayum.readReposFromConfig = False
- def doBackendSetup(self, anaconda): - if anaconda.dir == DISPATCH_BACK: - return DISPATCH_BACK + + def doBackendSetup(self, anaconda, called_from_repotasksel=False): + + # This is hack needed because of task selection being + # on the same screen as repo selection + if called_from_repotasksel: + # called after reposetup step fail or UI back + if anaconda.dir == DISPATCH_BACK: + anaconda.dir = DISPATCH_FORWARD + # after fail we don't want to try again right away + if self.ayum: + self.ayum.close() + self.ayum = None + return DISPATCH_BACK + else: + if anaconda.dir == DISPATCH_BACK: + self.ayum.close() + self.ayum = None + return DISPATCH_BACK + else: + # already set up in repotasksel + if self.ayum: + return
if anaconda.upgrade: # FIXME: make sure that the rpmdb doesn't have stale locks :/
--- pyanaconda/__init__.py | 2 +- pyanaconda/backend.py | 2 +- pyanaconda/yuminstall.py | 20 ++++++++++---------- 3 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/pyanaconda/__init__.py b/pyanaconda/__init__.py index 9d94519..39fb53c 100644 --- a/pyanaconda/__init__.py +++ b/pyanaconda/__init__.py @@ -400,7 +400,7 @@ class Anaconda(object): self.bootloader.writeKS(f)
if self.backend: - self.backend.writeKS(f) + self.backend.writeKS(f, self.repos) self.backend.writePackagesKS(f, self)
# Also write out any scripts from the input ksfile. diff --git a/pyanaconda/backend.py b/pyanaconda/backend.py index f7c9078..6166d2c 100644 --- a/pyanaconda/backend.py +++ b/pyanaconda/backend.py @@ -255,7 +255,7 @@ class AnacondaBackend:
# write out any other kickstart bits the backend requires - no warning # here because this may not be needed - def writeKS(self, f): + def writeKS(self, f, anaconda): pass
def getRequiredMedia(self): diff --git a/pyanaconda/yuminstall.py b/pyanaconda/yuminstall.py index 551cf0d..47ba336 100644 --- a/pyanaconda/yuminstall.py +++ b/pyanaconda/yuminstall.py @@ -2041,22 +2041,22 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon
line = "repo --name="%s" " % (repo.name or repo.repoid)
- if repo.baseurl: - line += " --baseurl=%s" % repo.anacondaBaseURLs[0] + if repo.mirrorlist: + line += " --mirrorlist=%s" % repo.url else: - line += " --mirrorlist=%s" % repo.mirrorlist + line += " --baseurl=%s" % repo.url
- if repo.proxy: + if repo.yumrepo.proxy: line += " --proxy="%s"" % repo.proxy_dict['http']
- if repo.cost: - line += " --cost=%s" % repo.cost + if repo.yumrepo.cost: + line += " --cost=%s" % repo.yumrepo.cost
- if repo.includepkgs: - line += " --includepkgs="%s"" % ",".join(repo.includepkgs) + if repo.yumrepo.includepkgs: + line += " --includepkgs="%s"" % ",".join(repo.yumrepo.includepkgs)
- if repo.exclude: - line += " --excludepkgs="%s"" % ",".join(repo.exclude) + if repo.yumrepo.exclude: + line += " --excludepkgs="%s"" % ",".join(repo.yumrepo.exclude)
line += "\n"
--- pyanaconda/yuminstall.py | 22 +++++----------------- 1 files changed, 5 insertions(+), 17 deletions(-)
diff --git a/pyanaconda/yuminstall.py b/pyanaconda/yuminstall.py index 47ba336..b6f5074 100644 --- a/pyanaconda/yuminstall.py +++ b/pyanaconda/yuminstall.py @@ -128,8 +128,6 @@ class RepoSpec(object): id += "-%s" % productStamp return id
- - # TODORV: remove from AnacondaYumRepo? def needsNetwork(self): return (self.url.startswith("http") or self.url.startswith("ftp:") or @@ -512,7 +510,7 @@ class AnacondaYumRepo(YumRepository): YumRepository.__init__(self, *args, **kwargs) self.enablegroups = True self.sslverify = True - self._anacondaBaseURLs = [] + self.repoSpec = None
def needsNetwork(self): def _isURL(s): @@ -537,17 +535,6 @@ class AnacondaYumRepo(YumRepository): if os.path.exists("%s/packages" % cachedir): shutil.rmtree("%s/packages" % cachedir)
- # needed to store nfs: repo url that yum doesn't know - def _getAnacondaBaseURLs(self): - return self._anacondaBaseURLs or self.baseurl or [self.mirrorlist] - - def _setAnacondaBaseURLs(self, value): - self._anacondaBaseURLs = value - - anacondaBaseURLs = property(_getAnacondaBaseURLs, _setAnacondaBaseURLs, - doc="Extends AnacondaYum.baseurl to store non-yum urls:") - - class YumSorter(yum.YumBase): def _transactionDataFactory(self): return SplitMediaTransactionData() @@ -774,7 +761,7 @@ class AnacondaYum(YumSorter): continue
def _handleFailure(self, package): - if package.repo.anacondaBaseURLs[0].startswith("cdrom:"): + if package.repo.repoSpec.url.startswith("cdrom:"): buttons = [_("Re_boot"), _("_Eject")] else: buttons = [_("Re_boot"), _("_Retry")] @@ -796,7 +783,7 @@ class AnacondaYum(YumSorter): if os.path.exists(package.localPkg()): os.unlink(package.localPkg())
- if package.repo.anacondaBaseURLs[0].startswith("cdrom:"): + if package.repo.repoSpec.url.startswith("cdrom:"): self._switchCD(self.currentMedia) else: return @@ -812,7 +799,7 @@ class AnacondaYum(YumSorter): "or downloaded file is corrupt" % (obj.url, repo.grab._next + 1, len(repo.grab.mirrors)))
- if repo.anacondaBaseURLs[0].startswith("cdrom:"): + if repo.repoSpec.url.startswith("cdrom:"): dev = self.anaconda.storage.devicetree.getDeviceByName(self.anaconda.mediaDevice) dev.format.mountpoint = self.tree unmountCD(dev, self.anaconda.intf.messageWindow) @@ -1348,6 +1335,7 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon repo.initYumRepo(yumrepo) yumrepo.enable() repo.yumrepo = yumrepo + yumrepo.repoSpec = repo try: ayum.repos.add(yumrepo) except yum.Errors.DuplicateRepoError, e:
--- pyanaconda/yuminstall.py | 25 +------------------------ 1 files changed, 1 insertions(+), 24 deletions(-)
diff --git a/pyanaconda/yuminstall.py b/pyanaconda/yuminstall.py index b6f5074..3ddf6b2 100644 --- a/pyanaconda/yuminstall.py +++ b/pyanaconda/yuminstall.py @@ -512,29 +512,6 @@ class AnacondaYumRepo(YumRepository): self.sslverify = True self.repoSpec = None
- def needsNetwork(self): - def _isURL(s): - return s.startswith("http") or s.startswith("ftp") - - if len(self.baseurl) > 0: - return len(filter(lambda s: _isURL(s), self.baseurl)) > 0 - elif self.mirrorlist: - return _isURL(self.mirrorlist) - else: - return False - - def dirCleanup(self): - cachedir = self.getAttribute('cachedir') - - if os.path.isdir(cachedir): - if not self.needsNetwork() or self.name == "Installation Repo" or self.id.startswith("anaconda-"): - shutil.rmtree(cachedir) - else: - if os.path.exists("%s/headers" % cachedir): - shutil.rmtree("%s/headers" % cachedir) - if os.path.exists("%s/packages" % cachedir): - shutil.rmtree("%s/packages" % cachedir) - class YumSorter(yum.YumBase): def _transactionDataFactory(self): return SplitMediaTransactionData() @@ -1871,7 +1848,7 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon
packages.rpmSetupGraphicalSystem(anaconda)
- for repo in self.ayum.repos.listEnabled(): + for repo in anaconda.repos: repo.dirCleanup()
# expire yum caches on upgrade
With new flow, we don't need this because we start with new YumBackend after going [Back]. --- pyanaconda/backend.py | 4 ++-- pyanaconda/textw/task_text.py | 1 - pyanaconda/yuminstall.py | 8 -------- 3 files changed, 2 insertions(+), 11 deletions(-)
diff --git a/pyanaconda/backend.py b/pyanaconda/backend.py index 6166d2c..2f4cecc 100644 --- a/pyanaconda/backend.py +++ b/pyanaconda/backend.py @@ -290,11 +290,11 @@ def doInstall(anaconda):
# does this need to be per-backend? we'll just leave here until it does :) def doBasePackageSelect(anaconda): + if anaconda.dir == DISPATCH_BACK: + return DISPATCH_BACK if anaconda.ksdata: - anaconda.backend.resetPackageSelections() kickstart.selectPackages(anaconda) elif anaconda.displayMode != 't': - anaconda.backend.resetPackageSelections() anaconda.instClass.setPackageSelection(anaconda) anaconda.instClass.setGroupSelection(anaconda)
diff --git a/pyanaconda/textw/task_text.py b/pyanaconda/textw/task_text.py index 416e572..3dd9290 100644 --- a/pyanaconda/textw/task_text.py +++ b/pyanaconda/textw/task_text.py @@ -22,7 +22,6 @@ from pyanaconda.constants import *
class TaskWindow: def __call__(self, screen, anaconda): - anaconda.backend.resetPackageSelections() anaconda.backend.selectGroup("Core")
return INSTALL_OK diff --git a/pyanaconda/yuminstall.py b/pyanaconda/yuminstall.py index 3ddf6b2..c94f14c 100644 --- a/pyanaconda/yuminstall.py +++ b/pyanaconda/yuminstall.py @@ -1424,14 +1424,6 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon rc.append(g.groupid) return rc
- def resetPackageSelections(self): - """Reset the package selection to an empty state.""" - for txmbr in self.ayum.tsInfo: - self.ayum.tsInfo.remove(txmbr.pkgtup) - self.ayum.tsInfo.conditionals.clear() - for grp in self.ayum.comps.groups: - grp.selected = False - def selectModulePackages(self, anaconda, kernelPkgName): (base, sep, ext) = kernelPkgName.partition("-")
Also refactor some mounting fuctions. --- data/ui/addrepo.glade | 1 + pyanaconda/image.py | 45 ----------------- pyanaconda/iutil.py | 13 +++++ pyanaconda/iw/task_gui.py | 10 ++++- pyanaconda/yuminstall.py | 116 ++++++++++++++++++++++++++++++++------------ 5 files changed, 107 insertions(+), 78 deletions(-)
diff --git a/data/ui/addrepo.glade b/data/ui/addrepo.glade index 7a6637e..69cab86 100644 --- a/data/ui/addrepo.glade +++ b/data/ui/addrepo.glade @@ -194,6 +194,7 @@ <property name="items" translatable="yes">HTTP/FTP CD/DVD NFS +NFS ISO Hard Drive</property> <property name="add_tearoffs">False</property> <property name="focus_on_click">True</property> diff --git a/pyanaconda/image.py b/pyanaconda/image.py index 9341192..84c5511 100644 --- a/pyanaconda/image.py +++ b/pyanaconda/image.py @@ -122,51 +122,6 @@ def getMediaId(path): else: return None
-# This mounts the directory containing the iso images, and places the -# mount point in /mnt/isodir. -def mountDirectory(methodstr, messageWindow): - if methodstr.startswith("hd:"): - method = methodstr[3:] - if method.count(":") == 1: - (device, path) = method.split(":") - fstype = "auto" - else: - (device, fstype, path) = method.split(":") - - if not device.startswith("/dev/") and not device.startswith("UUID=") \ - and not device.startswith("LABEL="): - device = "/dev/%s" % device - elif methodstr.startswith("nfsiso:"): - device = methodstr[7:] - fstype = "nfs" - else: - return - - # No need to mount it again. - if os.path.ismount("/mnt/isodir"): - return - - while True: - try: - isys.mount(device, "/mnt/isodir", fstype = fstype) - break - except SystemError, msg: - log.error("couldn't mount ISO source directory: %s" % msg) - ans = messageWindow(_("Couldn't Mount ISO Source"), - _("An error occurred mounting the source " - "device %s. This may happen if your ISO " - "images are located on an advanced storage " - "device like LVM or RAID, or if there was a " - "problem mounting a partition. Click exit " - "to abort the installation.") - % (device,), type="custom", custom_icon="error", - custom_buttons=[_("_Exit"), _("_Retry")]) - - if ans == 0: - sys.exit(0) - else: - continue - def mountImage(isodir, tree, discnum, messageWindow, discImages={}): if os.path.ismount(tree): raise SystemError, "trying to mount already-mounted iso image!" diff --git a/pyanaconda/iutil.py b/pyanaconda/iutil.py index 9a1c142..3aacd12 100644 --- a/pyanaconda/iutil.py +++ b/pyanaconda/iutil.py @@ -1074,6 +1074,19 @@ def parseNfsUrl(nfsurl): host = s[0] return (options, host, path)
+def parseHdUrl(url): + device = '' + fstype = '' + path = '' + if url: + s = url.split(":") + s.pop(0) + if len(s) >= 3: + (device, fstype, path) = s[:3] + elif len(2) == 2: + (device, path) = s + return (device, fstype, path) + def add_po_path(module, dir): """ Looks to see what translations are under a given path and tells the gettext module to use that path as the base dir """ diff --git a/pyanaconda/iw/task_gui.py b/pyanaconda/iw/task_gui.py index 3a6392e..5da1bd1 100644 --- a/pyanaconda/iw/task_gui.py +++ b/pyanaconda/iw/task_gui.py @@ -104,6 +104,7 @@ class RepoEditor: store.append(["HTTP/FTP", "http", self._applyURL, 0]) store.append(["NFS", "nfs", self._applyNfs, 2]) if repotype == "method": + store.append(["NFS ISO", "nfsiso", self._applyNfsIso, 2]) store.append(["CD/DVD", "cdrom", self._applyMedia, 1]) #store.append(["HD", "hd", self._applyHd, 3])
@@ -111,7 +112,8 @@ class RepoEditor: d = {"http":"http", "ftp":"http", "cdrom":"cdrom", - "nfs":"nfs", + "nfsiso":"nfsiso", + "nfs:":"nfs", "hd":"hd"} for prefix in d: if url.startswith(prefix): @@ -245,6 +247,12 @@ class RepoEditor: self.repo.url = "nfs:%s:%s:%s" % (options,server,path) return True
+ def _applyNfsIso(self): + if not self._applyNfs(): + return False + self.repo.url = "nfsiso" + self.repo.url[3:] + return True + def _applyHd(self): return True
diff --git a/pyanaconda/yuminstall.py b/pyanaconda/yuminstall.py index c94f14c..84e6aca 100644 --- a/pyanaconda/yuminstall.py +++ b/pyanaconda/yuminstall.py @@ -234,24 +234,90 @@ class YumRepoSpec(RepoSpec): self.enabled = yumrepo.enabled self.mediaid = yumrepo.mediaid
- def mount(self, ayum): - # TODORV: failure handling - if self.url.startswith("hd:"): - if self.url.count(":") == 2: - (device, path) = self.url[3:].split(":") - else: - (device, fstype, path) = self.url[3:].split(":") + # TODORV: handle failure (UI, setup attribute) + def _umountIsodir(self, ayum): + if os.path.ismount(ayum.tree): + isys.umount(ayum.tree, removeDir=False) + ayum.currentMedia = None + if os.path.isdir(ayum.isodir): + if os.path.ismount(ayum.isodir): + isys.umount(ayum.isodir, removeDir=False) + else: + os.mkdir(ayum.isodir) + + def _mountUI(self, intf, device, path, fstype, options=None, msg=("","")): + title, msg = msg + while True: + try: + isys.mount(device, path, fstype=fstype, options=options) + break + except SystemError as e: + log.error("repository mounting failed: %s" % e) + ans = intf.messageWindow(title, + "%s \n%s" % (msg, e), + type="custom", custom_icon="error", + custom_buttons=[_("_Exit"), _("_Edit"), _("_Retry")]) + + if ans == 0: + sys.exit(0) + elif ans == 1: + self.setup = "failed" + self.setup_fail_comment = msg + return False + else: + continue + return True
- ayum.isodir = "/mnt/isodir/%s" % path + def _mountNfsIso(self, ayum): + (opts, server, path) = iutil.parseNfsUrl(self.url) + ayum.isodir = "/mnt/isodir" + title = _("Couldn't Mount ISO Source") + msg = _("An error occurred mounting the ISO source " + "on %s.") % server+":"+path + return self._mountIso(ayum, server+":"+path, + ayum.isodir, "nfs", options=opts, + msg=(title, msg)) + + def _mountHdIso(self, ayum): + (device, fstype, path) = iutil.parseHdUrl(self.url) + ayum.isodir = "/mnt/isodir/%s" % path + title = _("Couldn't Mount ISO Source") + msg = _("An error occurred mounting the source " + "device %s. This may happen if your ISO " + "images are located on an advanced storage " + "device like LVM or RAID, or if there was a " + "problem mounting a partition.") % device + return self._mountIso(ayum, device, ayum.isodir, + fstype or "auto", msg=(title, msg)) + + def _mountIso(self, ayum, device, path, fstype, options=None, msg=""): + self._umountIsodir(ayum) + if not self._mountUI(ayum.anaconda.intf, device, path, fstype, options=options, msg=msg): + return False + ayum._switchImage(1) + ayum.mediagrabber = ayum.mediaHandler + return True
- # This takes care of mounting /mnt/isodir first. - ayum._switchImage(1) - ayum.mediagrabber = ayum.mediaHandler + def _mountNfs(self, ayum, dest): + (opts, server, path) = iutil.parseNfsUrl(self.url) + title = _("Couldn't Mount Source Tree") + msg = _("An error occured mounting the source tree on %s") % server+":"+path + return self._mountUI(ayum.anaconda.intf, server+":"+path, dest, "nfs", + options=opts, msg=(title, msg)) + def mount(self, ayum): + if self.url.startswith("hd:"): + if not self._mountHdIso(ayum): + return False + self.baseurl = "file://%s" % ayum.tree elif self.url.startswith("http") or self.url.startswith("ftp:"): self.baseurl = self.url elif self.url.startswith("cdrom:"): # When/if supported (KS, UI), parse device - if not self.media_device: + # nfsiso or hdiso could have been mounted to ayum.tree + if ayum.isodir and os.path.ismount(ayum.tree): + isys.umount(ayum.tree, removeDir=False) + ayum.currentMedia = None + if not self.media_device or ayum.isodir: cdr = scanForMedia(ayum.tree, ayum.anaconda.storage) if cdr: self.media_device = cdr @@ -274,12 +340,8 @@ class YumRepoSpec(RepoSpec): else: dest = tempfile.mkdtemp("", self.name.replace(" ", ""), "/mnt")
- (opts, server, path) = iutil.parseNfsUrl(self.url) - try: - isys.mount(server+":"+path, dest, "nfs", options=opts) - except Exception as e: - log.error("error mounting NFS repo %s: %s" % (self.name, e)) - raise + if not self._mountNfs(ayum, dest): + return False self.baseurl = "file://%s" % dest
# This really should be fixed in loader instead but for now see @@ -288,18 +350,13 @@ class YumRepoSpec(RepoSpec): if self.type == "method": images = findIsoImages(ayum.tree, ayum.anaconda.intf.messageWindow) if images != {}: - isys.umount(ayum.tree, removeDir=False) self.url = "nfsiso:%s" % self.url[4:] - # TODORV - fix properly? SPOT? - # "nfsiso:" case pasted - ayum.isodir = "/mnt/isodir" - ayum._switchImage(1) - ayum.mediagrabber = ayum.mediaHandler + if not self._mountNfsIso(ayum): + return False self.baseurl = "file://%s" % ayum.tree elif self.url.startswith("nfsiso:"): - ayum.isodir = "/mnt/isodir" - ayum._switchImage(1) - ayum.mediagrabber = ayum.mediaHandler + if not self._mountNfsIso(ayum): + return False self.baseurl = "file://%s" % ayum.tree else: log.info("repo %s has unsupported url: %s" % (self.name, @@ -614,11 +671,6 @@ class AnacondaYum(YumSorter): umountImage(self.tree, self.currentMedia) self.currentMedia = None
- # mountDirectory checks before doing anything, so it's safe to - # call this repeatedly. - mountDirectory(self.anaconda.methodstr, - self.anaconda.intf.messageWindow) - self._discImages = mountImage(self.isodir, self.tree, discnum, self.anaconda.intf.messageWindow, discImages=self._discImages)
On 09/30/2010 04:26 PM, Radek Vykydal wrote:
[3] network requiring repository set by boot param repo=, no network needed till repository steps - failing to enable network for base repository is fatal: http://rvykydal.fedorapeople.org/repoUI/example3.ogg - with patch, you can fall back to media (non network) repo: http://rvykydal.fedorapeople.org/repoUI/example3patched.ogg
sorry, wrong links, the correct are:
[3] network requiring repository set by boot param repo=, no network needed till repository steps - failing to enable network for base repository is fatal: http://rvykydal.fedorapeople.org/repoUI/example2.ogg - with patch, you can fall back to media (non network) repo: http://rvykydal.fedorapeople.org/repoUI/example2patched.ogg
anaconda-devel@lists.stg.fedoraproject.org