[zypp-devel] RFC: ZYpp application layer comments
Communication between the YCP application and the C++ package selector widgets is very limited (almost nonexistent). In particular, the YCP application cannot create a pool and pass a handle to that pool to the C++
I would like others (especially Michael) to review it. I am quoting the blocks of texts from the Wiki, which I would like to focus the discussion on. package selector or vice versa.
Thus, for all important objects like pool or similar, there must be a singleton object for each that can easily be accessed from both the YCP code (via package bindings) and from the package selectors. Those singleton objects should live in the app layer.
Selectables have a higher level status that is the result of the useful permutations of the status flags of their resolvables and the fact whether or not one resolvable from this selectable (one instance of that package) is already installed:
Matching Selectable and Resolvable Status and Candidate Handling The package selectors use the selectable status. They never store any selectable's status; they always retrieve the status when it is needed. When the user changes a selectable's status, they set new status and retrieve it for display so the display and the selectable status are always in sync. Since selectable don't exist in the libzypp layer, matching a selectable status to the individual resolvable status flags is done by the app layer and vice versa. The app layer also keeps track of the candidate. This is important when the selectable status is used since some operations (install, update) implicitly operate on the candidate.
I think this is one of the central parts of the problem we need to solve.
As long as the user did not explicitly choose a specific candidate, the app layer automatically chooses one from the available resolvables. That automatic choice takes matching architectures and most recent version number into account.
This is not trivial. Right now the UIs do this choices in some random code somewhere in the UI, or using the selectable candidate function, which is now incomplete. This kind of decision is the same as the solver local policy. So we have to connect them trough the API somehow. Imagine this something like: bestCandidate( list_of_candidates, task ) Of course this has to be in Selectable, but it has to be connected to the local policy, and it depends on the task you want to execute. Otherwise the UIs will continue breaking things like vendor (the UIs iterate over a package list and select the higher version instead of the best candidate according to the system policies ).
Patterns (collections of packages to fulfil a certain task)
This is where the UI <-> library problems starts. What the wiki describes there is the description of a Selection.
Technically, none of those higher level objects has a package content: They all just have dependencies that typically (but not necessarily) are satisfied by packages.
From a user's point of view, however, those higher level objects are nothing else than containers for packages. Though this is a somewhat simplistic view, users typically want to see what is behind a pattern, a patch, or a language resolvable. This is why each of them should have a mechanism to enumerate the
Exactly. packages that belong to them. This is true, but only if you can live with the true: the mechanism to enumerate the list of packages is just an algorithm, not a list. It can be false information. That is why I tried to start using the term "related packages" instead of "content".
Enumerate all packages that belong to an RPM group (not just for leaf RPM groups, also those that belong to a higher level like "Productivity")
Changing the status of a pattern, patch, or language typically affects the status of other resolvables, most notably the packages that belong to this
I think the best way to solve this problems is to introduce categories for all resolvabes (tags), in the case of packages it can be implemented using rpm groups, and implement iterators to match full tag, part of it, a collection of them, etc. pattern, patch, or language. Those changes should be propagated immediately to those packages so the user can instantly see the effect.
In the past there were calls to just propagate those changes, which worked in most cases, but not in certain pathological ones. But those calls were quick, so for the user there was an instant response. Recent changes rely on a full solver run to propagate the changes, which gives the correct result in all cases, but which on the other hand is much slower. As some users disable the automatic dependency check because of this, some of them get confused because no change is propagated. There have been bug reports with lively discussions about that.
I read this as, we need to do a solver run without doing a solver run? This is a technical limitation, which could be solved of we could for example cheap-ly clone a pool , do something and see the changes, without making the main one dirty. I have no idea yet if that undocumented dirpool has something to do with that ;-)
The user can select any one of those search modes:
* Contains * Begins with * Exact match * Use wildcards (*, ?) * Use regular expression
Case sensitivity in all searches can be switched on or off.
A query engine to replace this functionality in the UI should enumerate (return an iterator to) the selectables that match the given criteria.
Update Problems Filter View This filter view enumerates those packages that for one reason or the other could not automatically be updated during a system update. Adding packages to this list is done by the system update application layer.
And what is the criteria for searching those?
Libzypp/Application Layer/API Proposal
enum Type { PACKAGE, PATTERN, PATCH, LANGUAGE }
Why resolvable kind can't be used here? Why the need to duplicate it?
* Create pool iterator (based on the above query as a parameter)
Pool pool = new Pool (q);
So this is like a pool proxy based on a query? Why not the iterator taking the query as a parameter (Michael could comment more?) I have one concern with the design of the pool based on queries. It means we have to load the pool data, this will not be bad with the sat solver, but this is not the same zypper does, as zypper only gets the resolvable data to be displayed on the screen, without any pool. Appart of that the query api seems fine. Other API that could be part of the zypp layer is a command oriented api. If you look what API smart offers to the UIs is a much simpler API, and it is the same the command line tool offers, and this is quite similar to all zypper code. It defines a Command class (install, search, etc) it defines a abstract interface for callbacks (confirm dialogs, progress, questions, etc) The UI simply creates a command and executes it. This is a much simpler approach, but I think it is needed in addition to the layer our selector need. Both packagekit and any app wanting to install a package could benefit from this. Duncan -- To unsubscribe, e-mail: zypp-devel+unsubscribe@opensuse.org For additional commands, e-mail: zypp-devel+help@opensuse.org
On Sun, Jan 27, Duncan Mac-Vicar P. wrote:
Enumerate all packages that belong to an RPM group (not just for leaf RPM groups, also those that belong to a higher level like "Productivity")
I think the best way to solve this problems is to introduce categories for all resolvabes (tags), in the case of packages it can be implemented using rpm groups, and implement iterators to match full tag, part of it, a collection of them, etc.
We have filter iterators. We basically need to collect the common filter functions inside zypp so they are available to each application. More important is a nice and easy syntax to combine simple queries to create more complex one. And a fexible way to process the result as either a collection of individual PoolItems or sort of Selectables.
Changing the status of a pattern, patch, or language typically affects the status of other resolvables, most notably the packages that belong to this pattern, patch, or language. Those changes should be propagated immediately to those packages so the user can instantly see the effect. In the past there were calls to just propagate those changes, which worked in most cases, but not in certain pathological ones. But those calls were quick, so for the user there was an instant response. Recent changes rely on a full solver run to propagate the changes, which gives the correct result in all cases, but which on the other hand is much slower. As some users disable the automatic dependency check because of this, some of them get confused because no change is propagated. There have been bug reports with lively discussions about that.
I read this as, we need to do a solver run without doing a solver run?
This is a technical limitation, which could be solved of we could for example cheap-ly clone a pool , do something and see the changes, without making the
You don't need to clone for this. The solver does not change the pool status. The pool status are translated into a jobdescription. The solver processes this description and returns a result list. This result list can be written back to the pool or used to just create a 'diff'. But a solver run does not help you to determine something that can be displayed as 'pattern content'. IMO the current approach is ok. It's probably more important to collect and provide the appropriate interfaces in zypp.
main one dirty. I have no idea yet if that undocumented dirpool has something to do with that ;-)
No, dirpool is the internal representation of pathnames as (dirname, basename) tuples (didn't you read the source?).
Update Problems Filter View This filter view enumerates those packages that for one reason or the other could not automatically be updated during a system update. Adding packages to this list is done by the system update application layer.
And what is the criteria for searching those?
We'll have to see if this still applies. The 'old' update algorithm went through all installed packages, looking for an appropriate replacement. So we were able to tell why some package was selected, and if there was no replacement for a specific package. This is most probably not true if the solver updates the system. We have to see how to interpret and visualize the result we get back from the solver.
* Create pool iterator (based on the above query as a parameter)
Pool pool = new Pool (q);
So this is like a pool proxy based on a query? Why not the iterator taking the query as a parameter (Michael could comment more?)
Or the Query will provide an iterator to the result. But not create some result container. It's easy for the application to do this, if desired. set<PoolItem> result( query.begin(), query.end() ); -- cu, Michael Andres +------------------------------------------------------------------+ Key fingerprint = 2DFA 5D73 18B1 E7EF A862 27AC 3FB8 9E3A 27C6 B0E4 +------------------------------------------------------------------+ Michael Andres YaST Development ma@novell.com SUSE LINUX Products GmbH, GF: Markus Rex, HRB 16746 (AG Nuernberg) Maxfeldstrasse 5, D-90409 Nuernberg, Germany, ++49 (0)911 - 740 53-0 +------------------------------------------------------------------+ -- To unsubscribe, e-mail: zypp-devel+unsubscribe@opensuse.org For additional commands, e-mail: zypp-devel+help@opensuse.org
* Duncan Mac-Vicar P.
I would like others (especially Michael) to review it.
I am quoting the blocks of texts from the Wiki, which I would like to focus the discussion on.
I took the freedom of extending the application layer pages in the wiki with - tagging them with Category:Libzypp - pointing out that there are more consumers of libzypp than just UIs (i.e. bindings to common scripting languages) - questioning the semantics of a 'candidate' - pluraling 'installed package'
The app layer also keeps track of the candidate. This is important when the selectable status is used since some operations (install, update) implicitly operate on the candidate.
It should be clearly noted that the current notion of a 'candidate' is misleading and cannot be fulfilled (in each case) by the solver. Based on this, the selectable structure mypkg selectable * mypkg-12.3-42 resolvable from openSUSE DVD1 * mypkg-12.3-47 resolvable from openSUSE update server * mypkg-12.3-47 resolvable from gwdg.de mirror server * mypkg-12.3-42 resolvable from RPM DB (already installed) * mypkg-12.4-3 resolvable from Packman server should rather be mypkg * 12.3-42 installed * 12.3-42 available from openSUSE DVD1 * 12.3-47 available from openSUSE update server, gwdg.de mirror server * 12.4-3 available from Packman server Operations should always be performed on the root of this 'tree', namely 'mypkg' (without a specific version or repository). Choosing a specific version (eventually from a specific repo) should be the exception, because it might lead to - package not being installable (because of missing/breaking deps) - more package updates being triggered (because of changed deps)
Patterns (collections of packages to fulfil a certain task)
This is where the UI <-> library problems starts. What the wiki describes there is the description of a Selection.
Technically, none of those higher level objects has a package content: They all just have dependencies that typically (but not necessarily) are satisfied by packages.
This is going to change in 11.0, patterns and patches will have 'package content'.
Changing the status of a pattern, patch, or language typically affects the status of other resolvables, most notably the packages that belong to this pattern, patch, or language. Those changes should be propagated immediately to those packages so the user can instantly see the effect.
I question the need for immediate propagation, at least in a 'fully resolved' way[1][2]. Choosing a pattern 'for installation' should be treated as a macro call, resulting in all 'contained' packages (app layer will provide an interator) to be choosen 'for installation'. IMHO, this should be sufficient to match the user expectation. Klaus [1] Full resolution on a package level. [2] Resolution of pattern<->pattern dependencies should still be done. --- SUSE LINUX Products GmbH, GF: Markus Rex, HRB 16746 (AG Nürnberg) -- To unsubscribe, e-mail: zypp-devel+unsubscribe@opensuse.org For additional commands, e-mail: zypp-devel+help@opensuse.org
* Michael Andres
Or the Query will provide an iterator to the result. But not create some result container. It's easy for the application to do this, if desired.
set<PoolItem> result( query.begin(), query.end() );
I'd rather see _no_ application doing this. Depending on the number of matching pool items, this might result in severe memory pressure. Klaus --- SUSE LINUX Products GmbH, GF: Markus Rex, HRB 16746 (AG Nürnberg) -- To unsubscribe, e-mail: zypp-devel+unsubscribe@opensuse.org For additional commands, e-mail: zypp-devel+help@opensuse.org
Duncan Mac-Vicar P. wrote:
I would like others (especially Michael) to review it.
I am quoting the blocks of texts from the Wiki, which I would like to focus the discussion on.
Communication between the YCP application and the C++ package selector widgets is very limited (almost nonexistent). In particular, the YCP application cannot create a pool and pass a handle to that pool to the C++ package selector or vice versa.
Thus, for all important objects like pool or similar, there must be a singleton object for each that can easily be accessed from both the YCP code (via package bindings) and from the package selectors. Those singleton objects should live in the app layer.
My guess is, that there will be no need for the applications to manipulate the Pool directly as long as we have all the other API they need (the Query, content iterators of Patterns, etc.)
Selectables have a higher level status that is the result of the useful permutations of the status flags of their resolvables and the fact whether or not one resolvable from this selectable (one instance of that package) is already installed:
Matching Selectable and Resolvable Status and Candidate Handling The package selectors use the selectable status. They never store any selectable's status; they always retrieve the status when it is needed. When the user changes a selectable's status, they set new status and retrieve it for display so the display and the selectable status are always in sync.
Since selectable don't exist in the libzypp layer, matching a selectable status to the individual resolvable status flags is done by the app layer and vice versa. The app layer also keeps track of the candidate. This is important when the selectable status is used since some operations (install, update) implicitly operate on the candidate.
I think this is one of the central parts of the problem we need to solve.
What about merging NameKindProxy with Selectables? I mean to drop the NameKindProxy and allow a Selectable to have multiple target (installed) resolvables. See also this mail from Michael, the second half: http://lists4.suse.de/yast-devel/2007-06/msg00150.html I quite agree with Michael there and i came to the same conclusion recently. I don't mean using Selectables as Pool objects (i don't have much solver knowledge), but the others, namely: "It would be a good place to provide convenient status manipulating methods.", installation candidate handling. Also it would be a good place for a 'contents' iterator (whatever it would mean from the implementation POV). I guess this is also consistent with what Klaus says in the other mail [1] in this thread. Can this be done? While i know that we want to be able to have multiple versions of a resolvable to be installed, i don't know why this case needs to be represented by multiple Selectables instead of one (which has multiple target resolvables). Would it be harder or even impossible for the UIs to handle?
As long as the user did not explicitly choose a specific candidate, the app layer automatically chooses one from the available resolvables. That automatic choice takes matching architectures and most recent version number into account.
This is not trivial. Right now the UIs do this choices in some random code somewhere in the UI, or using the selectable candidate function, which is now incomplete. This kind of decision is the same as the solver local policy. So we have to connect them trough the API somehow. Imagine this something like:
bestCandidate( list_of_candidates, task )
Of course this has to be in Selectable, but it has to be connected to the local policy, and it depends on the task you want to execute.
Otherwise the UIs will continue breaking things like vendor (the UIs iterate over a package list and select the higher version instead of the best candidate according to the system policies ).
I bet we all agree on this, this stuff must be handled in libzypp. Before we continue to discuss this, let's settle on the term 'selectable' so that we know what we talk about. See my mail "Selectables and 'candidate'".
Patterns (collections of packages to fulfil a certain task)
This is where the UI <-> library problems starts. What the wiki describes there is the description of a Selection.
That's just a problem of agreeing on usage of the words. I see no _real_ problem here. The bottom line is, the UIs need to show what's inside a pattern - libzypp must provide an API to enumerate something to display. Once we agree on this, the rest is not such a big problem. We don't need to argue about words (although i agree we should speak as precisely as the subject requires when discussing technical matters).
Technically, none of those higher level objects has a package content: They all just have dependencies that typically (but not necessarily) are satisfied by packages.
Exactly.
From a user's point of view, however, those higher level objects are nothing else than containers for packages. Though this is a somewhat simplistic view, users typically want to see what is behind a pattern, a patch, or a language resolvable. This is why each of them should have a mechanism to enumerate the packages that belong to them.
This is true, but only if you can live with the true: the mechanism to enumerate the list of packages is just an algorithm, not a list. It can be false information.
That is why I tried to start using the term "related packages" instead of "content".
Again, this is 'just' about words. What we need is the API :O) Let's have Selectable::contents{Begin,End,Size}(), whatever we put inside. Now for searching. It was suggested that there should be a class handling the queries - thus zypp::ui::Query. Based on requirements and Bubli's (and my own) proposal, i've written the Query.h (see attached):
The user can select any one of those search modes:
* Contains * Begins with * Exact match
enum MatchLevel { MATCH_ANY_SUBSTRING, MATCH_WHOLE_WORDS, MATCH_EXACT_PHRASE, MATCH_BEGINNING, MATCH_END }; void Query::setMatchLevel(const MatchLevel level) { _match_level = level; } Maybe the MatchLevel should be in the addName() and addAttribute() methods, not for the whole Query?
* Use wildcards (*, ?)
If the queries will be implemented with regexes, any wildards in the search strings will be converted to correspondent regex sequences.
* Use regular expression
note the isRegex parameter: void Query::addName(const std::string & name, bool is_regexp = false); void addAttribute(const Attribute & attrid, const std::string & value, bool isRegex = false);
Case sensitivity in all searches can be switched on or off.
void Query::setCaseSensitive(const bool value = true); ==== Another thingy to solve is the combination of several queries, e.g. give two or three strings, match packages containg ALL of them, or ANY of them. Some basic degree of this could be provided by: void setRequireAll(bool require_all = true); which would serve as AND operator if true, and OR if false to combine all the conditions added to the Query with addName() and addAttribute() (or even addDependency()). Not much flexiblity, but the question is also whether we need more. ==== If you agree, i'll commit the file to svn trunk and we can polish it there. If it is fundamentally wrong, i'll rewrite it and check with you agin :O)
A query engine to replace this functionality in the UI should enumerate (return an iterator to) the selectables that match the given criteria.
Update Problems Filter View This filter view enumerates those packages that for one reason or the other could not automatically be updated during a system update. Adding packages to this list is done by the system update application layer.
And what is the criteria for searching those?
Again, whatever is the answer to this question, the API must be there somewhere in libzypp, so that the UIs can just use it and not implment it on their own.
Libzypp/Application Layer/API Proposal
enum Type { PACKAGE, PATTERN, PATCH, LANGUAGE }
Why resolvable kind can't be used here? Why the need to duplicate it?
No need to duplicate, it was meant only as a demonstration. Of course we will use Resolvable::Kind, but maybe it would be nice to have an enumeration of currently used kinds.
* Create pool iterator (based on the above query as a parameter)
Pool pool = new Pool (q);
So this is like a pool proxy based on a query? Why not the iterator taking the query as a parameter (Michael could comment more?)
I have one concern with the design of the pool based on queries. It means we have to load the pool data, this will not be bad with the sat solver, but this is not the same zypper does, as zypper only gets the resolvable data to be displayed on the screen, without any pool.
Dropped in the actual API proposal, see Query.h in the attachment.
Other API that could be part of the zypp layer is a command oriented api.
If you look what API smart offers to the UIs is a much simpler API, and it is the same the command line tool offers, and this is quite similar to all zypper code.
It defines a Command class (install, search, etc) it defines a abstract interface for callbacks (confirm dialogs, progress, questions, etc) The UI simply creates a command and executes it.
This is a much simpler approach, but I think it is needed in addition to the layer our selector need.
Both packagekit and any app wanting to install a package could benefit from this.
The is interesting, but i would rather discuss it in a separate thread. Also it probably something for post 11.0 releases. [1] http://lists4.suse.de/zypp-devel/2008-02/msg00018.html
Jan Kupec wrote:
Duncan Mac-Vicar P. wrote:
As long as the user did not explicitly choose a specific candidate, the app layer automatically chooses one from the available resolvables. That automatic choice takes matching architectures and most recent version number into account. This is not trivial. Right now the UIs do this choices in some random code somewhere in the UI, or using the selectable candidate function, which is now incomplete. This kind of decision is the same as the solver local policy. So we have to connect them trough the API somehow. Imagine this something like: bestCandidate( list_of_candidates, task )
Of course this has to be in Selectable, but it has to be connected to the local policy, and it depends on the task you want to execute.
Otherwise the UIs will continue breaking things like vendor (the UIs iterate over a package list and select the higher version instead of the best candidate according to the system policies ).
I bet we all agree on this, this stuff must be handled in libzypp.
Before we continue to discuss this, let's settle on the term 'selectable' so that we know what we talk about. See my mail "Selectables and 'candidate'".
i meant 'candidate', sorry, but the selectable needs to be ironed out, too :O) -- To unsubscribe, e-mail: zypp-devel+unsubscribe@opensuse.org For additional commands, e-mail: zypp-devel+help@opensuse.org
Hi, On Thu, 7 Feb 2008, Jan Kupec wrote:
This is where the UI <-> library problems starts. What the wiki describes there is the description of a Selection.
That's just a problem of agreeing on usage of the words. I see no _real_ problem here. The bottom line is, the UIs need to show what's inside a pattern - libzypp must provide an API to enumerate something to display.
A pattern is more than a selectable. A selectable is an arbitrary set of solvables. A pattern OTOH can have dependencies, i.e. is a solvable itself.
Now for searching.
I have no opinion on the libzypp API for searches, but I can tell you what the satsolver library will provide (and partly provides already): It has a generic repo_search function. The results will be given to you via a callback function. It will accept strings and numbers, searching in one or all solvables, in one or all attributes, for substrings, globs or regexps, case sensitive or case insensitive. Via the callback you will be given the information of which solvable matches, in which attribute, and what the value of it is. Using the repo_search function will be much faster and take much less memory than implementing the searches by hand in libzypp (those would need to retrieve all attributes one by one, or (even worse) cache that data itself). Ciao, Michael. -- To unsubscribe, e-mail: zypp-devel+unsubscribe@opensuse.org For additional commands, e-mail: zypp-devel+help@opensuse.org
Michael Matz wrote:
I have no opinion on the libzypp API for searches, but I can tell you what the satsolver library will provide (and partly provides already):
It has a generic repo_search function. The results will be given to you via a callback function. It will accept strings and numbers, searching in one or all solvables, in one or all attributes, for substrings, globs or regexps, case sensitive or case insensitive. Via the callback you will be given the information of which solvable matches, in which attribute, and what the value of it is.
Using the repo_search function will be much faster and take much less memory than implementing the searches by hand in libzypp (those would need to retrieve all attributes one by one, or (even worse) cache that data itself).
Ciao, Michael.
Actually that is what I am going to use to replace ResolvableQuery, it should be straightforward, and we have already the attribute class, and this time the callback can send back a ResObject, which was not possible before. Duncan -- To unsubscribe, e-mail: zypp-devel+unsubscribe@opensuse.org For additional commands, e-mail: zypp-devel+help@opensuse.org
Michael Matz wrote:
Now for searching.
I have no opinion on the libzypp API for searches, but I can tell you what the satsolver library will provide (and partly provides already):
It has a generic repo_search function. The results will be given to you via a callback function. It will accept strings and numbers, searching in one or all solvables, in one or all attributes, for substrings, globs or regexps, case sensitive or case insensitive. Via the callback you will be given the information of which solvable matches, in which attribute, and what the value of it is.
Using the repo_search function will be much faster and take much less memory than implementing the searches by hand in libzypp (those would need to retrieve all attributes one by one, or (even worse) cache that data itself).
I'm all for it, then the implementation of the libzypp API will use what sat solver provides, that's great. If there will be something missing in the sat solver to implement the needed API in libzypp, we can add what's needed to sat solver. The most imporant thing is, that the UIs have it and don't need to code it themselves. jano -- To unsubscribe, e-mail: zypp-devel+unsubscribe@opensuse.org For additional commands, e-mail: zypp-devel+help@opensuse.org
On Thu, Feb 07, Jan Kupec wrote:
I think this is one of the central parts of the problem we need to solve.
What about merging NameKindProxy with Selectables? I mean to drop the NameKindProxy and allow a Selectable to have multiple target (installed) resolvables.
That's what we're heading for.
/** \file zypp/ui/Query.h * */
/** * Filter by the \a value of any available attribute of selectables. * * \note Selectables of a kind not supporting the specified attribute will * <b>not</b> be returned. * * \param attrid attribute identfier (sat::SolvAttr or cache::Attribute * or something implementation independent) * \param value what to search for * \param isRegex is the value a regex? */ void addAttribute(const solv::SolvAttr & attrid, const std::string & value, bool isRegex = false);
/** * Add dependency filter. * * \param dtype depenedcy type * \param name depenency name * \param edition edition for a versioned dependency * \param rel operand for a versioned dependency * * \todo maybe a isRegexp bool as in addName() for the name parameter would * be handy here as well. * \todo add more addDependecy() variants */ void addDependency(const Dep & dtype, const std::string & name, const Edition & edition = Edition(), const Rel & rel = Rel::EQ);
Keep in mind that a selectable is just a container of solvables. A selectable has no attributes (except name, kind and status). Queries will return solvables in the first place. They can of course be mapped back to selectables, meaning that 'one or more solvables inside the selectable' matched. -- cu, Michael Andres +------------------------------------------------------------------+ Key fingerprint = 2DFA 5D73 18B1 E7EF A862 27AC 3FB8 9E3A 27C6 B0E4 +------------------------------------------------------------------+ Michael Andres YaST Development ma@novell.com SUSE LINUX Products GmbH, GF: Markus Rex, HRB 16746 (AG Nuernberg) Maxfeldstrasse 5, D-90409 Nuernberg, Germany, ++49 (0)911 - 740 53-0 +------------------------------------------------------------------+ -- To unsubscribe, e-mail: zypp-devel+unsubscribe@opensuse.org For additional commands, e-mail: zypp-devel+help@opensuse.org
participants (5)
-
Duncan Mac-Vicar P.
-
Jan Kupec
-
Klaus Kaempf
-
Michael Andres
-
Michael Matz