免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: duanjigang
打印 上一主题 下一主题

Linux平台软件管理系统设计与规划-中级篇(3)-深入理解和使用yum来管理RPM包 [复制链接]

论坛徽章:
0
11 [报告]
发表于 2013-01-03 12:25 |只看该作者
本帖最后由 duanjigang 于 2013-01-03 13:04 编辑

yum 本地 cache

之所以要把yum 的本地 cache 拿出来单独说下,是因为这个要素不管在 yum 的执行流程中,还是在 yum 的日常问题中都很重要。

首先看下本机的 repo 配置:

  1. # ls /etc/yum.repos.d/*
  2. /etc/yum.repos.d/newtest.repo  /etc/yum.repos.d/test.repo
  3. [root@yum yum]# cat /etc/yum.repos.d/*.repo
  4. [newtest]
  5. name=just a test reposity
  6. baseurl=http://yum.test.com:81/yum
  7. enabled=1
  8. gpgcheck=0
  9. gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release

  10. [test]
  11. name=just a test reposity
  12. baseurl=http://yum.test.com:81/yum
  13. enabled=1
  14. gpgcheck=0
  15. gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
复制代码
我们能够看到配置了两个 repo,其中一个名字为test, 另外一个为 newtest.

然后再看文件
/etc/yum.conf
中的配置

  1. cachedir=/var/cache/yum
复制代码
设备本地的cache 目录为
/var/cache/yum 目录,首先我们清空下该目录:

  1. # rm -fr /var/cache/yum/*
复制代码
然后执行命令

  1. #yum list
复制代码
看下在 cache 目录下能生成什么咚咚?

  1. # tree /var/cache/yum/
  2. /var/cache/yum/
  3. |-- newtest
  4. |   |-- cachecookie
  5. |   |-- headers
  6. |   |-- packages
  7. |   |-- primary.xml.gz
  8. |   |-- primary.xml.gz.sqlite
  9. |   `-- repomd.xml
  10. `-- test
  11.     |-- cachecookie
  12.     |-- headers
  13.     |-- packages
  14.     |-- primary.xml.gz
  15.     |-- primary.xml.gz.sqlite
  16.     `-- repomd.xml
复制代码
能够看到,客户端的两个repo 都对应生成了 cache 目录,每个cache 的目录中都有文件 repomd.xml 和 primary.xml 文件或者其 Sqlite 文件。
为了验证下客户端是否把服务器上的索引文件下载下来了,我们进行对比:
在客户端执行:

  1. # md5sum repomd.xml
  2. 916b78131cab447e60b17bf01a41240a  repomd.xml
复制代码
在服务器上执行:
# md5sum /usr/local/cme/web/yum/repodata/repomd.xml
916b78131cab447e60b17bf01a41240a  /usr/local/cme/web/yum/repodata/repomd.xml
[/code]
可以看到两者是一致的。验证了 yum 客户端 cache 服务器索引到本地的实事。
另外还有两个目录 headers 和 packages, 可以参考源码进行功能分析。
在此处貌似要补充上 yum 是怎么利用本地 cache 进行工作的,这貌似又成了 yum 的源码分析了,目前这块偶还未有阅读到,后面有空再补充吧。

在上一节中我们只是就 primary.xml 文件进行了内容分析,这里,在客户端 cache 中,我们花点时间看下 sqlite 文件,因为 yum 中对 rpm 包的信息存储数据库时采用的
是文件数据库 sqlite 存储的。因此我们单独拎出来说下.

yum 索引中的 sqlite 文件

以客户端的 primary.xml.gz.sqlite 为例,用 sqlite3 直接打开 sqlite 文件进行查看:

  1. # sqlite3 primary.xml.gz.sqlite
  2. sqlite> .table  
  3. conflicts  db_info    files      obsoletes  packages   provides   requires
  4. sqlite> .schema packages
  5. CREATE TABLE packages (  pkgKey INTEGER PRIMARY KEY,  pkgId TEXT,  name TEXT,  arch TEXT,  version TEXT,  epoch TEXT,  release TEXT,  summary TEXT,  description TEXT,  url TEXT,  time_file TEXT,  time_build TEXT,  rpm_license TEXT,  rpm_vendor TEXT,  rpm_group TEXT,  rpm_buildhost TEXT,  rpm_sourcerpm TEXT,  rpm_header_start TEXT,  rpm_header_end TEXT,  rpm_packager TEXT,  size_package TEXT,  size_installed TEXT,  size_archive TEXT,  location_href TEXT,  location_base TEXT,  checksum_type TEXT,  checksum_value TEXT);
  6. CREATE INDEX packageId ON packages (pkgId);
  7. CREATE INDEX packagename ON packages (name);
  8. CREATE TRIGGER removals AFTER DELETE ON packages  BEGIN    DELETE FROM files WHERE pkgKey = old.pkgKey;    DELETE FROM requires WHERE pkgKey = old.pkgKey;    DELETE FROM provides WHERE pkgKey = old.pkgKey;    DELETE FROM conflicts WHERE pkgKey = old.pkgKey;    DELETE FROM obsoletes WHERE pkgKey = old.pkgKey;  END;
复制代码
通过 sqlite 的命令能看到 sqlite 中的所有表,每个文件是一个库,这个库中的所有 table 包含的信息,与 我们前面看到的 primary.xml 中的信息是一致的。
只不过采用了不同的存储方式。
看下 package 表 中的数据:

sqlite> select * from packages;
1|6a6fbc7e58160ffa71b1f91912dc6eaf1cd6e0ae|wget|i386|1.14|0|1|GNU wget|The GNU wget program downloads files from the Internet using the command-line.||1357187570|1357185960|GPL||Development/Tools|yum.test.com|wget-1.14-1.src.rpm|280|9282||809949|2032999|2039664|wget-1.14-1.i386.rpm||sha|6a6fbc7e58160ffa71b1f91912dc6eaf1cd6e0ae
2|a9f4ad3086dcfcced210361bc72e4d418144cfcc|test-daddy|i386|1.1|0|1|GNU test-daddy|The GNU wget program downloads files from the Internet using the command-line.||1357187570|1357185424|GPL||Development/Tools|yum.test.com|test-daddy-1.1-1.src.rpm|280|2127||4235|9354|4936|test-daddy-1.1-1.i386.rpm||sha|a9f4ad3086dcfcced210361bc72e4d418144cfcc
3|82828b38c988399f597cceb14d896de6d36cd1c8|test-girl|i386|1.1|0|1|GNU test-girl|The GNU wget program downloads files from the Internet using the command-line.||1357187570|1357185262|GPL||Development/Tools|yum.test.com|test-girl-1.1-1.src.rpm|280|2119||4227|9354|4936|test-girl-1.1-1.i386.rpm||sha|82828b38c988399f597cceb14d896de6d36cd1c8
4|6ce87802e577736bb8b481c157cb9fb8822f8b88|wget-debuginfo|i386|1.14|0|1|Debug information for package wget|This package provides debug information for package wget.
Debug information is useful when developing applications that use this
package or when debugging this package.||1357187570|1357185960|GPL||Development/Debug|yum.test.com|wget-1.14-1.src.rpm|280|2043||19714|45564|45836|wget-debuginfo-1.14-1.i386.rpm||sha|6ce87802e577736bb8b481c157cb9fb8822f8b88
5|f377c921e23ba10507de267b345d03d1f32f02a4|test-baby|i386|1.1|0|1|GNU test-baby|The GNU wget program downloads files from the Internet using the command-line.||1356570667|1356570667|GPL||Development/Tools|localhost.localdomain|test-baby-1.1-1.src.rpm|280|2290||4463|9395|5108|test-baby-1.1-1.i386.rpm||sha|f377c921e23ba10507de267b345d03d1f32f02a4
sqlite>


呵呵,看到的数据和 xml 文件中的展示的数据是一样的。


论坛徽章:
0
12 [报告]
发表于 2013-01-03 13:05 |只看该作者
本帖最后由 duanjigang 于 2013-01-03 13:19 编辑

yum clean 分析
提到 yum 的客户端 cache,又不得不对yum 中一个常用的针对cache 进行操作的命令进行分析,那就是: yum clean

看下 man 手册:

  1. clean  Is  used  to  clean  up  various things which accumulate in the yum cache directory over time.
  2.               More complete details can be found in the Clean Options section below.
复制代码
看下命令行提示:

  1. # yum clean
  2. Error: clean requires an option: headers, packages, metadata, dbcache, plugins, all
复制代码
看到它支持 clean 的对象有 headers,packages,metadata,dbcache 和 plugins 这几项。
之前为了解决 yum 插件的clean 问题,简单分析了下 yum clean 的源码,附上分析日志如下。

yum clean的源码,在/usr/share/yum-cli/cli.py 中 882行

  1. def cleanCli(self, userlist):
复制代码
函数中.
支持的参数有:

  1. yum clean:
  2. header
  3. packages
  4. metadata
  5. dbcache
  6. expire-cache
  7. plugins
复制代码
针对不同的输入,会调用

  1. self.cleanPackages()
  2. self.cleanHeaders()
  3. self.cleanMetadata()
  4. self.cleanSqlite()
复制代码
这几个函数,删除不同的项

然后我们在文件

  1. /usr/lib/python2.4/site-packages/yum/__init__.py
复制代码
中能够看到上面几个函数的实现:

  1. def cleanHeaders(self):
  2.         exts = ['hdr']
  3.         return self._cleanFiles(exts, 'hdrdir', 'header')

  4.     def cleanPackages(self):
  5.         exts = ['rpm']
  6.         return self._cleanFiles(exts, 'pkgdir', 'package')
复制代码
等等。
这几个clean函数都是通过封装 __cleanFiles来实现的,看看这个函数:

  1. def _cleanFiles(self, exts, pathattr, filetype):
  2.         filelist = []
  3.         removed = 0
  4.         for ext in exts:
  5.             for repo in self.repos.listEnabled():
  6.                 repo.dirSetup()
  7.                 path = getattr(repo, pathattr)
  8.                 if os.path.exists(path) and os.path.isdir(path):
  9.                     filelist = misc.getFileList(path, ext, filelist)

  10.         for item in filelist:
  11.             try:
  12.                 os.unlink(item)
  13.             except OSError, e:
  14.                 self.logger.critical(_('Cannot remove %s file %s'), filetype, item)
  15.                 continue
  16.             else:
  17.                 self.verbose_logger.log(logginglevels.DEBUG_4,
  18.                     _('%s file %s removed'), filetype, item)
  19.                 removed+=1
  20.         msg = _('%d %s files removed') % (removed, filetype)
  21.         return 0, [msg]
复制代码
其实就是去对应的目录下删除文件而已。

可以如下测试:
修改代码段:

  1. for item in filelist:
  2.             try:
  3.                 print "====:", item
  4.                 os.unlink(item)
复制代码
添加打印,保存
然后先 yum list 生成cache
可以 yum list 看下生成的文件

  1. tree
  2. .
  3. |-- ops.5.i386
  4. |   |-- cachecookie
  5. |   |-- packages
  6. |   |-- primary.sqlite
  7. |   `-- repomd.xml
  8. |-- ops.5.noarch
  9. |   |-- cachecookie
  10. |   |-- packages
  11. |   |-- primary.sqlite
  12. |   `-- repomd.xml
  13. `-- rhel.5.i386
  14.     |-- cachecookie
  15.     |-- packages
  16.     |-- primary.sqlite
  17.     `-- repomd.xml
  18. 然后 sudo yum clean all
  19. 输出如下:
  20. ====: //var/cache/yum/ops.5.noarch/repomd.xml
  21. ====: //var/cache/yum/ops.5.i386/repomd.xml
  22. ====: //var/cache/yum/rhel.5.i386/repomd.xml
  23. ====: //var/cache/yum/ops.5.noarch/cachecookie
  24. ====: //var/cache/yum/ops.5.i386/cachecookie
  25. ====: //var/cache/yum/rhel.5.i386/cachecookie
  26. ====: //var/cache/yum/ops.5.noarch/primary.sqlite
  27. ====: //var/cache/yum/ops.5.i386/primary.sqlite
  28. ====: //var/cache/yum/rhel.5.i386/primary.sqlite
复制代码
再去/var/cache/yum下tree下,看不到yum list 后的文件了

yum clean 是这样实现的,我想,对于 install, update 和 list 等操作,如果你要分析源码的话,可以借鉴 clean 的分析过程。

论坛徽章:
0
13 [报告]
发表于 2013-01-04 06:25 |只看该作者
本帖最后由 duanjigang 于 2013-01-04 07:31 编辑

杭州好大雪~~继续总结
yum 的 配置文件

yum 的 配置分为三类:

1 全局配置     /etc/yum.conf   -- 配置整个 yum 服务的一些参数和options
2 repo 配置   /etc/yum.repos.d/*.repo 配置 yum 访问时的repo源和路径
3 插件配置   /etc/yum/pluginconf.d/*.conf 配置 yum 扩展插件


下来依次解释其内容的含义:

/etc/yum.conf
先看下内容"

  1. $ cat /etc/yum.conf
  2. [main]
  3. cachedir=/var/cache/yum
  4. keepcache=0
  5. debuglevel=2
  6. logfile=/var/log/yum.log
  7. distroverpkg=redhat-release
  8. tolerant=1
  9. exactarch=1
  10. obsoletes=1
  11. gpgcheck=1
  12. plugins=1
  13. metadata_expire=0

  14. # Default.
  15. # installonly_limit = 3

  16. # PUT YOUR REPOS HERE OR IN separate files named file.repo
  17. # in /etc/yum.repos.d
复制代码
cachedir: 前面说过,本地存放cache 的目录。
keepcache: 在成功安装rpm后是否保留rpm和header文件,在前面介绍 cachdir 的例子中,我们看到cachedir 中的 header 和 packages 目录是空的,现在打开keepcache

  1. keepcache=1
复制代码
然后重新安装 test-girl 和 test-daddy 包,之后再看下 cachedir 的内容:

  1. # tree /var/cache/yum/
  2. /var/cache/yum/
  3. |-- newtest
  4. |   |-- cachecookie
  5. |   |-- headers
  6. |   |-- packages
  7. |   |-- primary.xml.gz
  8. |   |-- primary.xml.gz.sqlite
  9. |   `-- repomd.xml
  10. `-- test
  11.     |-- cachecookie
  12.     |-- headers
  13.     |   |-- test-daddy-1.1-1.i386.hdr
  14.     |   `-- test-girl-1.1-1.i386.hdr
  15.     |-- packages
  16.     |   |-- test-daddy-1.1-1.i386.rpm
  17.     |   `-- test-girl-1.1-1.i386.rpm
  18.     |-- primary.xml.gz
  19.     |-- primary.xml.gz.sqlite
  20.     `-- repomd.xml
复制代码
看到了本地cache 的 hdr 和 rpm 文件了吧。

debuglevel: debug 信息输出等级,范围为0-10,默认是2
persistdir:  yum 持久化存储数据的存储目录,默认为 /var/lib/yum
reposdir: 我们都知道,yum的默认repo 配置目录是 /etc/yum.repos.d, 这个目录可以通过配置文件中的行

  1. reposdir=dir1,dir2
复制代码
来修改,这样,yum 会加载这多个目录下的*.repo 文件,在加载完这些之后,还要和 /etc/yum.conf 中的 repo 配置项合并,我们能够看到 /etc/yum.conf 中最后一行有提示:
# PUT YOUR REPOS HERE OR IN separate files named file.repo
# in /etc/yum.repos.d

也就是在告诉使用者:你直接把 *.repo 中的内容写在这里也好。这样,yum.conf 中的repo 最终会和 reposdir 中配置的 repo 合并。
从个人喜好角度讲:我更喜欢把*.repo分开,不同的应用不同的repo,也方便各自修改,更新,如果都合并到一个文件 yum.conf 中,更新修改都是这个文件,涉及到多个程序修改repo时,还得进行互斥操作,比较麻烦。另外,看起来格式也比较乱。

errorlevel:和 Debuglevel 一样,只不过是错误信息输出的配置等级。
logfile: yum 安装的日志,
pkgpolicy:包安装的策略,有两个选项,newest和last,如果你设置了多个repo,并且出现同一个软件在不同的repos中同时存在的情况,yum 应该安装哪一个,如果配置是 newest,则yum会安装最新的那个版本。如果配置是 last,yum 则会将服务器 id 以字母表排序,并选择最后的那个服务器上的软件安装。一般都是选 newest。
distroverpkg:指定一个软件包,yum会根据这个包判断你的发行版本,默认是redhat-release,也可以是安装的任何针对自己发行版的rpm包。我们在 *.repo中经常会这样配置:

  1. baseurl=http://xxxx/redhat/$releasever/$basearch/
复制代码
其中 $basearch 的值会为 i386 或者 x86_64,而 $releasever 的取值就是以 distroverpkg 的取值为名称的rpm的版本号,基本上取值都是 redhat-release
如果你看到你的系统上 redhat-release 的包为:

  1. $ rpm -qa redhat-release
  2. redhat-release-5Server-5.7.0.3
复制代码
那么就能断定 *.repo 中的  $releasever 为5.7了。很多人容易把这个值理解为获取的是 /etc/redhat-release 这个文件中的内容,其实不是,我之前的脚本也是获取这个值的。这个设计,貌似是个比较让人诟病的点,碰巧在水木社区看到一篇帖子,就是恶心这个设计的,转过来跟大家分享下:

http://www.newsmth.net/nForum/#!article/LinuxApp/738396
发信人: JulyClyde (torred), 信区: LinuxApp
标  题: yum的$releasever真是太反动了
发信站: 水木社区 (Fri Oct  9 16:54:21 2009), 站内
  
原创;网址 http://www.julyclyde.org/?p=275
yum的$releasever真是太反动了
  
来看这篇文章的人,大都应该同意《Unix编程艺术》中提到的那些观点吧。今天就给大家看一个反例:yum 的 $releasever 变量
  
在 /etc/yum.repos.d/ 目录下的软件库定义文件中,常常会在 baseurl 的路径中提到 $releasever 这个变量,表示当前发行版的大版本号,但大家知道这个变量是在哪设置的吗?我 grep 了整个 etc 目录都没找到,还是看了 yum.conf 才知道的,是在 yum.conf 文件里 distroverpkg 选项定义的。但这个选项就很有问题:
  
    1. distroverpkg 和 releasever 名字不同,且看不出什么联系
    2. distroverpkg 的值,并不是明文,而是“redhat-release”。不知道大家看到这个会有什么想法,反正我是首先想到了 /etc/redhat-release 文件,但我错了。实际上指的是 redhat-release 这个RPM包。所谓“distroverpkg=redhat-release”的意思,其实是将 $releasever 设置为 redhat-release 这个RPM包的版本号
  
够变态吧?别人都是直接赋值,或者 include 一个各种变量定义的文件进来,而yum竟然用某个包的属性作为值,违反了“everything is file”的原则。烂!
  
有人为心爱的 RedHat 分辩说是为了升级 redhat-release 包之后可以自动升级整个系统,但事实证明 RedHat 的选择一向都是很傻的。为什么不在软件库定义文件中 include 一个表示版本号的头文件,每次大升级的时候更改这个文件呢?
  
再举个例子:
  
Debian系里面,内核包的版本维护是利用一个虚拟的 linux-image 包,依赖于某个风格的比如 linux-image-generic 包,而该包又依赖于 linux-image-2.6.28-15-generic,后者就是真正的内核包,其版本号直接写在包名里。系统升级的时候,由于 linux-image 和 linux-image-generic 的依赖关系变动,会依赖于新的 linux-image-2.6.xx-yy-generic 包,自然会装上。
  
反观 RedHat 的做法:各个内核包,只有若干个 kernel-<flavor> 的包名,版本号作为 RPM 的属性来实现,但是内核这么重大的包又不能轻易用新的代替旧的,于是再给 yum 新增一个 instalonly 插件(注意不是install,而是少一个l字母)来抑制新 kernel 代替旧 kernel 的动作。



exactarch:有两个选项1和0,代表是否只升级和你安装软件包cpu体系一致的包,如果设为1,则如你安装了一个i386的rpm,则yum不会用1686的包来升级。
retries: 网络连接发生错误后的重试次数,如果设为0,则会无限重试。
tolerent: 也有1和0两个选项,表示yum是否容忍命令行发生与软件包有关的错误,比如你要安装1,2,3三个包,而其中3此前已经安装了,如果你设为1,则yum不会出现错误信息。默认是0。
exclude: 排除某些软件在升级名单之外,可以用通配符,列表中各个项目要用空格隔开。
gpgchkeck: 有1和0两个选择,分别代表是否是否进行gpg校验.
alwaysprompt: 安装时是否提示,设置为1就会提示,需要你在 yum install 时输入参数 -y,比如

  1. yum install xxoo -y
复制代码
如果设置为0,就不需要附件 -y 输入了。
obsoletes: 默认为1,是否用新的包替换旧的包,比如你在 制作 xxoo 包时,spec 中已经标明会 obsoletes  掉 ooxx包,别人如果直接
yum update/install ooxx
就会提示安装 xxoo包,这个对于改名的软件包非常有用。
timeout : 在http连接时,等待超时的秒数,如果你的网络状况不好,可以设置大点,默认为30,标识超时为30秒。
另外还有一些比较实用的配置,比如限制安装时带宽使用的选项: throttle  和 bandwith,这个问题思考好几次了,终于找到这个配置了。
想要了解更多配置,可以去yum.conf 的文档页查阅:
http://linux.die.net/man/5/yum.conf
我们这里只列举可一些常见的配置。

论坛徽章:
0
14 [报告]
发表于 2013-01-04 14:35 |只看该作者
本帖最后由 duanjigang 于 2013-01-04 15:20 编辑

repo配置文件 /etc/yum.repos.d/*.repo

这个目录地下的文件是使用 yum 最长用到的配置文件。
我们看一个例子文件的内容:

  1. #cat RHEL.repo
  2. [redhat.$releasever.base.$basearch]
  3. name=redhat
  4. baseurl=http://yum.test.com/redhat/$releasever/iso/$basearch/
  5. gpgcheck=0

  6. [redhat.$releasever.updates.$basearch]
  7. name=redhat updates
  8. baseurl=http://yum.test.com/redhat/$releasever/updates/$basearch/
  9. gpgcheck=0
复制代码
可以看到,一个文件是由一个或者若干个 reposity 组成的,每个 reposity 保持了传统的 ini 文件的定义格式。
每个reposity 至少包含以下信息:

  1. [repositoryid]
  2. name=Some name for this repository
  3. baseurl=url://path/to/repository/
复制代码
reposityid 必须是一个唯一的单词,也就是说是一个单词,不能有空格隔开。
name:对这个repo的描述
baseurl 是访问时的http地址模版,之所以叫模版,因为组成这个 url 可以是一个实际的地址,也可以是变量组成的一个URL。
这里要注意一点,baseurl 的域名都容易理解,是 yum 服务器的域名,然后后面的目录路径写到什么地步呢?看下文档原文:
baseurl Must be a URL to the directory where the yum repository's 'repodata' directory lives. Can be an http://, ftp:// or file:// URL
对了,baseurl 就是要写到包含索引目录(repodata)那一层目录为止,这样yum 才能根据既定的协议去这个路径下找 repodata 目录,找对应的 repomd.xml 文件,否则会报错。
可以采用 http/ftp/file 协议进行访问。

也可以在一个baseurl 表达式中指定多个url,比如:

  1. [repositoryid]
  2. name=Some name for this repository
  3. baseurl=url://server1/path/to/repository/
  4. url://server2/path/to/repository/
  5. url://server3/path/to/repository/
复制代码
如果有http认证的话,可以写上用户名和密码:

  1. baseurl=http://user:passwd@example.com/
复制代码
mirrorlist  指向一个文件的 url 地址,这个文件中可以写多个 baseurl 来给yum 用,组成了一个mirror list.
enabled  1 或者 0,表示是否启用。
gpgcheck 1 或者 0,表示对于从这个源下载的包是否启用 gpg signature 校验。
gpgkey  gpgkey 的 URL 地址。

其它的选项,可以参考文档原文,我在此大概贴下:

gpgcakey A URL pointing to the ASCII-armored CA key file for the repository. This is a normal gpg public key - but this key will be used to validate detached signatures of all other keys. The idea is you are asked to confirm import for this key. After that any other gpg key needed for package or repository verification, if it has a detached signature which matches this key will be automatically imported without user confirmation.

exclude Same as the [main] exclude option but only for this repository. Substitution variables, described below, are honored here.

includepkgs Inverse of exclude. This is a list of packages you want to use from a repository. If this option lists only one package then that is all yum will ever see from the repository. Defaults to an empty list. Substitution variables, described below, are honored here.

enablegroups Either '0' or '1'. Determines whether yum will allow the use of package groups for this repository. Default is '1' (package groups are allowed).

failovermethod Either 'roundrobin' or 'priority'.

'roundrobin' randomly selects a URL out of the list of URLs to start with and proceeds through each of them as it encounters a failure contacting the host.

'priority' starts from the first baseurl listed and reads through them sequentially.

failovermethod defaults to 'roundrobin' if not specified.

keepalive Either '1' or '0'. This tells yum whether or not HTTP/1.1 keepalive should be used with this repository. See the global option in the [main] section above for more information.

timeout Overrides the timeout option from the [main] section for this repository.

http_caching Overrides the http_caching option from the [main] section for this repository.

retries Overrides the retries option from the [main] section for this repository.

throttle Overrides the throttle option from the [main] section for this repository.

bandwidth Overrides the bandwidth option from the [main] section for this repository.

sslcacert Overrides the sslcacert option from the [main] section for this repository.

sslverify Overrides the sslverify option from the [main] section for this repository.

sslclientcert Overrides the sslclientcert option from the [main] section for this repository.

sslclientkey Overrides the sslclientkey option from the [main] section for this repository.

metadata_expire Overrides the metadata_expire option from the [main] section for this repository.

mirrorlist_expire Overrides the mirrorlist_expire option from the [main] section for this repository.

proxy URL to the proxy server for this repository. Set to '_none_' to disable the global proxy setting for this repository. If this is unset it inherits it from the global setting

proxy_username username to use for proxy. If this is unset it inherits it from the global setting

proxy_password password for this proxy. If this is unset it inherits it from the global setting

username username to use for basic authentication to a repo or really any url. If this is unset it inherits it from the global setting

password password to use with the username for basic authentication. If this is unset it inherits it from the global setting

cost relative cost of accessing this repository. Useful for weighing one repo's packages as greater/less than any other. defaults to 1000

skip_if_unavailable If set to True yum will continue running if this repository cannot be contacted for any reason. This should be set carefully as all repos are consulted for any given command. Defaults to False


论坛徽章:
0
15 [报告]
发表于 2013-01-04 15:21 |只看该作者
本帖最后由 duanjigang 于 2013-01-04 15:56 编辑

reposity 访问的优先级

有时候,我们会遇到同一个包在多个 repo 中存在的情况。大多数情况都是,同一个包的不同版本分布在多个repo中,这时如果你采用

  1. yum install pkgname
复制代码
的方式去安装的话,因为 reposity 的默认配置中 pkgpolicy 的机制为 newest, 因此,这样会安装最新版的包,大多数时候,问题就是这么产生的,好多人往往不明确自己需要软件的版本,一个 yum install 就给安装到了最新版,最后才发现不是想要的版本,因此,建议安装时最好能明确版本,这样,即使同一个包在不同 repo 中,也会找对应的版本去安装,不至于出错。

另外一种情况是可能同版本的包也位于不同的repo中,按理说这种蛋疼的做法是不允许的,但是有时候我们还是希望关注下到底会安装哪个 repo 中的包,yum 为此提供了两种机制:
cost 和 priorities 插件,其实这两者的最终目的都是设置不同源的访问优先级。

我们在此只说下 cost 的用法,priorities 插件留待后面介绍。

cost 的用法也很简单,就是配置一个 cost = xxx, xxx 为权值,越小优先级越高,默认值是 1000,因此我们配置为1000以内的都会比不配置的优先级高。
看一个例子:
首先看下 yum 服务器上的配置,是一个典型的不规范yum源:

  1. ls *
  2. repo1:
  3. cmeguard-1.1.2-20.i386.rpm  repodata  test-cmeadmin-1.1.1-21.i386.rpm

  4. repo2:
  5. cmeguard-1.1.2-20.i386.rpm  repodata  test-cmeadmin-1.1.1-21.i386.rpm

  6. repo3:
  7. cmeguard-1.1.2-20.i386.rpm  repodata  test-cmeadmin-1.1.1-21.i386.rpm
复制代码
有三个repo,其中都放置了相同的rpm包,而且同版本。

然后客户端配置了三个repo文件:

  1. # ls /etc/yum.repos.d/
  2. yum0.repo  yum1.repo  yum2.repo
  3. cat /etc/yum.repos.d/yum0.repo
  4. [yum2]
  5. name=yum2
  6. baseurl=http://yum.test.com:81/yum/repo1
  7. enabled=1
  8. cost=100
  9. gpgcheck=0
  10. #cat /etc/yum.repos.d/yum1.repo
  11. [yum3]
  12. name=yum3
  13. baseurl=http://yum.test.com:81/yum/repo2
  14. enabled=1
  15. cost=103
  16. gpgcheck=0

  17. #cat /etc/yum.repos.d/yum2.repo
  18. [yum4]
  19. name=yum4
  20. baseurl=http://yum.test.com:81/yum/repo3
  21. enabled=1
  22. cost=105
  23. gpgcheck=0

复制代码
三个 repo 中 yum2 的 cost 最小,当执行

  1. yum install cmeguard
复制代码
时,提示如下:

  1. Dependencies Resolved

  2. =========================================================================================================================
  3. Package                      Arch                     Version                            Repository                Size
  4. =========================================================================================================================
  5. Installing:
  6. cmeguard                     i386                     1.1.2-20                        yum2                     9.6 M

  7. Transaction Summary
  8. =========================================================================================================================
  9. Install      1 Package(s)         
  10. Update       0 Package(s)         
  11. Remove       0 Package(s)
复制代码
可以看到,是选择了 yum2 中的包进行安装,验证了 cost 的作用。
以此为例,你可以修改多个 repo的 cost 进行优先级的设置。 需要注意的一点是,repo 的优先级跟 *.repo 的文件名称无关。

论坛徽章:
0
16 [报告]
发表于 2013-01-04 16:23 |只看该作者
本帖最后由 duanjigang 于 2013-01-04 16:44 编辑

repo 配置中的变量

有时候我们会看到这样的 repo 配置:

  1. [redhat.$releasever.iso.$basearch]
  2. name=redhat
  3. baseurl=http://yum.test.com/redhat/$releasever/iso/$basearch/
  4. gpgcheck=0
复制代码
这其中 reposityid,name,baseurl 中都可能用到了一系列变量,在实际执行 yum 命令时,这些变量会被赋值,然后按照实际的结果去执行。
有时,我们总想知道,这个变量到底是什么取值,在这段文字中,就针对这几个变量进行说明下.

  1.     $releasever This will be replaced with the value of the version of the package listed in distroverpkg. This defaults to the version of 'redhat-release' package.

  2.     $arch This will be replaced with your architecture as listed by os.uname()[4] in Python.

  3.     $basearch This will be replaced with your base architecture in yum. For example, if your $arch is i686 your $basearch will be i386.

  4.     $uuid This will be replaced with a unique but persistent uuid for this machine. The value that is first generated will be stored in /var/lib/yum/uuid and reused until this file is deleted.

  5.     $YUM0-$YUM9 These will be replaced with the value of the shell environment variable of the same name. If the shell environment variable does not exist then the configuration file variable will not be replaced.
复制代码
简单翻译下:

$releasever:就是yum.conf 中配置的distroverpkg的值对应的rpm包的版本,这个奇葩配置,前面已经说过了。
$arch,就是python代码中

  1. os.uname()
复制代码
的结果。
$basearch 对于64位架构的机器就是x86_64, 32位架构的就是i386.
$uuid 本机上产生的一个持久存储的uuid值,唯一存在。
$YUM0-$YUM9 根据环境变量取值。
后面两个变量基本上不会怎么用到。

最后,我们看几个例子,在 我的一台机器上, yum list 看到的输出如下:

  1. xorg-x11-fonts-Type1.noarch                               7.1-2.1.el5                       redhat.5Server.iso.x86_64
复制代码
这个就是前面的配置

  1. [redhat.$releasever.iso.$basearch]
复制代码
展开后的值。

另外,需要注意的是,在4u机器上,reposityid 这一项中,变量貌似是不支持的。
比如,我在一台 4u 的机器上看到如下输出:

  1. xterm.i386                               192-8.el4_7.2          $releasever.$bas
复制代码
在reposityid 这一项,变量没有被替换,但是在5u和6u的系统是没问题的。

论坛徽章:
0
17 [报告]
发表于 2013-01-04 16:46 |只看该作者
本帖最后由 duanjigang 于 2013-01-04 18:59 编辑

下面说下本篇的最后一个话题,也是比较有意思的一块:
yum plugin (YUM 的插件)

使用 apach 和 nginx 的人都知道 apache 和 nginx 都具有在不同的调用点植入插件的功能,这些接口和 netfilter 的 五个 hook 类似。

作为 redhat 系统的包管理命令- yum 也支持插件:一个 python 脚本文件和一个配置文件,用来扩展或者修改 yum 命令的行为,根据注册函数的名称来确定这些hook函数在何时调用。

增加 一个 yum 插件需要完成两件事情:一是开发对应插件的 python 脚本,二是填写对应于该插件的配置文件。
插件的存储默认位置是:/usr/lib/yum-plugins/*.py
配置文件的存储位置是:/etc/yum/pluginconf.d/*.conf

看下一个 rhel 5.7 系统上的插件:

  1. $ ls /usr/lib/yum-plugins/*.py
  2. /usr/lib/yum-plugins/rhnplugin.py  /usr/lib/yum-plugins/subscription-manager.py
  3. /usr/lib/yum-plugins/product-id.py  /usr/lib/yum-plugins/security.py
  4. $ ls /etc/yum/pluginconf.d/
  5. product-id.conf  rhnplugin.conf  security.conf  subscription-manager.conf
复制代码
我们都知道,一个数据包在主机上的网络协议栈中被处理时会经过很多点,netfilter 提供了一种机制,可以在一些关键的点,提供 hook 函数注册:用户按照netfilter 模块的的定义规范开发自己的代码,编译生成模块文件 xx.ko,然后把这个模块插入内核,如果没有什么异常的话,每一个经过xxoo.ko注册点的网络报文都会被 xxoo.ko 的 hook 函数进行处理。或者DROP,或者ACCEPT, 或者STOLEN等等。总之,通过这些 hook 点能够改变报文的最终目的地或者它在协议栈中的走向。

和 报文在协议栈中的处理一样,当你执行一个 yum 命令时,比如安装一个软件包,这个事情也有很多关键点,yum 的开发者在这些点也预留了一些外部可以注册hook函数的接口,以方便使用者通过第三方插件的方式改变 yum 的行为和功能。yum 操作中 我们能够想到的一些点有: yum 初始化,配置文件加载,repo加载,文件下载,安装/卸载事务等等。
这对这些操作点,yum 提供了以下几个 hook 点来方便用户植入 hook 模块,细说如下:

config
插件初始化以后的时刻,试图去扩展yum 的配置文件和命令行接口的 hook 可以放在这里


postconfig
yum的配置对象刚刚初始化以后的时刻,试图修改或者扩展 yum 中的变量的hook可以放在这里。yum-3.1.7 以后才支持该 hook点。

init
在yum 初始化的早起,可以进行插件相关的初始化工作

predownload
包下载之前的时刻,想要获取包信息的hook可以放在此处

postdownload
包下载之后的时刻,想要获取下载包的错误信息的hook可以放在此

prereposetup
在 yum 初始化 reposity 之前

postreposetup
在 yum 初始化 reposity 之后

exclude
在包的 inclusion 和 exclusion 处理完之后调用,想要改变 inclusion 和 exclusion 的 hook 放在此

preresolve
在包决定之前

postresolve
在包决定之后

pretrans
在 update trasanction 之前

posttrans
在 update transation 结束之后

close
在 yum 正常运行结束之后

clean
在yum clean 之后调用 (4u的系统自带yum不支持该hook)

论坛徽章:
0
18 [报告]
发表于 2013-01-04 19:02 |只看该作者
本帖最后由 duanjigang 于 2013-01-05 11:20 编辑

yum 的 hook 规范

随便看一个 插件的代码,能发现hook代码的规范:

1): 如果要在 XX点注册一个hook函数,你的python 代码中的函数名字应该为

  1. XX_hook
复制代码
2):每个hook 函数都会带一个参数 conduit
一个 conduit 变量在不同的 hook/slot 点包含的数据是会变化的,类似于 netfilter 中的参数 skbuff 一样,在不同的hook点,skbuff 中的指针,变量,或者有效,或者无效,这都要看它所处的位置(slot点)。
不同的成员和方法在不同的 slot, 对于 conduit 也是会变得,这个可以参考 yum.plugins.SLOT_TO_CONDUIT这个字典,所有 condiut 子类的父类都是 PluginConduit


API依赖:

  在不同的yum版本中,支持的插件API和yum API都是会变化的。因此,为了防止出错,并且使用到合适的API,需要在插件的代码中指明版本号。当前的API版本可以参考:yum.plugins.API_VERSION

插件类型:

目前支持两种插件类型:

TYPE_CORE 和 TYPE_INTERACTIVE 两种。

核心插件会修改yum的基础功能,比如包排斥列表,依赖和 reposity 加载等。

交互式插件 可以修改yum的用户界面,比如,交互式插件可以检查一些错误,然后退出yum,并且打印提示信息给用户。

在yum2.6以及以前版本中,交互式插件定义为: TYPE_INTERFACE,不是很清晰,才进行了更新。
看一下我写的一个例子插件中注册插件类型的代码:

  1. requires_api_version = '2.4'
  2. if yum.plugins.API_VERSION < '2.3':
  3.     from yum.plugins import TYPE_INTERFACE
  4.     plugin_type = (TYPE_INTERFACE,)
  5. else:
  6.     from yum.plugins import TYPE_INTERACTIVE
  7.     plugin_type = (TYPE_INTERACTIVE,)
复制代码
其它

可以在yum的任意调用点,调用yum.plugins.PluginYumExit 来退出yum,并且打印你传递信息。

插件的配置文件都是INI格式存储的,以下接口可以在插件的任何位置调用,来帮助你加载yum的配置信息(他们都是conduit中可以调用的):


  1. def confString(self, section, opt, default=None)

  2.     def confInt(self, section, opt, default=None)

  3.     def confFloat(self, section, opt, default=None)

  4.     def confBool(self, section, opt, default=None)
复制代码
看名字,猜意思,很容易的。

yum-priorities 插件
yum-priorities 是一个标准的 yum 插件包,它的目的在于为不同的reposity 设置各自的优先级,优先级在软件升级或者安装时决定访问reposity 的顺序。
优先级低的repo 中的包不能升级优先级高的 reposity 中的包所安装的系统(针对于同一个包),同一个包,会先选择优先级较高的repo去选择安装。
安装方法,直接

  1. yum install yum-priorities -y
复制代码
即可。会安装文件:

  1. /etc/yum/pluginconf.d/priorities.conf
  2. /usr/lib/yum-plugins/priorities.py
  3. /usr/lib/yum-plugins/priorities.pyc
  4. /usr/lib/yum-plugins/priorities.pyo
复制代码
对于 priority 插件来说,每个repo 的默认优先级是99,可以配置的优先级是1到99,你可以在repo的配置中添加一行来定义该repo的优先级,比如:

  1. [base]
  2. name=CentOS-$releasever - Base
  3. mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os
  4. gpgcheck=1
  5. gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-centos4
  6. priority=1
复制代码
优先级取值越小,级别越高。
另外有一点要注意,因为package 之间可以存在 obsoletes(废弃的意思)的关系,也就是说A包在spec 中指定了 obsoletes B 包,那么当A和B都发布后,如果有人

  1. yum install/update B
复制代码
包的时候,如果 /etc/yum.conf 中的 obsoletes 为 1,这时就会用A来替换B包,安装A包。
即使 A 存在于优先级较低的repo中而B存在于优先级较高的repo中,这样一来的话,priorities 的功能就和  obsoletes 冲突了。
obsoletes 允许更新,而 priorities 不允许更新,为了解决这个问题,给 priorities 插件引入了一个选项 check_obsoletes
如果你如下配置的话:

  1. cat  /etc/yum/pluginconf.d/priorities.conf
  2. [main]
  3. enabled = 1
  4. check_obsoletes=1
复制代码
obsoletes 就会被 priorities 插件禁掉。这样互掐的功能,难免让人觉得有些不爽。
因此,也会有如下的言论:
http://wiki.centos.org/PackageManagement/Yum/Priorities
Note: The upstream maintainer of yum, Seth Vidal, had the following to say about 'yum priorities' in September 2009:
Gosh, I hope people do not set up yum priorities. There are so many things about
priorities that make me cringe all over. It could just be that it reminds me of
apt 'pinning' and that makes me want to hurl.

论坛徽章:
0
19 [报告]
发表于 2013-01-04 19:13 |只看该作者
本帖最后由 duanjigang 于 2013-01-04 19:18 编辑

yum 插件例子之一: 在所有 hook 点打印信息

如下是我写的一个 test 插件的例子,注意在5u7 或者更高版本 OS 上运行。
配置如下:

  1. cat /etc/yum/pluginconf.d/test.conf
  2. [main]
  3. enabled=1
复制代码
插件代码如下,就是打印一行信息而已,只不过在所有slot/hook 点都进行了注册:

  1. # cat /usr/lib/yum-plugins/test.py
  2. #!/usr/bin/python -tt
  3. from yum import config
  4. from yum.plugins import PluginYumExit
  5. import yum
  6. import yum.plugins
  7. import yum.config
  8. import rpmUtils.arch

  9. import locale
  10. locale.setlocale(locale.LC_ALL, '')


  11. requires_api_version = '2.4'
  12. if yum.plugins.API_VERSION < '2.3':
  13.     from yum.plugins import TYPE_INTERFACE
  14.     plugin_type = (TYPE_INTERFACE,)
  15. else:
  16.     from yum.plugins import TYPE_INTERACTIVE
  17.     plugin_type = (TYPE_INTERACTIVE,)

  18. def config_hook(conduit):
  19.         print "config_hook-----------------------------"
  20. def postconfig_hook(conduit):
  21.         print "postconfig_hook-----------------------------"

  22. def init_hook(conduit):
  23.         print "init_hook-----------------------------"
  24. def predownload_hook(conduit):
  25.         print "predownload_hook-----------------------------"
  26. def postdownload_hook(conduit):
  27.         print "postdownload_hook-----------------------------"
  28. def prereposetup_hook(conduit):
  29.         print "prereposetup_hook-----------------------------"
  30. def postreposetup_hook(conduit):
  31.         print "postreposetup_hook-----------------------------"
  32.     #raise PluginYumExit('Goodbye')

  33. def preresolve_hook(conduit):
  34.         print "preresolve_hook-----------------------------"
  35. def postresolve_hook(conduit):
  36.         print "postresolve_hook-----------------------------"

  37. def exclude_hook(conduit):
  38.         print "exclude_hook-----------------------------"
  39. def pretrans_hook(conduit):
  40.         print "pretrans_hook----------------------------"
  41. def posttrans_hook(conduit):
  42.         print "posttrans_hook----------------------------"
  43.        
  44. def close_hook(conduit):
  45.         print "close_hook-----------------------------"
  46. #It is  not supported on a rhel4 os
  47. def clean_hook(conduit):
  48.         print "clean_hook-----------------------------"
复制代码
你可以通过:
yum list  search info update repolist clean remove install
等操作来检查打印的信息,同时也帮助我们理解了 yum 的执行流程,什么命令会进行什么操作。

论坛徽章:
0
20 [报告]
发表于 2013-01-04 19:19 |只看该作者
本帖最后由 duanjigang 于 2013-01-04 19:31 编辑

yum 插件例子之二:downloadonly

该例子演示了 downloadonly 只下载rpm 而不安装的功能,其中展示了 命令行参数扩展功能,以及内置函数调用的示例代码,可以说是一个不错的例子:

  1. $ cat  /usr/lib/yum-plugins/test.py
  2. #!/usr/bin/python -tt
  3. from yum import config
  4. from yum.plugins import PluginYumExit
  5. import yum
  6. import yum.plugins
  7. import yum.config
  8. import rpmUtils.arch
  9. #print rpmUtils.arch.getBaseArch()
  10. #print rpmUtils.arch.getCanonArch()

  11. # Decent (UK.US English only) number formatting.
  12. import locale
  13. locale.setlocale(locale.LC_ALL, '')

  14. requires_api_version = '2.4'
  15. if yum.plugins.API_VERSION < '2.3':
  16.     from yum.plugins import TYPE_INTERFACE
  17.     plugin_type = (TYPE_INTERFACE,)
  18. else:
  19.     from yum.plugins import TYPE_INTERACTIVE
  20.     plugin_type = (TYPE_INTERACTIVE,)

  21. def config_hook(conduit):
  22.     parser = conduit.getOptParser()
  23.     parser.add_option('', '--downloadonly', dest='dlonly',
  24.       action='store_true', default=False,
  25.       help="don't update, just download,added by duanjigang")
  26.     # Add a boolean option to the [main] section
  27.     config.YumConf.enable_foo = config.BoolOption(False)

  28.     # Add a URL option to repository sections
  29.     config.RepoConf.foo_url = config.UrlOption()

  30.     # Add an option to to [main] and the repository sections. The
  31.     # repository options will inherit the properties of the [main] option
  32.     # and will use the value from [main] if the option is not specified in
  33.     # the repo section.
  34.     config.YumConf.max_foo = config.IntOption(10)
  35.     config.RepoConf.max_foo = config.Inherit(config.YumConf.max_foo)

  36. def init_hook(conduit):
  37.     conf = conduit.getConf()


  38.     # Display the options from the [main] section
  39.     conduit.info(2, "enable_foo = %r" % conf.enable_foo)
  40.     conduit.info(2, "max_foo = %r" % conf.max_foo)

  41.     # Display the options from the repository sections
  42.     for repo in conduit.getRepos().listEnabled():
  43.     #for repo in conduit.getRepos().listEnabled():
  44.         conduit.info(2, "%s.foo_url = %r" % (repo.id, repo.foo_url))
  45.         conduit.info(2, "%s.max_foo = %r" % (repo.id, repo.max_foo))

  46. def postdownload_hook(conduit):
  47.         opts, commands = conduit.getCmdLine()
  48.         if opts.dlonly:
  49.             raise PluginYumExit('exiting because --downloadonly specified--by duanjigang')
复制代码
测试下其中的赋值和打印语句:

  1. $ yum info a | grep redhat
  2. redhat.5Server.iso.x86_64.foo_url = None
  3. redhat.5Server.iso.x86_64.max_foo = 10
  4. redhat.5Server.updates.x86_64.foo_url = None
  5. redhat.5Server.updates.x86_64.max_foo = 10
复制代码
看一下命令行选项:执行
yum --help

后面有输出一行:

  1. --downloadonly        don't update, just download,added by duanjigang
复制代码
是我们增加的命令行参数。

最后我们测试下它的 downloadonly 功能:

  1. $ sudo yum install zsh --downloadonly -y
  2. =========================================================================================================================
  3. Package             Arch                   Version                     Repository                                  Size
  4. =========================================================================================================================
  5. Installing:
  6. zsh                 x86_64                 4.2.6-6.el5                 redhat.5Server.iso.x86_64                 1.8 M

  7. Transaction Summary
  8. =========================================================================================================================
  9. Install       1 Package(s)
  10. Upgrade       0 Package(s)

  11. Total size: 1.8 M
  12. Downloading Packages:


  13. exiting because --downloadonly specified--by duanjigang
复制代码
验证下:

  1. $ rpm -qa zsh
  2. $ ls  -lat /var/cache/yum/redhat.5Server.iso.x86_64/packages/zsh-4.2.6-6.el5.x86_64.rpm
  3. -rw-r--r-- 1 root root 1835335 Apr  1  2012 /var/cache/yum/redhat.5Server.iso.x86_64/packages/zsh-4.2.6-6.el5.x86_64.rpm
复制代码
我们能够看到 包确实下载下来了,但是却没有安装,正是我们期望的。

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP